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