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