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 for ( it = resource->begin(); it != resource->end(); ++it )
00543 addContact( *it, weight, s_completionSources->size()-1 );
00544 }
00545 }
00546
00547 #ifndef KDEPIM_NEW_DISTRLISTS // new distr lists are normal contact, already done above
00548 int weight = config.readNumEntry( "DistributionLists", 60 );
00549 KABC::DistributionListManager manager( addressBook );
00550 manager.load();
00551 const QStringList distLists = manager.listNames();
00552 QStringList::const_iterator listIt;
00553 int idx = addCompletionSource( i18n( "Distribution Lists" ) );
00554 for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) {
00555
00556
00557 addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx );
00558
00559
00560 QStringList sl( (*listIt).simplifyWhiteSpace() );
00561 addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx, &sl );
00562
00563 }
00564 #endif
00565
00566 QApplication::restoreOverrideCursor();
00567
00568 if ( !m_addressBookConnected ) {
00569 connect( addressBook, SIGNAL( addressBookChanged( AddressBook* ) ), SLOT( loadContacts() ) );
00570 m_addressBookConnected = true;
00571 }
00572 }
00573
00574 void AddresseeLineEdit::addContact( const KABC::Addressee& addr, int weight, int source )
00575 {
00576 #ifdef KDEPIM_NEW_DISTRLISTS
00577 if ( KPIM::DistributionList::isDistributionList( addr ) ) {
00578
00579
00580
00581 addCompletionItem( addr.formattedName(), weight, source );
00582
00583
00584 QStringList sl( addr.formattedName() );
00585 addCompletionItem( addr.formattedName(), weight, source, &sl );
00586
00587 return;
00588 }
00589 #endif
00590
00591 const QStringList emails = addr.emails();
00592 QStringList::ConstIterator it;
00593 const int prefEmailWeight = 1;
00594 int isPrefEmail = prefEmailWeight;
00595 for ( it = emails.begin(); it != emails.end(); ++it ) {
00596
00597 const QString email( (*it) );
00598 const QString givenName = addr.givenName();
00599 const QString familyName= addr.familyName();
00600 const QString nickName = addr.nickName();
00601 const QString domain = email.mid( email.find( '@' ) + 1 );
00602 QString fullEmail = addr.fullEmail( email );
00603
00604
00605
00606 if ( givenName.isEmpty() && familyName.isEmpty() ) {
00607 addCompletionItem( fullEmail, weight + isPrefEmail, source );
00608 } else {
00609 const QString byFirstName= "\"" + givenName + " " + familyName + "\" <" + email + ">";
00610 const QString byLastName = "\"" + familyName + ", " + givenName + "\" <" + email + ">";
00611 addCompletionItem( byFirstName, weight + isPrefEmail, source );
00612 addCompletionItem( byLastName, weight + isPrefEmail, source );
00613 }
00614
00615 addCompletionItem( email, weight + isPrefEmail, source );
00616
00617 if ( !nickName.isEmpty() ){
00618 const QString byNick = "\"" + nickName + "\" <" + email + ">";
00619 addCompletionItem( byNick, weight + isPrefEmail, source );
00620 }
00621
00622 if ( !domain.isEmpty() ){
00623 const QString byDomain = "\"" + domain + " " + familyName + " " + givenName + "\" <" + email + ">";
00624 addCompletionItem( byDomain, weight + isPrefEmail, source );
00625 }
00626
00627
00628 QStringList keyWords;
00629 const QString realName = addr.realName();
00630
00631 if ( !givenName.isEmpty() && !familyName.isEmpty() ) {
00632 keyWords.append( givenName + " " + familyName );
00633 keyWords.append( familyName + " " + givenName );
00634 keyWords.append( familyName + ", " + givenName);
00635 }else if ( !givenName.isEmpty() )
00636 keyWords.append( givenName );
00637 else if ( !familyName.isEmpty() )
00638 keyWords.append( familyName );
00639
00640 if ( !nickName.isEmpty() )
00641 keyWords.append( nickName );
00642
00643 if ( !realName.isEmpty() )
00644 keyWords.append( realName );
00645
00646 if ( !domain.isEmpty() )
00647 keyWords.append( domain );
00648
00649 keyWords.append( email );
00650
00651
00652
00653
00654
00655
00656
00657 if ( isPrefEmail == prefEmailWeight )
00658 fullEmail.replace( " <", " <" );
00659
00660 addCompletionItem( fullEmail, weight + isPrefEmail, source, &keyWords );
00661 isPrefEmail = 0;
00662
00663 #if 0
00664 int len = (*it).length();
00665 if ( len == 0 ) continue;
00666 if( '\0' == (*it)[len-1] )
00667 --len;
00668 const QString tmp = (*it).left( len );
00669 const QString fullEmail = addr.fullEmail( tmp );
00670
00671 addCompletionItem( fullEmail.simplifyWhiteSpace(), weight, source );
00672
00673
00674
00675 QString name( addr.realName().simplifyWhiteSpace() );
00676 if( name.endsWith("\"") )
00677 name.truncate( name.length()-1 );
00678 if( name.startsWith("\"") )
00679 name = name.mid( 1 );
00680
00681
00682 if ( !name.isEmpty() )
00683 addCompletionItem( addr.preferredEmail() + " (" + name + ")", weight, source );
00684
00685 bool bDone = false;
00686 int i = -1;
00687 while( ( i = name.findRev(' ') ) > 1 && !bDone ) {
00688 QString sLastName( name.mid( i+1 ) );
00689 if( ! sLastName.isEmpty() &&
00690 2 <= sLastName.length() &&
00691 ! sLastName.endsWith(".") ) {
00692 name.truncate( i );
00693 if( !name.isEmpty() ){
00694 sLastName.prepend( "\"" );
00695 sLastName.append( ", " + name + "\" <" );
00696 }
00697 QString sExtraEntry( sLastName );
00698 sExtraEntry.append( tmp.isEmpty() ? addr.preferredEmail() : tmp );
00699 sExtraEntry.append( ">" );
00700
00701 addCompletionItem( sExtraEntry.simplifyWhiteSpace(), weight, source );
00702 bDone = true;
00703 }
00704 if( !bDone ) {
00705 name.truncate( i );
00706 if( name.endsWith("\"") )
00707 name.truncate( name.length()-1 );
00708 }
00709 }
00710 #endif
00711 }
00712 }
00713
00714 void AddresseeLineEdit::addCompletionItem( const QString& string, int weight, int completionItemSource, const QStringList * keyWords )
00715 {
00716
00717
00718 CompletionItemsMap::iterator it = s_completionItemMap->find( string );
00719 if ( it != s_completionItemMap->end() ) {
00720 weight = QMAX( ( *it ).first, weight );
00721 ( *it ).first = weight;
00722 } else {
00723 s_completionItemMap->insert( string, qMakePair( weight, completionItemSource ) );
00724 }
00725 if ( keyWords == 0 )
00726 s_completion->addItem( string, weight );
00727 else
00728 s_completion->addItemWithKeys( string, weight, keyWords );
00729 }
00730
00731 void AddresseeLineEdit::slotStartLDAPLookup()
00732 {
00733 KGlobalSettings::Completion mode = completionMode();
00734
00735 if ( mode == KGlobalSettings::CompletionNone )
00736 return;
00737
00738 if ( !s_LDAPSearch->isAvailable() ) {
00739 return;
00740 }
00741 if ( s_LDAPLineEdit != this )
00742 return;
00743
00744 startLoadingLDAPEntries();
00745 }
00746
00747 void AddresseeLineEdit::stopLDAPLookup()
00748 {
00749 s_LDAPSearch->cancelSearch();
00750 s_LDAPLineEdit = NULL;
00751 }
00752
00753 void AddresseeLineEdit::startLoadingLDAPEntries()
00754 {
00755 QString s( *s_LDAPText );
00756
00757 QString prevAddr;
00758 int n = s.findRev( ',' );
00759 if ( n >= 0 ) {
00760 prevAddr = s.left( n + 1 ) + ' ';
00761 s = s.mid( n + 1, 255 ).stripWhiteSpace();
00762 }
00763
00764 if ( s.isEmpty() )
00765 return;
00766
00767
00768 s_LDAPSearch->startSearch( s );
00769 }
00770
00771 void AddresseeLineEdit::slotLDAPSearchData( const KPIM::LdapResultList& adrs )
00772 {
00773 if ( s_LDAPLineEdit != this )
00774 return;
00775
00776 for ( KPIM::LdapResultList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) {
00777 KABC::Addressee addr;
00778 addr.setNameFromString( (*it).name );
00779 addr.setEmails( (*it).email );
00780
00781 addContact( addr, (*it).completionWeight, (*it ).clientNumber );
00782 }
00783
00784 }
00785
00786 void AddresseeLineEdit::setCompletedItems( const QStringList& items, bool autoSuggest )
00787 {
00788 KCompletionBox* completionBox = this->completionBox();
00789
00790 if ( !items.isEmpty() &&
00791 !(items.count() == 1 && m_searchString == items.first()) )
00792 {
00793 QString oldCurrentText = completionBox->currentText();
00794 QListBoxItem *itemUnderMouse = completionBox->itemAt(
00795 completionBox->viewport()->mapFromGlobal(QCursor::pos()) );
00796 QString oldTextUnderMouse;
00797 QPoint oldPosOfItemUnderMouse;
00798 if ( itemUnderMouse ) {
00799 oldTextUnderMouse = itemUnderMouse->text();
00800 oldPosOfItemUnderMouse = completionBox->itemRect( itemUnderMouse ).topLeft();
00801 }
00802
00803 completionBox->setItems( items );
00804
00805 if ( !completionBox->isVisible() ) {
00806 if ( !m_searchString.isEmpty() )
00807 completionBox->setCancelledText( m_searchString );
00808 completionBox->popup();
00809
00810
00811
00812 if ( s_completion->order() == KCompletion::Weighted )
00813 qApp->installEventFilter( this );
00814 }
00815
00816
00817
00818 QListBoxItem* item = 0;
00819 if ( oldCurrentText.isEmpty()
00820 || ( item = completionBox->findItem( oldCurrentText ) ) == 0 ) {
00821 item = completionBox->item( 1 );
00822 }
00823 if ( item )
00824 {
00825 if ( itemUnderMouse ) {
00826 QListBoxItem *newItemUnderMouse = completionBox->findItem( oldTextUnderMouse );
00827
00828
00829 if ( newItemUnderMouse ) {
00830 QRect r = completionBox->itemRect( newItemUnderMouse );
00831 QPoint target = r.topLeft();
00832 if ( oldPosOfItemUnderMouse != target ) {
00833 target.setX( target.x() + r.width()/2 );
00834 QCursor::setPos( completionBox->viewport()->mapToGlobal(target) );
00835 }
00836 }
00837 }
00838 completionBox->blockSignals( true );
00839 completionBox->setSelected( item, true );
00840 completionBox->setCurrentItem( item );
00841 completionBox->ensureCurrentVisible();
00842
00843 completionBox->blockSignals( false );
00844 }
00845
00846 if ( autoSuggest )
00847 {
00848 int index = items.first().find( m_searchString );
00849 QString newText = items.first().mid( index );
00850 setUserSelection(false);
00851 setCompletedText(newText,true);
00852 }
00853 }
00854 else
00855 {
00856 if ( completionBox && completionBox->isVisible() ) {
00857 completionBox->hide();
00858 completionBox->setItems( QStringList() );
00859 }
00860 }
00861 }
00862
00863 QPopupMenu* AddresseeLineEdit::createPopupMenu()
00864 {
00865 QPopupMenu *menu = KLineEdit::createPopupMenu();
00866 if ( !menu )
00867 return 0;
00868
00869 if ( m_useCompletion ){
00870 menu->setItemVisible( ShortAutoCompletion, false );
00871 menu->setItemVisible( PopupAutoCompletion, false );
00872 menu->insertItem( i18n( "Configure Completion Order..." ),
00873 this, SLOT( slotEditCompletionOrder() ) );
00874 }
00875 return menu;
00876 }
00877
00878 void AddresseeLineEdit::slotEditCompletionOrder()
00879 {
00880 init();
00881 CompletionOrderEditor editor( s_LDAPSearch, this );
00882 editor.exec();
00883 }
00884
00885 void KPIM::AddresseeLineEdit::slotIMAPCompletionOrderChanged()
00886 {
00887 if ( m_useCompletion )
00888 s_addressesDirty = true;
00889 }
00890
00891 void KPIM::AddresseeLineEdit::slotUserCancelled( const QString& cancelText )
00892 {
00893 if ( s_LDAPSearch && s_LDAPLineEdit == this )
00894 stopLDAPLookup();
00895 userCancelled( m_previousAddresses + cancelText );
00896 }
00897
00898 void AddresseeLineEdit::updateSearchString()
00899 {
00900 m_searchString = text();
00901
00902 int n = -1;
00903 bool inQuote = false;
00904 for ( uint i = 0; i < m_searchString.length(); ++i ) {
00905 if ( m_searchString[ i ] == '"' )
00906 inQuote = !inQuote;
00907 if ( m_searchString[ i ] == '\\' && (i + 1) < m_searchString.length() && m_searchString[ i + 1 ] == '"' )
00908 ++i;
00909 if ( inQuote )
00910 continue;
00911 if ( m_searchString[ i ] == ',' || ( m_useSemiColonAsSeparator && m_searchString[ i ] == ';' ) )
00912 n = i;
00913 }
00914
00915 if ( n >= 0 ) {
00916 ++n;
00917
00918 int len = m_searchString.length();
00919
00920
00921 while ( n < len && m_searchString[ n ].isSpace() )
00922 ++n;
00923
00924 m_previousAddresses = m_searchString.left( n );
00925 m_searchString = m_searchString.mid( n ).stripWhiteSpace();
00926 }
00927 else
00928 {
00929 m_previousAddresses = QString::null;
00930 }
00931 }
00932
00933 void KPIM::AddresseeLineEdit::slotCompletion()
00934 {
00935
00936
00937 updateSearchString();
00938 if ( completionBox() )
00939 completionBox()->setCancelledText( m_searchString );
00940 doCompletion( false );
00941 }
00942
00943
00944 KCompletion::CompOrder KPIM::AddresseeLineEdit::completionOrder()
00945 {
00946 KConfig config( "kpimcompletionorder" );
00947 config.setGroup( "General" );
00948 const QString order = config.readEntry( "CompletionOrder", "Weighted" );
00949
00950 if ( order == "Weighted" )
00951 return KCompletion::Weighted;
00952 else
00953 return KCompletion::Sorted;
00954 }
00955
00956 int KPIM::AddresseeLineEdit::addCompletionSource( const QString &source )
00957 {
00958 s_completionSources->append( source );
00959 return s_completionSources->size()-1;
00960 }
00961
00962 bool KPIM::AddresseeLineEdit::eventFilter(QObject *obj, QEvent *e)
00963 {
00964 if ( obj == completionBox() ) {
00965 if ( e->type() == QEvent::MouseButtonPress
00966 || e->type() == QEvent::MouseMove
00967 || e->type() == QEvent::MouseButtonRelease ) {
00968 QMouseEvent* me = static_cast<QMouseEvent*>( e );
00969
00970 QListBoxItem *item = completionBox()->itemAt( me->pos() );
00971 if ( !item ) {
00972
00973
00974 bool eat = e->type() == QEvent::MouseMove;
00975 return eat;
00976 }
00977
00978
00979 if ( e->type() == QEvent::MouseButtonPress
00980 || me->state() & LeftButton || me->state() & MidButton
00981 || me->state() & RightButton ) {
00982 if ( itemIsHeader(item) ) {
00983 return true;
00984 } else {
00985
00986
00987
00988 completionBox()->setCurrentItem( item );
00989 completionBox()->setSelected( completionBox()->index( item ), true );
00990 if ( e->type() == QEvent::MouseMove )
00991 return true;
00992 }
00993 }
00994 }
00995 }
00996 if ( ( obj == this ) &&
00997 ( e->type() == QEvent::AccelOverride ) ) {
00998 QKeyEvent *ke = static_cast<QKeyEvent*>( e );
00999 if ( ke->key() == Key_Up || ke->key() == Key_Down || ke->key() == Key_Tab ) {
01000 ke->accept();
01001 return true;
01002 }
01003 }
01004 if ( ( obj == this ) &&
01005 ( e->type() == QEvent::KeyPress ) &&
01006 completionBox()->isVisible() ) {
01007 QKeyEvent *ke = static_cast<QKeyEvent*>( e );
01008 unsigned int currentIndex = completionBox()->currentItem();
01009 if ( ke->key() == Key_Up ) {
01010
01011
01012
01013 QListBoxItem *itemAbove = completionBox()->item( currentIndex - 1 );
01014 if ( itemAbove && itemIsHeader(itemAbove) ) {
01015
01016
01017 if ( currentIndex > 1 && completionBox()->item( currentIndex - 2 ) ) {
01018
01019 completionBox()->setCurrentItem( itemAbove->prev() );
01020 completionBox()->setSelected( currentIndex - 2, true );
01021 } else if ( currentIndex == 1 ) {
01022
01023
01024 completionBox()->ensureVisible( 0, 0 );
01025 completionBox()->setSelected( currentIndex, true );
01026 }
01027 return true;
01028 }
01029 } else if ( ke->key() == Key_Down ) {
01030
01031
01032 QListBoxItem *itemBelow = completionBox()->item( currentIndex + 1 );
01033 if ( itemBelow && itemIsHeader( itemBelow ) ) {
01034 if ( completionBox()->item( currentIndex + 2 ) ) {
01035
01036 completionBox()->setCurrentItem( itemBelow->next() );
01037 completionBox()->setSelected( currentIndex + 2, true );
01038 } else {
01039
01040 completionBox()->setSelected( currentIndex, true );
01041 }
01042 return true;
01043 }
01044
01045 if ( !itemBelow && currentIndex == 1 ) {
01046 completionBox()->setSelected( currentIndex, true );
01047 }
01048
01049
01050
01051 QListBoxItem *item = completionBox()->item( currentIndex );
01052 if ( item && itemIsHeader(item) ) {
01053 completionBox()->setSelected( currentIndex, true );
01054 }
01055 } else if ( ke->key() == Key_Tab || ke->key() == Key_Backtab ) {
01057 QListBoxItem *myHeader = 0;
01058 int i = currentIndex;
01059 while ( i>=0 ) {
01060 if ( itemIsHeader( completionBox()->item(i) ) ) {
01061 myHeader = completionBox()->item( i );
01062 break;
01063 }
01064 i--;
01065 }
01066 Q_ASSERT( myHeader );
01067
01068
01069 QListBoxItem *nextHeader = 0;
01070 const int iterationstep = ke->key() == Key_Tab ? 1 : -1;
01071
01072
01073 uint j = ke->key() == Key_Tab ? currentIndex : i==0 ? completionBox()->count()-1 : (i-1) % completionBox()->count();
01074 while ( ( nextHeader = completionBox()->item( j ) ) && nextHeader != myHeader ) {
01075 if ( itemIsHeader(nextHeader) ) {
01076 break;
01077 }
01078 j = (j + iterationstep) % completionBox()->count();
01079 }
01080 if ( nextHeader && nextHeader != myHeader ) {
01081 QListBoxItem *item = completionBox()->item( j + 1 );
01082 if ( item && !itemIsHeader(item) ) {
01083 completionBox()->setSelected( j+1, true );
01084 completionBox()->setCurrentItem( item );
01085 completionBox()->ensureCurrentVisible();
01086 }
01087 }
01088 return true;
01089 }
01090 }
01091 return ClickLineEdit::eventFilter( obj, e );
01092 }
01093
01094 const QStringList KPIM::AddresseeLineEdit::getAdjustedCompletionItems( bool fullSearch )
01095 {
01096 QStringList items = fullSearch ?
01097 s_completion->allMatches( m_searchString )
01098 : s_completion->substringCompletion( m_searchString );
01099
01100 int lastSourceIndex = -1;
01101 unsigned int i = 0;
01102 QMap<int, QStringList> sections;
01103 QStringList sortedItems;
01104 for ( QStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i ) {
01105 CompletionItemsMap::const_iterator cit = s_completionItemMap->find(*it);
01106 if ( cit == s_completionItemMap->end() )continue;
01107 int idx = (*cit).second;
01108 if ( s_completion->order() == KCompletion::Weighted ) {
01109 if ( lastSourceIndex == -1 || lastSourceIndex != idx ) {
01110 const QString sourceLabel( (*s_completionSources)[idx] );
01111 if ( sections.find(idx) == sections.end() ) {
01112 items.insert( it, sourceLabel );
01113 }
01114 lastSourceIndex = idx;
01115 }
01116 (*it) = (*it).prepend( s_completionItemIndentString );
01117
01118 (*it).replace( " <", " <" );
01119 }
01120 sections[idx].append( *it );
01121
01122 if ( s_completion->order() == KCompletion::Sorted ) {
01123 sortedItems.append( *it );
01124 }
01125 }
01126 if ( s_completion->order() == KCompletion::Weighted ) {
01127 for ( QMap<int, QStringList>::Iterator it( sections.begin() ), end( sections.end() ); it != end; ++it ) {
01128 sortedItems.append( (*s_completionSources)[it.key()] );
01129 for ( QStringList::Iterator sit( (*it).begin() ), send( (*it).end() ); sit != send; ++sit ) {
01130 sortedItems.append( *sit );
01131 }
01132 }
01133 } else {
01134 sortedItems.sort();
01135 }
01136 return sortedItems;
01137 }
01138 #include "addresseelineedit.moc"