00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "addresseelineedit.h"
00028
00029 #include "resourceabc.h"
00030 #include "completionordereditor.h"
00031 #include "ldapclient.h"
00032
00033 #include "distributionlist.h"
00034 #include <kabc/distributionlist.h>
00035 #include <kabc/stdaddressbook.h>
00036 #include <kabc/resource.h>
00037
00038 #include <kcompletionbox.h>
00039 #include <kcursor.h>
00040 #include <kdebug.h>
00041 #include <kstandarddirs.h>
00042 #include <kstaticdeleter.h>
00043 #include <kstdaccel.h>
00044 #include <kurldrag.h>
00045 #include <klocale.h>
00046
00047 #include <qpopupmenu.h>
00048 #include <qapplication.h>
00049 #include <qobject.h>
00050 #include <qptrlist.h>
00051 #include <qregexp.h>
00052 #include <qevent.h>
00053 #include <qdragobject.h>
00054 #include <qclipboard.h>
00055
00056 using namespace KPIM;
00057
00058 KMailCompletion * AddresseeLineEdit::s_completion = 0L;
00059 KPIM::CompletionItemsMap* AddresseeLineEdit::s_completionItemMap = 0L;
00060 QStringList* AddresseeLineEdit::s_completionSources = 0L;
00061 bool AddresseeLineEdit::s_addressesDirty = false;
00062 QTimer* AddresseeLineEdit::s_LDAPTimer = 0L;
00063 KPIM::LdapSearch* AddresseeLineEdit::s_LDAPSearch = 0L;
00064 QString* AddresseeLineEdit::s_LDAPText = 0L;
00065 AddresseeLineEdit* AddresseeLineEdit::s_LDAPLineEdit = 0L;
00066
00067 static KStaticDeleter<KMailCompletion> completionDeleter;
00068 static KStaticDeleter<KPIM::CompletionItemsMap> completionItemsDeleter;
00069 static KStaticDeleter<QTimer> ldapTimerDeleter;
00070 static KStaticDeleter<KPIM::LdapSearch> ldapSearchDeleter;
00071 static KStaticDeleter<QString> ldapTextDeleter;
00072 static KStaticDeleter<QStringList> completionSourcesDeleter;
00073
00074
00075 static QCString newLineEditDCOPObjectName()
00076 {
00077 static int s_count = 0;
00078 QCString name( "KPIM::AddresseeLineEdit" );
00079 if ( s_count++ ) {
00080 name += '-';
00081 name += QCString().setNum( s_count );
00082 }
00083 return name;
00084 }
00085
00086 static const QString s_completionItemIndentString = " ";
00087
00088 static bool itemIsHeader( const QListBoxItem* item )
00089 {
00090 return item && !item->text().startsWith( s_completionItemIndentString );
00091 }
00092
00093
00094
00095 AddresseeLineEdit::AddresseeLineEdit( QWidget* parent, bool useCompletion,
00096 const char *name )
00097 : ClickLineEdit( parent, QString::null, name ), DCOPObject( newLineEditDCOPObjectName() )
00098 {
00099 m_useCompletion = useCompletion;
00100 m_completionInitialized = false;
00101 m_smartPaste = false;
00102 m_addressBookConnected = false;
00103 m_searchExtended = false;
00104
00105 init();
00106
00107 if ( m_useCompletion )
00108 s_addressesDirty = true;
00109 }
00110
00111
00112 void AddresseeLineEdit::init()
00113 {
00114 if ( !s_completion ) {
00115 completionDeleter.setObject( s_completion, new KMailCompletion() );
00116 s_completion->setOrder( completionOrder() );
00117 s_completion->setIgnoreCase( true );
00118
00119 completionItemsDeleter.setObject( s_completionItemMap, new KPIM::CompletionItemsMap() );
00120 completionSourcesDeleter.setObject( s_completionSources, new QStringList() );
00121 }
00122
00123
00124
00125
00126 if ( m_useCompletion ) {
00127 if ( !s_LDAPTimer ) {
00128 ldapTimerDeleter.setObject( s_LDAPTimer, new QTimer( 0, "ldapTimerDeleter" ) );
00129 ldapSearchDeleter.setObject( s_LDAPSearch, new KPIM::LdapSearch );
00130 ldapTextDeleter.setObject( s_LDAPText, new QString );
00131
00132
00133
00134 QValueList< LdapClient* > clients = s_LDAPSearch->clients();
00135 for ( QValueList<LdapClient*>::iterator it = clients.begin(); it != clients.end(); ++it ) {
00136 addCompletionSource( "LDAP server: " + (*it)->host() );
00137 }
00138 }
00139 if ( !m_completionInitialized ) {
00140 setCompletionObject( s_completion, false );
00141 connect( this, SIGNAL( completion( const QString& ) ),
00142 this, SLOT( slotCompletion() ) );
00143
00144 KCompletionBox *box = completionBox();
00145 connect( box, SIGNAL( highlighted( const QString& ) ),
00146 this, SLOT( slotPopupCompletion( const QString& ) ) );
00147 connect( box, SIGNAL( userCancelled( const QString& ) ),
00148 SLOT( slotUserCancelled( const QString& ) ) );
00149
00150
00151 if ( !connectDCOPSignal( 0, "KPIM::IMAPCompletionOrder", "orderChanged()",
00152 "slotIMAPCompletionOrderChanged()", false ) )
00153 kdError() << "AddresseeLineEdit: connection to orderChanged() failed" << endl;
00154
00155 connect( s_LDAPTimer, SIGNAL( timeout() ), SLOT( slotStartLDAPLookup() ) );
00156 connect( s_LDAPSearch, SIGNAL( searchData( const KPIM::LdapResultList& ) ),
00157 SLOT( slotLDAPSearchData( const KPIM::LdapResultList& ) ) );
00158
00159 m_completionInitialized = true;
00160 }
00161 }
00162 }
00163
00164 AddresseeLineEdit::~AddresseeLineEdit()
00165 {
00166 if ( s_LDAPSearch && s_LDAPLineEdit == this )
00167 stopLDAPLookup();
00168 }
00169
00170 void AddresseeLineEdit::setFont( const QFont& font )
00171 {
00172 KLineEdit::setFont( font );
00173 if ( m_useCompletion )
00174 completionBox()->setFont( font );
00175 }
00176
00177 void AddresseeLineEdit::allowSemiColonAsSeparator( bool useSemiColonAsSeparator )
00178 {
00179 m_useSemiColonAsSeparator = useSemiColonAsSeparator;
00180 }
00181
00182 void AddresseeLineEdit::keyPressEvent( QKeyEvent *e )
00183 {
00184 bool accept = false;
00185
00186 if ( KStdAccel::shortcut( KStdAccel::SubstringCompletion ).contains( KKey( e ) ) ) {
00187
00188 updateSearchString();
00189 doCompletion( true );
00190 accept = true;
00191 } else if ( KStdAccel::shortcut( KStdAccel::TextCompletion ).contains( KKey( e ) ) ) {
00192 int len = text().length();
00193
00194 if ( len == cursorPosition() ) {
00195 updateSearchString();
00196 doCompletion( true );
00197 accept = true;
00198 }
00199 }
00200
00201 if ( !accept )
00202 KLineEdit::keyPressEvent( e );
00203
00204 if ( e->isAccepted() ) {
00205 updateSearchString();
00206 QString searchString( m_searchString );
00207
00208 if ( m_searchExtended )
00209 searchString = m_searchString.mid( 1 );
00210
00211 if ( m_useCompletion && s_LDAPTimer != NULL ) {
00212 if ( *s_LDAPText != searchString || s_LDAPLineEdit != this )
00213 stopLDAPLookup();
00214
00215 *s_LDAPText = searchString;
00216 s_LDAPLineEdit = this;
00217 s_LDAPTimer->start( 500, true );
00218 }
00219 }
00220 }
00221
00222 void AddresseeLineEdit::insert( const QString &t )
00223 {
00224 if ( !m_smartPaste ) {
00225 KLineEdit::insert( t );
00226 return;
00227 }
00228
00229
00230
00231 QString newText = t.stripWhiteSpace();
00232 if ( newText.isEmpty() )
00233 return;
00234
00235
00236 QStringList lines = QStringList::split( QRegExp("\r?\n"), newText, false );
00237 for ( QStringList::iterator it = lines.begin();
00238 it != lines.end(); ++it ) {
00239
00240 (*it).remove( QRegExp(",?\\s*$") );
00241 }
00242 newText = lines.join( ", " );
00243
00244 if ( newText.lower().startsWith("mailto:") ) {
00245 KURL url( newText );
00246 newText = url.path();
00247 }
00248 else if ( newText.find(" at ") != -1 ) {
00249
00250 newText.replace( " at ", "@" );
00251 newText.replace( " dot ", "." );
00252 }
00253 else if ( newText.find("(at)") != -1 ) {
00254 newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" );
00255 }
00256
00257 QString contents = text();
00258 int start_sel = 0;
00259 int end_sel = 0;
00260 int pos = cursorPosition();
00261 if ( getSelection( &start_sel, &end_sel ) ) {
00262
00263 if ( pos > end_sel )
00264 pos -= (end_sel - start_sel);
00265 else if ( pos > start_sel )
00266 pos = start_sel;
00267 contents = contents.left( start_sel ) + contents.right( end_sel + 1 );
00268 }
00269
00270 int eot = contents.length();
00271 while ((eot > 0) && contents[ eot - 1 ].isSpace() ) eot--;
00272 if ( eot == 0 )
00273 contents = QString::null;
00274 else if ( pos >= eot ) {
00275 if ( contents[ eot - 1 ] == ',' )
00276 eot--;
00277 contents.truncate( eot );
00278 contents += ", ";
00279 pos = eot + 2;
00280 }
00281
00282 contents = contents.left( pos ) + newText + contents.mid( pos );
00283 setText( contents );
00284 setEdited( true );
00285 setCursorPosition( pos + newText.length() );
00286 }
00287
00288 void AddresseeLineEdit::setText( const QString & text )
00289 {
00290 ClickLineEdit::setText( text.stripWhiteSpace() );
00291 }
00292
00293 void AddresseeLineEdit::paste()
00294 {
00295 if ( m_useCompletion )
00296 m_smartPaste = true;
00297
00298 KLineEdit::paste();
00299 m_smartPaste = false;
00300 }
00301
00302 void AddresseeLineEdit::mouseReleaseEvent( QMouseEvent *e )
00303 {
00304
00305 if ( m_useCompletion
00306 && QApplication::clipboard()->supportsSelection()
00307 && !isReadOnly()
00308 && e->button() == MidButton ) {
00309 m_smartPaste = true;
00310 }
00311
00312 KLineEdit::mouseReleaseEvent( e );
00313 m_smartPaste = false;
00314 }
00315
00316 void AddresseeLineEdit::dropEvent( QDropEvent *e )
00317 {
00318 KURL::List uriList;
00319 if ( !isReadOnly()
00320 && KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ) ) {
00321 QString contents = text();
00322
00323 int eot = contents.length();
00324 while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() )
00325 eot--;
00326 if ( eot == 0 )
00327 contents = QString::null;
00328 else if ( contents[ eot - 1 ] == ',' ) {
00329 eot--;
00330 contents.truncate( eot );
00331 }
00332 bool mailtoURL = false;
00333
00334 for ( KURL::List::Iterator it = uriList.begin();
00335 it != uriList.end(); ++it ) {
00336 if ( !contents.isEmpty() )
00337 contents.append( ", " );
00338 KURL u( *it );
00339 if ( u.protocol() == "mailto" ) {
00340 mailtoURL = true;
00341 contents.append( (*it).path() );
00342 }
00343 }
00344 if ( mailtoURL ) {
00345 setText( contents );
00346 setEdited( true );
00347 return;
00348 }
00349 }
00350
00351 if ( m_useCompletion )
00352 m_smartPaste = true;
00353 QLineEdit::dropEvent( e );
00354 m_smartPaste = false;
00355 }
00356
00357 void AddresseeLineEdit::cursorAtEnd()
00358 {
00359 setCursorPosition( text().length() );
00360 }
00361
00362 void AddresseeLineEdit::enableCompletion( bool enable )
00363 {
00364 m_useCompletion = enable;
00365 }
00366
00367 void AddresseeLineEdit::doCompletion( bool ctrlT )
00368 {
00369 m_lastSearchMode = ctrlT;
00370
00371 KGlobalSettings::Completion mode = completionMode();
00372
00373 if ( mode == KGlobalSettings::CompletionNone )
00374 return;
00375
00376 if ( s_addressesDirty ) {
00377 loadContacts();
00378 s_completion->setOrder( completionOrder() );
00379 }
00380
00381
00382 if ( ctrlT ) {
00383 const QStringList completions = getAdjustedCompletionItems( false );
00384
00385 if ( completions.count() > 1 )
00386 ;
00387 else if ( completions.count() == 1 )
00388 setText( m_previousAddresses + completions.first().stripWhiteSpace() );
00389
00390 setCompletedItems( completions, true );
00391
00392 cursorAtEnd();
00393 setCompletionMode( mode );
00394 return;
00395 }
00396
00397
00398 switch ( mode ) {
00399 case KGlobalSettings::CompletionPopupAuto:
00400 {
00401 if ( m_searchString.isEmpty() )
00402 break;
00403 }
00404
00405 case KGlobalSettings::CompletionPopup:
00406 {
00407 const QStringList items = getAdjustedCompletionItems( true );
00408 setCompletedItems( items, false );
00409 break;
00410 }
00411
00412 case KGlobalSettings::CompletionShell:
00413 {
00414 QString match = s_completion->makeCompletion( m_searchString );
00415 if ( !match.isNull() && match != m_searchString ) {
00416 setText( m_previousAddresses + match );
00417 setEdited( true );
00418 cursorAtEnd();
00419 }
00420 break;
00421 }
00422
00423 case KGlobalSettings::CompletionMan:
00424 case KGlobalSettings::CompletionAuto:
00425 {
00426
00427 setCompletionMode( completionMode() );
00428
00429 if ( !m_searchString.isEmpty() ) {
00430
00431
00432 if ( m_searchExtended && m_searchString == "\"" ){
00433 m_searchExtended = false;
00434 m_searchString = QString::null;
00435 setText( m_previousAddresses );
00436 break;
00437 }
00438
00439 QString match = s_completion->makeCompletion( m_searchString );
00440
00441 if ( !match.isEmpty() ) {
00442 if ( match != m_searchString ) {
00443 QString adds = m_previousAddresses + match;
00444 setCompletedText( adds );
00445 }
00446 } else {
00447 if ( !m_searchString.startsWith( "\"" ) ) {
00448
00449 match = s_completion->makeCompletion( "\"" + m_searchString );
00450 if ( !match.isEmpty() && match != m_searchString ) {
00451 m_searchString = "\"" + m_searchString;
00452 m_searchExtended = true;
00453 setText( m_previousAddresses + m_searchString );
00454 setCompletedText( m_previousAddresses + match );
00455 }
00456 } else if ( m_searchExtended ) {
00457
00458 m_searchString = m_searchString.mid( 1 );
00459 m_searchExtended = false;
00460 setText( m_previousAddresses + m_searchString );
00461
00462 match = s_completion->makeCompletion( m_searchString );
00463 if ( !match.isEmpty() && match != m_searchString ) {
00464 QString adds = m_previousAddresses + match;
00465 setCompletedText( adds );
00466 }
00467 }
00468 }
00469 }
00470 break;
00471 }
00472
00473 case KGlobalSettings::CompletionNone:
00474 default:
00475 break;
00476 }
00477 }
00478
00479 void AddresseeLineEdit::slotPopupCompletion( const QString& completion )
00480 {
00481 setText( m_previousAddresses + completion.stripWhiteSpace() );
00482 cursorAtEnd();
00483
00484 }
00485
00486 void AddresseeLineEdit::loadContacts()
00487 {
00488 s_completion->clear();
00489 s_completionItemMap->clear();
00490 s_addressesDirty = false;
00491
00492
00493 QApplication::setOverrideCursor( KCursor::waitCursor() );
00494
00495 KConfig config( "kpimcompletionorder" );
00496 config.setGroup( "CompletionWeights" );
00497
00498 KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
00499
00500
00501 QPtrList<KABC::Resource> resources( addressBook->resources() );
00502 for( QPtrListIterator<KABC::Resource> resit( resources ); *resit; ++resit ) {
00503 KABC::Resource* resource = *resit;
00504 KPIM::ResourceABC* resabc = dynamic_cast<ResourceABC *>( resource );
00505 if ( resabc ) {
00506 const QMap<QString, QString> uidToResourceMap = resabc->uidToResourceMap();
00507 KABC::Resource::Iterator it;
00508 for ( it = resource->begin(); it != resource->end(); ++it ) {
00509 QString uid = (*it).uid();
00510 QMap<QString, QString>::const_iterator wit = uidToResourceMap.find( uid );
00511 const QString subresourceLabel = resabc->subresourceLabel( *wit );
00512 int idx = s_completionSources->findIndex( subresourceLabel );
00513 if ( idx == -1 ) {
00514 s_completionSources->append( subresourceLabel );
00515 idx = s_completionSources->size() -1;
00516 }
00517 int weight = ( wit != uidToResourceMap.end() ) ? resabc->subresourceCompletionWeight( *wit ) : 80;
00518
00519 addContact( *it, weight, idx );
00520 }
00521 } else {
00522 int weight = config.readNumEntry( resource->identifier(), 60 );
00523 s_completionSources->append( resource->resourceName() );
00524 KABC::Resource::Iterator it;
00525 for ( it = resource->begin(); it != resource->end(); ++it )
00526 addContact( *it, weight, s_completionSources->size()-1 );
00527 }
00528 }
00529
00530 #if 0 // now done as part of the normal contacts
00531 int weight = config.readNumEntry( "DistributionLists", 60 );
00532 KABC::DistributionListManager manager( addressBook );
00533 manager.load();
00534 const QStringList distLists = manager.listNames();
00535 QStringList::const_iterator listIt;
00536 int idx = addCompletionSource( i18n( "Distribution Lists" ) );
00537 for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) {
00538
00539
00540 addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx );
00541
00542
00543 QStringList sl( (*listIt).simplifyWhiteSpace() );
00544 addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx, &sl );
00545
00546 }
00547 #endif
00548
00549 QApplication::restoreOverrideCursor();
00550
00551 if ( !m_addressBookConnected ) {
00552 connect( addressBook, SIGNAL( addressBookChanged( AddressBook* ) ), SLOT( loadContacts() ) );
00553 m_addressBookConnected = true;
00554 }
00555 }
00556
00557 void AddresseeLineEdit::addContact( const KABC::Addressee& addr, int weight, int source )
00558 {
00559 if ( KPIM::DistributionList::isDistributionList( addr ) ) {
00560
00561
00562
00563 addCompletionItem( addr.formattedName(), weight, source );
00564
00565
00566 QStringList sl( addr.formattedName() );
00567 addCompletionItem( addr.formattedName(), weight, source, &sl );
00568
00569 return;
00570 }
00571
00572 const QStringList emails = addr.emails();
00573 QStringList::ConstIterator it;
00574 const int prefEmailWeight = 1;
00575 int isPrefEmail = prefEmailWeight;
00576 for ( it = emails.begin(); it != emails.end(); ++it ) {
00577
00578 const QString email( (*it) );
00579 const QString givenName = addr.givenName();
00580 const QString familyName= addr.familyName();
00581 const QString nickName = addr.nickName();
00582 const QString domain = email.mid( email.find( '@' ) + 1 );
00583 QString fullEmail = addr.fullEmail( email );
00584
00585
00586
00587 if ( givenName.isEmpty() && familyName.isEmpty() ) {
00588 addCompletionItem( fullEmail, weight + isPrefEmail, source );
00589 } else {
00590 const QString byFirstName= "\"" + givenName + " " + familyName + "\" <" + email + ">";
00591 const QString byLastName = "\"" + familyName + ", " + givenName + "\" <" + email + ">";
00592 addCompletionItem( byFirstName, weight + isPrefEmail, source );
00593 addCompletionItem( byLastName, weight + isPrefEmail, source );
00594 }
00595
00596 addCompletionItem( email, weight + isPrefEmail, source );
00597
00598 if ( !nickName.isEmpty() ){
00599 const QString byNick = "\"" + nickName + "\" <" + email + ">";
00600 addCompletionItem( byNick, weight + isPrefEmail, source );
00601 }
00602
00603 if ( !domain.isEmpty() ){
00604 const QString byDomain = "\"" + domain + " " + familyName + " " + givenName + "\" <" + email + ">";
00605 addCompletionItem( byDomain, weight + isPrefEmail, source );
00606 }
00607
00608
00609 QStringList keyWords;
00610 const QString realName = addr.realName();
00611
00612 if ( !givenName.isEmpty() && !familyName.isEmpty() ) {
00613 keyWords.append( givenName + " " + familyName );
00614 keyWords.append( familyName + " " + givenName );
00615 keyWords.append( familyName + ", " + givenName);
00616 }else if ( !givenName.isEmpty() )
00617 keyWords.append( givenName );
00618 else if ( !familyName.isEmpty() )
00619 keyWords.append( familyName );
00620
00621 if ( !nickName.isEmpty() )
00622 keyWords.append( nickName );
00623
00624 if ( !realName.isEmpty() )
00625 keyWords.append( realName );
00626
00627 if ( !domain.isEmpty() )
00628 keyWords.append( domain );
00629
00630 keyWords.append( email );
00631
00632
00633
00634
00635
00636
00637
00638 if ( isPrefEmail == prefEmailWeight )
00639 fullEmail.replace( " <", " <" );
00640
00641 addCompletionItem( fullEmail, weight + isPrefEmail, source, &keyWords );
00642 isPrefEmail = 0;
00643
00644 #if 0
00645 int len = (*it).length();
00646 if ( len == 0 ) continue;
00647 if( '\0' == (*it)[len-1] )
00648 --len;
00649 const QString tmp = (*it).left( len );
00650 const QString fullEmail = addr.fullEmail( tmp );
00651
00652 addCompletionItem( fullEmail.simplifyWhiteSpace(), weight, source );
00653
00654
00655
00656 QString name( addr.realName().simplifyWhiteSpace() );
00657 if( name.endsWith("\"") )
00658 name.truncate( name.length()-1 );
00659 if( name.startsWith("\"") )
00660 name = name.mid( 1 );
00661
00662
00663 if ( !name.isEmpty() )
00664 addCompletionItem( addr.preferredEmail() + " (" + name + ")", weight, source );
00665
00666 bool bDone = false;
00667 int i = -1;
00668 while( ( i = name.findRev(' ') ) > 1 && !bDone ) {
00669 QString sLastName( name.mid( i+1 ) );
00670 if( ! sLastName.isEmpty() &&
00671 2 <= sLastName.length() &&
00672 ! sLastName.endsWith(".") ) {
00673 name.truncate( i );
00674 if( !name.isEmpty() ){
00675 sLastName.prepend( "\"" );
00676 sLastName.append( ", " + name + "\" <" );
00677 }
00678 QString sExtraEntry( sLastName );
00679 sExtraEntry.append( tmp.isEmpty() ? addr.preferredEmail() : tmp );
00680 sExtraEntry.append( ">" );
00681
00682 addCompletionItem( sExtraEntry.simplifyWhiteSpace(), weight, source );
00683 bDone = true;
00684 }
00685 if( !bDone ) {
00686 name.truncate( i );
00687 if( name.endsWith("\"") )
00688 name.truncate( name.length()-1 );
00689 }
00690 }
00691 #endif
00692 }
00693 }
00694
00695 void AddresseeLineEdit::addCompletionItem( const QString& string, int weight, int completionItemSource, const QStringList * keyWords )
00696 {
00697
00698
00699 CompletionItemsMap::iterator it = s_completionItemMap->find( string );
00700 if ( it != s_completionItemMap->end() ) {
00701 weight = QMAX( ( *it ).first, weight );
00702 ( *it ).first = weight;
00703 } else {
00704 s_completionItemMap->insert( string, qMakePair( weight, completionItemSource ) );
00705 }
00706 if ( keyWords == 0 )
00707 s_completion->addItem( string, weight );
00708 else
00709 s_completion->addItemWithKeys( string, weight, keyWords );
00710 }
00711
00712 void AddresseeLineEdit::slotStartLDAPLookup()
00713 {
00714 if ( !s_LDAPSearch->isAvailable() ) {
00715 return;
00716 }
00717 if ( s_LDAPLineEdit != this )
00718 return;
00719
00720 startLoadingLDAPEntries();
00721 }
00722
00723 void AddresseeLineEdit::stopLDAPLookup()
00724 {
00725 s_LDAPSearch->cancelSearch();
00726 s_LDAPLineEdit = NULL;
00727 }
00728
00729 void AddresseeLineEdit::startLoadingLDAPEntries()
00730 {
00731 QString s( *s_LDAPText );
00732
00733 QString prevAddr;
00734 int n = s.findRev( ',' );
00735 if ( n >= 0 ) {
00736 prevAddr = s.left( n + 1 ) + ' ';
00737 s = s.mid( n + 1, 255 ).stripWhiteSpace();
00738 }
00739
00740 if ( s.isEmpty() )
00741 return;
00742
00743
00744 s_LDAPSearch->startSearch( s );
00745 }
00746
00747 void AddresseeLineEdit::slotLDAPSearchData( const KPIM::LdapResultList& adrs )
00748 {
00749 if ( s_LDAPLineEdit != this )
00750 return;
00751
00752 for ( KPIM::LdapResultList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) {
00753 KABC::Addressee addr;
00754 addr.setNameFromString( (*it).name );
00755 addr.setEmails( (*it).email );
00756
00757 addContact( addr, (*it).completionWeight, (*it ).clientNumber );
00758 }
00759
00760 if ( (hasFocus() || completionBox()->hasFocus() )
00761 && completionMode() != KGlobalSettings::CompletionNone
00762 && completionMode() != KGlobalSettings::CompletionShell) {
00763 setText( m_previousAddresses + m_searchString );
00764 doCompletion( m_lastSearchMode );
00765 }
00766 }
00767
00768
00769
00770 class KCompletionBoxHack : public KCompletionBox
00771 {
00772 public:
00773 KCompletionBoxHack() : KCompletionBox( 0 ) {}
00774 void sizeAndPosition() { KCompletionBox::sizeAndPosition(); }
00775 };
00776
00777 void AddresseeLineEdit::setCompletedItems( const QStringList& items, bool autoSuggest )
00778 {
00779 KCompletionBox* completionBox = this->completionBox();
00780
00781 if ( !items.isEmpty() &&
00782 !(items.count() == 1 && m_searchString == items.first()) )
00783 {
00784 QString oldCurrentText = completionBox->currentText();
00785 QListBoxItem *itemUnderMouse = completionBox->itemAt(
00786 completionBox->viewport()->mapFromGlobal(QCursor::pos()) );
00787 QString oldTextUnderMouse;
00788 QPoint oldPosOfItemUnderMouse;
00789 if ( itemUnderMouse ) {
00790 oldTextUnderMouse = itemUnderMouse->text();
00791 oldPosOfItemUnderMouse = completionBox->itemRect( itemUnderMouse ).topLeft();
00792 }
00793
00794 completionBox->setItems( items );
00795
00796
00797 KCompletionBoxHack* hack = static_cast<KCompletionBoxHack *>( completionBox );
00798 hack->sizeAndPosition();
00799
00800 if ( !completionBox->isVisible() ) {
00801 if ( !m_searchString.isEmpty() )
00802 completionBox->setCancelledText( m_searchString );
00803 completionBox->popup();
00804
00805
00806
00807 if ( s_completion->order() == KCompletion::Weighted )
00808 qApp->installEventFilter( this );
00809 }
00810
00811
00812
00813 QListBoxItem* item = 0;
00814 if ( oldCurrentText.isEmpty()
00815 || ( item = completionBox->findItem( oldCurrentText ) ) == 0 ) {
00816 item = completionBox->item( 1 );
00817 }
00818 if ( item )
00819 {
00820 if ( itemUnderMouse ) {
00821 QListBoxItem *newItemUnderMouse = completionBox->findItem( oldTextUnderMouse );
00822
00823
00824 if ( newItemUnderMouse ) {
00825 QRect r = completionBox->itemRect( newItemUnderMouse );
00826 QPoint target = r.topLeft();
00827 if ( oldPosOfItemUnderMouse != target ) {
00828 target.setX( target.x() + r.width()/2 );
00829 QCursor::setPos( completionBox->viewport()->mapToGlobal(target) );
00830 }
00831 }
00832 }
00833 completionBox->blockSignals( true );
00834 completionBox->setSelected( item, true );
00835 completionBox->setCurrentItem( item );
00836 completionBox->ensureCurrentVisible();
00837
00838 completionBox->blockSignals( false );
00839 }
00840
00841 if ( autoSuggest )
00842 {
00843 int index = items.first().find( m_searchString );
00844 QString newText = items.first().mid( index );
00845 setUserSelection(false);
00846 setCompletedText(newText,true);
00847 }
00848 }
00849 else
00850 {
00851 if ( completionBox && completionBox->isVisible() ) {
00852 completionBox->hide();
00853 completionBox->setItems( QStringList() );
00854 }
00855 }
00856 }
00857
00858 QPopupMenu* AddresseeLineEdit::createPopupMenu()
00859 {
00860 QPopupMenu *menu = KLineEdit::createPopupMenu();
00861 if ( !menu )
00862 return 0;
00863
00864 if ( m_useCompletion ){
00865 menu->setItemVisible( ShortAutoCompletion, false );
00866 menu->setItemVisible( PopupAutoCompletion, false );
00867 menu->insertItem( i18n( "Configure Completion Order..." ),
00868 this, SLOT( slotEditCompletionOrder() ) );
00869 }
00870 return menu;
00871 }
00872
00873 void AddresseeLineEdit::slotEditCompletionOrder()
00874 {
00875 init();
00876 CompletionOrderEditor editor( s_LDAPSearch, this );
00877 editor.exec();
00878 }
00879
00880 void KPIM::AddresseeLineEdit::slotIMAPCompletionOrderChanged()
00881 {
00882 if ( m_useCompletion )
00883 s_addressesDirty = true;
00884 }
00885
00886 void KPIM::AddresseeLineEdit::slotUserCancelled( const QString& cancelText )
00887 {
00888 if ( s_LDAPSearch && s_LDAPLineEdit == this )
00889 stopLDAPLookup();
00890 userCancelled( m_previousAddresses + cancelText );
00891 }
00892
00893 void AddresseeLineEdit::updateSearchString()
00894 {
00895 m_searchString = text();
00896
00897 int n = m_searchString.findRev(',');
00898 if( m_useSemiColonAsSeparator )
00899 n = QMAX( n, m_searchString.findRev(';') );
00900
00901 if ( n >= 0 ) {
00902 ++n;
00903
00904 int len = m_searchString.length();
00905
00906
00907 while ( n < len && m_searchString[ n ].isSpace() )
00908 ++n;
00909
00910 m_previousAddresses = m_searchString.left( n );
00911 m_searchString = m_searchString.mid( n ).stripWhiteSpace();
00912 }
00913 else
00914 {
00915 m_previousAddresses = QString::null;
00916 }
00917 }
00918
00919 void KPIM::AddresseeLineEdit::slotCompletion()
00920 {
00921
00922
00923 updateSearchString();
00924 if ( completionBox() )
00925 completionBox()->setCancelledText( m_searchString );
00926 doCompletion( false );
00927 }
00928
00929
00930 KCompletion::CompOrder KPIM::AddresseeLineEdit::completionOrder()
00931 {
00932 KConfig config( "kpimcompletionorder" );
00933 config.setGroup( "General" );
00934 const QString order = config.readEntry( "CompletionOrder", "Weighted" );
00935
00936 if ( order == "Weighted" )
00937 return KCompletion::Weighted;
00938 else
00939 return KCompletion::Sorted;
00940 }
00941
00942 int KPIM::AddresseeLineEdit::addCompletionSource( const QString &source )
00943 {
00944 s_completionSources->append( source );
00945 return s_completionSources->size()-1;
00946 }
00947
00948 bool KPIM::AddresseeLineEdit::eventFilter(QObject *obj, QEvent *e)
00949 {
00950 if ( obj == completionBox() ) {
00951 if ( e->type() == QEvent::MouseButtonPress
00952 || e->type() == QEvent::MouseMove
00953 || e->type() == QEvent::MouseButtonRelease ) {
00954 QMouseEvent* me = static_cast<QMouseEvent*>( e );
00955
00956 QListBoxItem *item = completionBox()->itemAt( me->pos() );
00957 if ( !item ) {
00958
00959
00960 bool eat = e->type() == QEvent::MouseMove;
00961 return eat;
00962 }
00963
00964
00965 if ( e->type() == QEvent::MouseButtonPress
00966 || me->state() & LeftButton || me->state() & MidButton
00967 || me->state() & RightButton ) {
00968 if ( itemIsHeader(item) ) {
00969 return true;
00970 } else {
00971
00972
00973
00974 completionBox()->setCurrentItem( item );
00975 completionBox()->setSelected( completionBox()->index( item ), true );
00976 if ( e->type() == QEvent::MouseMove )
00977 return true;
00978 }
00979 }
00980 }
00981 }
00982 if ( ( obj == this ) &&
00983 ( e->type() == QEvent::AccelOverride ) ) {
00984 QKeyEvent *ke = static_cast<QKeyEvent*>( e );
00985 if ( ke->key() == Key_Up || ke->key() == Key_Down || ke->key() == Key_Tab ) {
00986 ke->accept();
00987 return true;
00988 }
00989 }
00990 if ( ( obj == this ) &&
00991 ( e->type() == QEvent::KeyPress ) &&
00992 completionBox()->isVisible() ) {
00993 QKeyEvent *ke = static_cast<QKeyEvent*>( e );
00994 unsigned int currentIndex = completionBox()->currentItem();
00995 if ( ke->key() == Key_Up ) {
00996
00997
00998
00999 QListBoxItem *itemAbove = completionBox()->item( currentIndex - 1 );
01000 if ( itemAbove && itemIsHeader(itemAbove) ) {
01001
01002
01003 if ( currentIndex > 1 && completionBox()->item( currentIndex - 2 ) ) {
01004
01005 completionBox()->setCurrentItem( itemAbove->prev() );
01006 completionBox()->setSelected( currentIndex - 2, true );
01007 } else if ( currentIndex == 1 ) {
01008
01009
01010 completionBox()->ensureVisible( 0, 0 );
01011 completionBox()->setSelected( currentIndex, true );
01012 }
01013 return true;
01014 }
01015 } else if ( ke->key() == Key_Down ) {
01016
01017
01018 QListBoxItem *itemBelow = completionBox()->item( currentIndex + 1 );
01019 if ( itemBelow && itemIsHeader( itemBelow ) ) {
01020 if ( completionBox()->item( currentIndex + 2 ) ) {
01021
01022 completionBox()->setCurrentItem( itemBelow->next() );
01023 completionBox()->setSelected( currentIndex + 2, true );
01024 } else {
01025
01026 completionBox()->setSelected( currentIndex, true );
01027 }
01028 return true;
01029 }
01030
01031 if ( !itemBelow && currentIndex == 1 ) {
01032 completionBox()->setSelected( currentIndex, true );
01033 }
01034
01035
01036
01037 QListBoxItem *item = completionBox()->item( currentIndex );
01038 if ( item && itemIsHeader(item) ) {
01039 completionBox()->setSelected( currentIndex, true );
01040 }
01041 } else if ( ke->key() == Key_Tab || ke->key() == Key_Backtab ) {
01043 QListBoxItem *myHeader = 0;
01044 int i = currentIndex;
01045 while ( i>=0 ) {
01046 if ( itemIsHeader( completionBox()->item(i) ) ) {
01047 myHeader = completionBox()->item( i );
01048 break;
01049 }
01050 i--;
01051 }
01052 Q_ASSERT( myHeader );
01053
01054
01055 QListBoxItem *nextHeader = 0;
01056 const int iterationstep = ke->key() == Key_Tab ? 1 : -1;
01057
01058
01059 uint j = ke->key() == Key_Tab ? currentIndex : i==0 ? completionBox()->count()-1 : (i-1) % completionBox()->count();
01060 while ( ( nextHeader = completionBox()->item( j ) ) && nextHeader != myHeader ) {
01061 if ( itemIsHeader(nextHeader) ) {
01062 break;
01063 }
01064 j = (j + iterationstep) % completionBox()->count();
01065 }
01066 if ( nextHeader && nextHeader != myHeader ) {
01067 QListBoxItem *item = completionBox()->item( j + 1 );
01068 if ( item && !itemIsHeader(item) ) {
01069 completionBox()->setSelected( j+1, true );
01070 completionBox()->setCurrentItem( item );
01071 completionBox()->ensureCurrentVisible();
01072 }
01073 }
01074 return true;
01075 }
01076 }
01077 return ClickLineEdit::eventFilter( obj, e );
01078 }
01079
01080 const QStringList KPIM::AddresseeLineEdit::getAdjustedCompletionItems( bool fullSearch )
01081 {
01082 QStringList items = fullSearch ?
01083 s_completion->allMatches( m_searchString )
01084 : s_completion->substringCompletion( m_searchString );
01085
01086 int lastSourceIndex = -1;
01087 unsigned int i = 0;
01088 QMap<int, QStringList> sections;
01089 QStringList sortedItems;
01090 for ( QStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i ) {
01091 CompletionItemsMap::const_iterator cit = s_completionItemMap->find(*it);
01092 if ( cit == s_completionItemMap->end() )continue;
01093 int idx = (*cit).second;
01094 if ( s_completion->order() == KCompletion::Weighted ) {
01095 if ( lastSourceIndex == -1 || lastSourceIndex != idx ) {
01096 const QString sourceLabel( (*s_completionSources)[idx] );
01097 if ( sections.find(idx) == sections.end() ) {
01098 items.insert( it, sourceLabel );
01099 }
01100 lastSourceIndex = idx;
01101 }
01102 (*it) = (*it).prepend( s_completionItemIndentString );
01103
01104 (*it).replace( " <", " <" );
01105 }
01106 sections[idx].append( *it );
01107
01108 if ( s_completion->order() == KCompletion::Sorted ) {
01109 sortedItems.append( *it );
01110 }
01111 }
01112 if ( s_completion->order() == KCompletion::Weighted ) {
01113 for ( QMap<int, QStringList>::Iterator it( sections.begin() ), end( sections.end() ); it != end; ++it ) {
01114 sortedItems.append( (*s_completionSources)[it.key()] );
01115 for ( QStringList::Iterator sit( (*it).begin() ), send( (*it).end() ); sit != send; ++sit ) {
01116 sortedItems.append( *sit );
01117 }
01118 }
01119 } else {
01120 sortedItems.sort();
01121 }
01122 return sortedItems;
01123 }
01124 #include "addresseelineedit.moc"