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