libkdepim Library API Documentation

addresseelineedit.cpp

00001 /* 00002 This file is part of libkdepim. 00003 Copyright (c) 2002 Helge Deller <deller@gmx.de> 00004 2002 Lubos Lunak <llunak@suse.cz> 00005 2001,2003 Carsten Pfeiffer <pfeiffer@kde.org> 00006 2001 Waldo Bastian <bastian@kde.org> 00007 2004 Daniel Molkentin <danimo@klaralvdalens-datakonsult.se> 00008 2004 Karl-Heinz Zimmer <khz@klaralvdalens-datakonsult.se> 00009 00010 This library is free software; you can redistribute it and/or 00011 modify it under the terms of the GNU Library General Public 00012 License as published by the Free Software Foundation; either 00013 version 2 of the License, or (at your option) any later version. 00014 00015 This library is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 Library General Public License for more details. 00019 00020 You should have received a copy of the GNU Library General Public License 00021 along with this library; see the file COPYING.LIB. If not, write to 00022 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00023 Boston, MA 02111-1307, USA. 00024 */ 00025 00026 #include "addresseelineedit.h" 00027 00028 #include <kabc/distributionlist.h> 00029 #include <kabc/stdaddressbook.h> 00030 #include <kabc/resource.h> 00031 00032 #include <kcompletionbox.h> 00033 #include <kcursor.h> 00034 #include <kdebug.h> 00035 #include <kstandarddirs.h> 00036 #include <kstaticdeleter.h> 00037 #include <kstdaccel.h> 00038 #include <kurldrag.h> 00039 #include <klocale.h> 00040 00041 #include "completionordereditor.h" 00042 #include "ldapclient.h" 00043 00044 #include <qpopupmenu.h> 00045 #include <qapplication.h> 00046 #include <qobject.h> 00047 #include <qptrlist.h> 00048 #include <qregexp.h> 00049 #include <qevent.h> 00050 #include <qdragobject.h> 00051 #include <qclipboard.h> 00052 #include "resourceabc.h" 00053 00054 using namespace KPIM; 00055 00056 KCompletion * AddresseeLineEdit::s_completion = 0L; 00057 bool AddresseeLineEdit::s_addressesDirty = false; 00058 QTimer* AddresseeLineEdit::s_LDAPTimer = 0L; 00059 KPIM::LdapSearch* AddresseeLineEdit::s_LDAPSearch = 0L; 00060 QString* AddresseeLineEdit::s_LDAPText = 0L; 00061 AddresseeLineEdit* AddresseeLineEdit::s_LDAPLineEdit = 0L; 00062 KConfig *AddresseeLineEdit::s_config = 0L; 00063 00064 static KStaticDeleter<KCompletion> completionDeleter; 00065 static KStaticDeleter<QTimer> ldapTimerDeleter; 00066 static KStaticDeleter<KPIM::LdapSearch> ldapSearchDeleter; 00067 static KStaticDeleter<QString> ldapTextDeleter; 00068 static KStaticDeleter<KConfig> configDeleter; 00069 00070 AddresseeLineEdit::AddresseeLineEdit( QWidget* parent, bool useCompletion, 00071 const char *name ) 00072 : ClickLineEdit( parent, QString::null, name ) 00073 { 00074 m_useCompletion = useCompletion; 00075 m_completionInitialized = false; 00076 m_smartPaste = false; 00077 00078 init(); 00079 00080 if ( m_useCompletion ) 00081 s_addressesDirty = true; 00082 } 00083 00084 00085 void AddresseeLineEdit::init() 00086 { 00087 if ( !s_completion ) { 00088 completionDeleter.setObject( s_completion, new KCompletion() ); 00089 s_completion->setOrder( KCompletion::Weighted ); 00090 s_completion->setIgnoreCase( true ); 00091 } 00092 00093 // connect( s_completion, SIGNAL( match( const QString& ) ), 00094 // this, SLOT( slotMatched( const QString& ) ) ); 00095 00096 if ( m_useCompletion ) { 00097 if ( !s_LDAPTimer ) { 00098 ldapTimerDeleter.setObject( s_LDAPTimer, new QTimer ); 00099 ldapSearchDeleter.setObject( s_LDAPSearch, new KPIM::LdapSearch ); 00100 ldapTextDeleter.setObject( s_LDAPText, new QString ); 00101 } 00102 connect( s_LDAPTimer, SIGNAL( timeout() ), SLOT( slotStartLDAPLookup() ) ); 00103 connect( s_LDAPSearch, SIGNAL( searchData( const KPIM::LdapResultList& ) ), 00104 SLOT( slotLDAPSearchData( const KPIM::LdapResultList& ) ) ); 00105 } 00106 00107 if ( m_useCompletion && !m_completionInitialized ) { 00108 setCompletionObject( s_completion, false ); 00109 connect( this, SIGNAL( completion( const QString& ) ), 00110 this, SLOT( slotCompletion() ) ); 00111 00112 KCompletionBox *box = completionBox(); 00113 connect( box, SIGNAL( highlighted( const QString& ) ), 00114 this, SLOT( slotPopupCompletion( const QString& ) ) ); 00115 connect( box, SIGNAL( userCancelled( const QString& ) ), 00116 SLOT( userCancelled( const QString& ) ) ); 00117 00118 m_completionInitialized = true; 00119 } 00120 } 00121 00122 AddresseeLineEdit::~AddresseeLineEdit() 00123 { 00124 } 00125 00126 void AddresseeLineEdit::setFont( const QFont& font ) 00127 { 00128 KLineEdit::setFont( font ); 00129 if ( m_useCompletion ) 00130 completionBox()->setFont( font ); 00131 } 00132 00133 void AddresseeLineEdit::keyPressEvent( QKeyEvent *e ) 00134 { 00135 bool accept = false; 00136 00137 if ( KStdAccel::shortcut( KStdAccel::SubstringCompletion ).contains( KKey( e ) ) ) { 00138 doCompletion( true ); 00139 accept = true; 00140 } else if ( KStdAccel::shortcut( KStdAccel::TextCompletion ).contains( KKey( e ) ) ) { 00141 int len = text().length(); 00142 00143 if ( len == cursorPosition() ) { // at End? 00144 doCompletion( true ); 00145 accept = true; 00146 } 00147 } 00148 00149 if ( !accept ) 00150 KLineEdit::keyPressEvent( e ); 00151 00152 if ( e->isAccepted() ) { 00153 if ( m_useCompletion && s_LDAPTimer != NULL ) { 00154 if ( *s_LDAPText != text() ) 00155 stopLDAPLookup(); 00156 00157 *s_LDAPText = text(); 00158 s_LDAPLineEdit = this; 00159 s_LDAPTimer->start( 500, true ); 00160 } 00161 } 00162 } 00163 00164 void AddresseeLineEdit::insert( const QString &t ) 00165 { 00166 if ( !m_smartPaste ) { 00167 KLineEdit::insert( t ); 00168 return; 00169 } 00170 00171 //kdDebug(5300) << " AddresseeLineEdit::insert( \"" << t << "\" )" << endl; 00172 00173 QString newText = t.stripWhiteSpace(); 00174 if ( newText.isEmpty() ) 00175 return; 00176 00177 // remove newlines in the to-be-pasted string as well as an eventual 00178 // mailto: protocol 00179 newText.replace( QRegExp("\r?\n"), ", " ); 00180 00181 if ( newText.startsWith("mailto:") ) { 00182 KURL url( newText ); 00183 newText = url.path(); 00184 } 00185 else if ( newText.find(" at ") != -1 ) { 00186 // Anti-spam stuff 00187 newText.replace( " at ", "@" ); 00188 newText.replace( " dot ", "." ); 00189 } 00190 else if ( newText.find("(at)") != -1 ) { 00191 newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" ); 00192 } 00193 00194 QString contents = text(); 00195 int start_sel = 0; 00196 int end_sel = 0; 00197 int pos = cursorPosition(); 00198 if ( getSelection( &start_sel, &end_sel ) ) { 00199 // Cut away the selection. 00200 if ( pos > end_sel ) 00201 pos -= (end_sel - start_sel); 00202 else if ( pos > start_sel ) 00203 pos = start_sel; 00204 contents = contents.left( start_sel ) + contents.right( end_sel + 1 ); 00205 } 00206 00207 int eot = contents.length(); 00208 while ((eot > 0) && contents[ eot - 1 ].isSpace() ) eot--; 00209 if ( eot == 0 ) 00210 contents = QString::null; 00211 else if ( pos >= eot ) { 00212 if ( contents[ eot - 1 ] == ',' ) 00213 eot--; 00214 contents.truncate( eot ); 00215 contents += ", "; 00216 pos = eot + 2; 00217 } 00218 00219 contents = contents.left( pos ) + newText + contents.mid( pos ); 00220 setText( contents ); 00221 setCursorPosition( pos + newText.length() ); 00222 } 00223 00224 void AddresseeLineEdit::paste() 00225 { 00226 if ( m_useCompletion ) 00227 m_smartPaste = true; 00228 00229 KLineEdit::paste(); 00230 m_smartPaste = false; 00231 } 00232 00233 void AddresseeLineEdit::mouseReleaseEvent( QMouseEvent *e ) 00234 { 00235 // reimplemented from QLineEdit::mouseReleaseEvent() 00236 if ( m_useCompletion 00237 && QApplication::clipboard()->supportsSelection() 00238 && !isReadOnly() 00239 && e->button() == MidButton ) { 00240 m_smartPaste = true; 00241 } 00242 00243 KLineEdit::mouseReleaseEvent( e ); 00244 m_smartPaste = false; 00245 } 00246 00247 void AddresseeLineEdit::dropEvent( QDropEvent *e ) 00248 { 00249 KURL::List uriList; 00250 if ( !isReadOnly() 00251 && KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ) ) { 00252 QString contents = text(); 00253 // remove trailing white space and comma 00254 int eot = contents.length(); 00255 while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) 00256 eot--; 00257 if ( eot == 0 ) 00258 contents = QString::null; 00259 else if ( contents[ eot - 1 ] == ',' ) { 00260 eot--; 00261 contents.truncate( eot ); 00262 } 00263 // append the mailto URLs 00264 for ( KURL::List::Iterator it = uriList.begin(); 00265 it != uriList.end(); ++it ) { 00266 if ( !contents.isEmpty() ) 00267 contents.append( ", " ); 00268 KURL u( *it ); 00269 contents.append( (*it).path() ); 00270 } 00271 setText( contents ); 00272 setEdited( true ); 00273 } 00274 else { 00275 if ( m_useCompletion ) 00276 m_smartPaste = true; 00277 QLineEdit::dropEvent( e ); 00278 m_smartPaste = false; 00279 } 00280 } 00281 00282 void AddresseeLineEdit::cursorAtEnd() 00283 { 00284 setCursorPosition( text().length() ); 00285 } 00286 00287 void AddresseeLineEdit::enableCompletion( bool enable ) 00288 { 00289 m_useCompletion = enable; 00290 } 00291 00292 void AddresseeLineEdit::doCompletion( bool ctrlT ) 00293 { 00294 if ( !m_useCompletion ) 00295 return; 00296 00297 QString prevAddr; 00298 00299 QString s( text() ); 00300 int n = s.findRev(','); 00301 00302 if ( n >= 0 ) { 00303 n++; // Go past the "," 00304 00305 int len = s.length(); 00306 00307 // Increment past any whitespace... 00308 while ( n < len && s[ n ].isSpace() ) 00309 n++; 00310 00311 prevAddr = s.left( n ); 00312 s = s.mid( n, 255 ).stripWhiteSpace(); 00313 } 00314 00315 if ( s_addressesDirty ) 00316 loadContacts(); // read from local address book 00317 00318 // cursor at end of string - or Ctrl+T pressed for substring completion? 00319 if ( ctrlT ) { 00320 const QStringList completions = s_completion->substringCompletion( s ); 00321 00322 if ( completions.count() > 1 ) 00323 m_previousAddresses = prevAddr; 00324 else if ( completions.count() == 1 ) 00325 setText( prevAddr + completions.first() ); 00326 00327 setCompletedItems( completions, true ); // this makes sure the completion popup is closed if no matching items were found 00328 00329 cursorAtEnd(); 00330 return; 00331 } 00332 00333 KGlobalSettings::Completion mode = completionMode(); 00334 00335 switch ( mode ) { 00336 case KGlobalSettings::CompletionPopupAuto: 00337 { 00338 if ( s.isEmpty() ) 00339 break; 00340 } 00341 00342 case KGlobalSettings::CompletionPopup: 00343 { 00344 m_previousAddresses = prevAddr; 00345 QStringList items = s_completion->allMatches( s ); 00346 items += s_completion->allMatches( "\"" + s ); 00347 uint beforeDollarCompletionCount = items.count(); 00348 00349 if ( s.find( ' ' ) == -1 ) // one word, possibly given name 00350 items += s_completion->allMatches( "$$" + s ); 00351 00352 if ( items.isEmpty() ) { 00353 setCompletedItems( items, false ); 00354 } else { 00355 if ( items.count() > beforeDollarCompletionCount ) { 00356 // remove the '$$whatever$' part 00357 for ( QStringList::Iterator it = items.begin(); it != items.end(); ++it ) { 00358 int pos = (*it).find( '$', 2 ); 00359 if ( pos < 0 ) // ??? 00360 continue; 00361 (*it) = (*it).mid( pos + 1 ); 00362 } 00363 } 00364 00365 bool autoSuggest = (mode != KGlobalSettings::CompletionPopupAuto); 00366 setCompletedItems( items, autoSuggest ); 00367 00368 if ( !autoSuggest ) { 00369 int index = items.first().find( s ); 00370 QString newText = prevAddr + items.first().mid( index ); 00371 setUserSelection( false ); 00372 setCompletedText( newText, true ); 00373 } 00374 } 00375 00376 break; 00377 } 00378 00379 case KGlobalSettings::CompletionShell: 00380 { 00381 QString match = s_completion->makeCompletion( s ); 00382 if ( !match.isNull() && match != s ) { 00383 setText( prevAddr + match ); 00384 cursorAtEnd(); 00385 } 00386 break; 00387 } 00388 00389 case KGlobalSettings::CompletionMan: // Short-Auto in fact 00390 case KGlobalSettings::CompletionAuto: 00391 { 00392 if ( !s.isEmpty() ) { 00393 QString match = s_completion->makeCompletion( s ); 00394 if ( !match.isNull() && match != s ) { 00395 QString adds = prevAddr + match; 00396 setCompletedText( adds ); 00397 } 00398 break; 00399 } 00400 } 00401 00402 case KGlobalSettings::CompletionNone: 00403 default: // fall through 00404 break; 00405 } 00406 } 00407 00408 void AddresseeLineEdit::slotPopupCompletion( const QString& completion ) 00409 { 00410 setText( m_previousAddresses + completion ); 00411 cursorAtEnd(); 00412 // slotMatched( m_previousAddresses + completion ); 00413 } 00414 00415 void AddresseeLineEdit::loadContacts() 00416 { 00417 s_completion->clear(); 00418 s_addressesDirty = false; 00419 //m_contactMap.clear(); 00420 00421 QApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while 00422 00423 KConfig config( "kpimcompletionorder" ); // The weights for non-imap kabc resources is there. 00424 config.setGroup( "CompletionWeights" ); 00425 00426 KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); 00427 // Can't just use the addressbook's iterator, we need to know which subresource 00428 // is behind which contact. 00429 QPtrList<KABC::Resource> resources( addressBook->resources() ); 00430 for( QPtrListIterator<KABC::Resource> resit( resources ); *resit; ++resit ) { 00431 KABC::Resource* resource = *resit; 00432 KPIM::ResourceABC* resabc = dynamic_cast<ResourceABC *>( resource ); 00433 if ( resabc ) { // IMAP KABC resource; need to associate each contact with the subresource 00434 const QMap<QString, QString> uidToResourceMap = resabc->uidToResourceMap(); 00435 KABC::Resource::Iterator it; 00436 for ( it = resource->begin(); it != resource->end(); ++it ) { 00437 QString uid = (*it).uid(); 00438 QMap<QString, QString>::const_iterator wit = uidToResourceMap.find( uid ); 00439 int weight = ( wit != uidToResourceMap.end() ) ? resabc->subresourceCompletionWeight( *wit ) : 80; 00440 //kdDebug(5300) << (*it).fullEmail() << " subres=" << *wit << " weight=" << weight << endl; 00441 addContact( *it, weight ); 00442 } 00443 } else { // KABC non-imap resource 00444 int weight = config.readNumEntry( resource->identifier(), 60 ); 00445 KABC::Resource::Iterator it; 00446 for ( it = resource->begin(); it != resource->end(); ++it ) 00447 addContact( *it, weight ); 00448 } 00449 } 00450 00451 int weight = config.readNumEntry( "DistributionLists", 60 ); 00452 KABC::DistributionListManager manager( addressBook ); 00453 manager.load(); 00454 const QStringList distLists = manager.listNames(); 00455 QStringList::const_iterator listIt; 00456 for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) { 00457 s_completion->addItem( (*listIt).simplifyWhiteSpace(), weight ); 00458 } 00459 00460 QApplication::restoreOverrideCursor(); 00461 00462 disconnect( addressBook, SIGNAL( addressBookChanged( AddressBook* ) ), 00463 this, SLOT( loadContacts() ) ); 00464 00465 connect( addressBook, SIGNAL( addressBookChanged( AddressBook* ) ), SLOT( loadContacts() ) ); 00466 } 00467 00468 void AddresseeLineEdit::addContact( const KABC::Addressee& addr, int weight ) 00469 { 00470 //m_contactMap.insert( addr.realName(), addr ); 00471 const QStringList emails = addr.emails(); 00472 QStringList::ConstIterator it; 00473 for ( it = emails.begin(); it != emails.end(); ++it ) { 00474 int len = (*it).length(); 00475 if( '\0' == (*it)[len-1] ) 00476 --len; 00477 const QString tmp = (*it).left( len ); 00478 //kdDebug(5300) << " AddresseeLineEdit::addContact() \"" << tmp << "\"" << endl; 00479 QString fullEmail = addr.fullEmail( tmp ); 00480 //kdDebug(5300) << " \"" << fullEmail << "\"" << endl; 00481 s_completion->addItem( fullEmail.simplifyWhiteSpace(), weight ); 00482 } 00483 } 00484 00485 void AddresseeLineEdit::slotStartLDAPLookup() 00486 { 00487 if ( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this ) 00488 return; 00489 00490 startLoadingLDAPEntries(); 00491 } 00492 00493 void AddresseeLineEdit::stopLDAPLookup() 00494 { 00495 s_LDAPSearch->cancelSearch(); 00496 s_LDAPLineEdit = NULL; 00497 } 00498 00499 void AddresseeLineEdit::startLoadingLDAPEntries() 00500 { 00501 QString s( *s_LDAPText ); 00502 // TODO cache last? 00503 QString prevAddr; 00504 int n = s.findRev( ',' ); 00505 if ( n >= 0 ) { 00506 prevAddr = s.left( n + 1 ) + ' '; 00507 s = s.mid( n + 1, 255 ).stripWhiteSpace(); 00508 } 00509 00510 if ( s.isEmpty() ) 00511 return; 00512 00513 loadContacts(); // TODO reuse these? 00514 s_LDAPSearch->startSearch( s ); 00515 } 00516 00517 void AddresseeLineEdit::slotLDAPSearchData( const KPIM::LdapResultList& adrs ) 00518 { 00519 if ( s_LDAPLineEdit != this ) 00520 return; 00521 00522 for ( KPIM::LdapResultList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) { 00523 KABC::Addressee addr; 00524 addr.setNameFromString( (*it).name ); 00525 addr.setEmails( (*it).email ); 00526 addContact( addr, (*it).completionWeight ); 00527 } 00528 00529 if ( hasFocus() || completionBox()->hasFocus() ) 00530 if ( completionMode() != KGlobalSettings::CompletionNone ) 00531 doCompletion( false ); 00532 } 00533 00534 void AddresseeLineEdit::setCompletedItems( const QStringList& items, bool autoSuggest ) 00535 { 00536 QString txt = text(); 00537 00538 if ( !items.isEmpty() && 00539 !(items.count() == 1 && txt == items.first()) ) 00540 { 00541 if ( !txt.isEmpty() ) 00542 completionBox()->setCancelledText( txt ); 00543 00544 completionBox()->setItems( items ); 00545 completionBox()->popup(); 00546 00547 if ( autoSuggest ) 00548 { 00549 int index = items.first().find( txt ); 00550 QString newText = items.first().mid( index ); 00551 setUserSelection(false); 00552 setCompletedText(newText,true); 00553 } 00554 } 00555 else 00556 { 00557 if ( completionBox() && completionBox()->isVisible() ) 00558 completionBox()->hide(); 00559 } 00560 } 00561 00562 QPopupMenu* AddresseeLineEdit::createPopupMenu() 00563 { 00564 QPopupMenu *menu = KLineEdit::createPopupMenu(); 00565 if ( !menu ) 00566 return 0; 00567 00568 if ( m_useCompletion ) 00569 menu->insertItem( i18n( "Edit Completion Order..." ), 00570 this, SLOT( slotEditCompletionOrder() ) ); 00571 return menu; 00572 } 00573 00574 void AddresseeLineEdit::slotEditCompletionOrder() 00575 { 00576 init(); // for s_LDAPSearch 00577 CompletionOrderEditor editor( s_LDAPSearch, this ); 00578 editor.exec(); 00579 } 00580 00581 KConfig* AddresseeLineEdit::config() 00582 { 00583 if ( !s_config ) 00584 configDeleter.setObject( s_config, new KConfig( locateLocal( "config", 00585 "kabldaprc" ) ) ); 00586 00587 return s_config; 00588 } 00589 00590 #include "addresseelineedit.moc"
KDE Logo
This file is part of the documentation for libkdepim Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 27 12:50:22 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003