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