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