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