diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-11-06 15:57:02 -0600 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-11-06 15:57:02 -0600 |
commit | 2c2fbd828ca474671bb9e03681b30b115d8d6035 (patch) | |
tree | 526a9da418f8d3d7ccf515c37048d3dfc80f2843 /libkdepim/addresseelineedit.cpp | |
parent | f0610eece3676b6fe99f42cf4ef2b19a39a5c4e8 (diff) | |
download | tdepim-2c2fbd828ca474671bb9e03681b30b115d8d6035.tar.gz tdepim-2c2fbd828ca474671bb9e03681b30b115d8d6035.zip |
Actually move the kde files that were renamed in the last commit
Diffstat (limited to 'libkdepim/addresseelineedit.cpp')
-rw-r--r-- | libkdepim/addresseelineedit.cpp | 1285 |
1 files changed, 0 insertions, 1285 deletions
diff --git a/libkdepim/addresseelineedit.cpp b/libkdepim/addresseelineedit.cpp deleted file mode 100644 index 50d19e07b..000000000 --- a/libkdepim/addresseelineedit.cpp +++ /dev/null @@ -1,1285 +0,0 @@ -/* - This file is part of libtdepim. - Copyright (c) 2002 Helge Deller <deller@gmx.de> - 2002 Lubos Lunak <llunak@suse.cz> - 2001,2003 Carsten Pfeiffer <pfeiffer@kde.org> - 2001 Waldo Bastian <bastian@kde.org> - 2004 Daniel Molkentin <danimo@klaralvdalens-datakonsult.se> - 2004 Karl-Heinz Zimmer <khz@klaralvdalens-datakonsult.se> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "addresseelineedit.h" - -#include "resourceabc.h" -#include "completionordereditor.h" -#include "ldapclient.h" - -#include <config.h> - -#ifdef KDEPIM_NEW_DISTRLISTS -#include "distributionlist.h" -#else -#include <kabc/distributionlist.h> -#endif - -#include <kabc/stdaddressbook.h> -#include <kabc/resource.h> -#include <libemailfunctions/email.h> - -#include <kcompletionbox.h> -#include <kcursor.h> -#include <kdebug.h> -#include <kstandarddirs.h> -#include <kstaticdeleter.h> -#include <kstdaccel.h> -#include <kurldrag.h> -#include <klocale.h> - -#include <tqpopupmenu.h> -#include <tqapplication.h> -#include <tqobject.h> -#include <tqptrlist.h> -#include <tqregexp.h> -#include <tqevent.h> -#include <tqdragobject.h> -#include <tqclipboard.h> - -using namespace KPIM; - -KMailCompletion * AddresseeLineEdit::s_completion = 0L; -KPIM::CompletionItemsMap* AddresseeLineEdit::s_completionItemMap = 0L; -TQStringList* AddresseeLineEdit::s_completionSources = 0L; -bool AddresseeLineEdit::s_addressesDirty = false; -TQTimer* AddresseeLineEdit::s_LDAPTimer = 0L; -KPIM::LdapSearch* AddresseeLineEdit::s_LDAPSearch = 0L; -TQString* AddresseeLineEdit::s_LDAPText = 0L; -AddresseeLineEdit* AddresseeLineEdit::s_LDAPLineEdit = 0L; - -// The weights associated with the completion sources in s_completionSources. -// Both are maintained by addCompletionSource(), don't attempt to modifiy those yourself. -TQMap<TQString,int>* s_completionSourceWeights = 0; - -// maps LDAP client indices to completion source indices -// the assumption that they are always the first n indices in s_completion -// does not hold when clients are added later on -TQMap<int, int>* AddresseeLineEdit::s_ldapClientToCompletionSourceMap = 0; - -static KStaticDeleter<KMailCompletion> completionDeleter; -static KStaticDeleter<KPIM::CompletionItemsMap> completionItemsDeleter; -static KStaticDeleter<TQTimer> ldapTimerDeleter; -static KStaticDeleter<KPIM::LdapSearch> ldapSearchDeleter; -static KStaticDeleter<TQString> ldapTextDeleter; -static KStaticDeleter<TQStringList> completionSourcesDeleter; -static KStaticDeleter<TQMap<TQString,int> > completionSourceWeightsDeleter; -static KStaticDeleter<TQMap<int, int> > ldapClientToCompletionSourceMapDeleter; - -// needs to be unique, but the actual name doesn't matter much -static TQCString newLineEditDCOPObjectName() -{ - static int s_count = 0; - TQCString name( "KPIM::AddresseeLineEdit" ); - if ( s_count++ ) { - name += '-'; - name += TQCString().setNum( s_count ); - } - return name; -} - -static const TQString s_completionItemIndentString = " "; - -static bool itemIsHeader( const TQListBoxItem* item ) -{ - return item && !item->text().startsWith( s_completionItemIndentString ); -} - - - -AddresseeLineEdit::AddresseeLineEdit( TQWidget* parent, bool useCompletion, - const char *name ) - : ClickLineEdit( parent, TQString(), name ), DCOPObject( newLineEditDCOPObjectName() ), - m_useSemiColonAsSeparator( false ), m_allowDistLists( true ) -{ - m_useCompletion = useCompletion; - m_completionInitialized = false; - m_smartPaste = false; - m_addressBookConnected = false; - m_searchExtended = false; - - init(); - - if ( m_useCompletion ) - s_addressesDirty = true; -} - -void AddresseeLineEdit::updateLDAPWeights() -{ - /* Add completion sources for all ldap server, 0 to n. Added first so - * that they map to the ldapclient::clientNumber() */ - s_LDAPSearch->updateCompletionWeights(); - TQValueList< LdapClient* > clients = s_LDAPSearch->clients(); - int clientIndex = 0; - for ( TQValueList<LdapClient*>::iterator it = clients.begin(); it != clients.end(); ++it, ++clientIndex ) { - const int sourceIndex = addCompletionSource( "LDAP server: " + (*it)->server().host(), (*it)->completionWeight() ); - s_ldapClientToCompletionSourceMap->insert( clientIndex, sourceIndex ); - } -} - -void AddresseeLineEdit::init() -{ - if ( !s_completion ) { - completionDeleter.setObject( s_completion, new KMailCompletion() ); - s_completion->setOrder( completionOrder() ); - s_completion->setIgnoreCase( true ); - - completionItemsDeleter.setObject( s_completionItemMap, new KPIM::CompletionItemsMap() ); - completionSourcesDeleter.setObject( s_completionSources, new TQStringList() ); - completionSourceWeightsDeleter.setObject( s_completionSourceWeights, new TQMap<TQString,int> ); - ldapClientToCompletionSourceMapDeleter.setObject( s_ldapClientToCompletionSourceMap, new TQMap<int,int> ); - } -// connect( s_completion, TQT_SIGNAL( match( const TQString& ) ), -// this, TQT_SLOT( slotMatched( const TQString& ) ) ); - - if ( m_useCompletion ) { - if ( !s_LDAPTimer ) { - ldapTimerDeleter.setObject( s_LDAPTimer, new TQTimer( 0, "ldapTimerDeleter" ) ); - ldapSearchDeleter.setObject( s_LDAPSearch, new KPIM::LdapSearch ); - ldapTextDeleter.setObject( s_LDAPText, new TQString ); - } - - updateLDAPWeights(); - - if ( !m_completionInitialized ) { - setCompletionObject( s_completion, false ); - connect( this, TQT_SIGNAL( completion( const TQString& ) ), - this, TQT_SLOT( slotCompletion() ) ); - connect( this, TQT_SIGNAL( returnPressed( const TQString& ) ), - this, TQT_SLOT( slotReturnPressed( const TQString& ) ) ); - - KCompletionBox *box = completionBox(); - connect( box, TQT_SIGNAL( highlighted( const TQString& ) ), - this, TQT_SLOT( slotPopupCompletion( const TQString& ) ) ); - connect( box, TQT_SIGNAL( userCancelled( const TQString& ) ), - TQT_SLOT( slotUserCancelled( const TQString& ) ) ); - - // The emitter is always called KPIM::IMAPCompletionOrder by contract - if ( !connectDCOPSignal( 0, "KPIM::IMAPCompletionOrder", "orderChanged()", - "slotIMAPCompletionOrderChanged()", false ) ) - kdError() << "AddresseeLineEdit: connection to orderChanged() failed" << endl; - - connect( s_LDAPTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotStartLDAPLookup() ) ); - connect( s_LDAPSearch, TQT_SIGNAL( searchData( const KPIM::LdapResultList& ) ), - TQT_SLOT( slotLDAPSearchData( const KPIM::LdapResultList& ) ) ); - - m_completionInitialized = true; - } - } -} - -AddresseeLineEdit::~AddresseeLineEdit() -{ - if ( s_LDAPSearch && s_LDAPLineEdit == this ) - stopLDAPLookup(); -} - -void AddresseeLineEdit::setFont( const TQFont& font ) -{ - KLineEdit::setFont( font ); - if ( m_useCompletion ) - completionBox()->setFont( font ); -} - -void AddresseeLineEdit::allowSemiColonAsSeparator( bool useSemiColonAsSeparator ) -{ - m_useSemiColonAsSeparator = useSemiColonAsSeparator; -} - -void AddresseeLineEdit::allowDistributionLists( bool allowDistLists ) -{ - m_allowDistLists = allowDistLists; -} - -void AddresseeLineEdit::keyPressEvent( TQKeyEvent *e ) -{ - bool accept = false; - - if ( KStdAccel::shortcut( KStdAccel::SubstringCompletion ).contains( KKey( e ) ) ) { - //TODO: add LDAP substring lookup, when it becomes available in KPIM::LDAPSearch - updateSearchString(); - doCompletion( true ); - accept = true; - } else if ( KStdAccel::shortcut( KStdAccel::TextCompletion ).contains( KKey( e ) ) ) { - int len = text().length(); - - if ( len == cursorPosition() ) { // at End? - updateSearchString(); - doCompletion( true ); - accept = true; - } - } - - const TQString oldContent = text(); - if ( !accept ) - KLineEdit::keyPressEvent( e ); - - // if the text didn't change (eg. because a cursor navigation key was pressed) - // we don't need to trigger a new search - if ( oldContent == text() ) - return; - - if ( e->isAccepted() ) { - updateSearchString(); - TQString searchString( m_searchString ); - //LDAP does not know about our string manipulation, remove it - if ( m_searchExtended ) - searchString = m_searchString.mid( 1 ); - - if ( m_useCompletion && s_LDAPTimer != NULL ) { - if ( *s_LDAPText != searchString || s_LDAPLineEdit != this ) - stopLDAPLookup(); - - *s_LDAPText = searchString; - s_LDAPLineEdit = this; - s_LDAPTimer->start( 500, true ); - } - } -} - -void AddresseeLineEdit::insert( const TQString &t ) -{ - if ( !m_smartPaste ) { - KLineEdit::insert( t ); - return; - } - - //kdDebug(5300) << " AddresseeLineEdit::insert( \"" << t << "\" )" << endl; - - TQString newText = t.stripWhiteSpace(); - if ( newText.isEmpty() ) - return; - - // remove newlines in the to-be-pasted string - TQStringList lines = TQStringList::split( TQRegExp("\r?\n"), newText, false ); - for ( TQStringList::iterator it = lines.begin(); - it != lines.end(); ++it ) { - // remove trailing commas and whitespace - (*it).remove( TQRegExp(",?\\s*$") ); - } - newText = lines.join( ", " ); - - if ( newText.startsWith("mailto:") ) { - KURL url( newText ); - newText = url.path(); - } - else if ( newText.find(" at ") != -1 ) { - // Anti-spam stuff - newText.replace( " at ", "@" ); - newText.replace( " dot ", "." ); - } - else if ( newText.find("(at)") != -1 ) { - newText.replace( TQRegExp("\\s*\\(at\\)\\s*"), "@" ); - } - - TQString contents = text(); - int start_sel = 0; - int pos = cursorPosition( ); - - if ( hasSelectedText() ) { - // Cut away the selection. - start_sel = selectionStart(); - pos = start_sel; - contents = contents.left( start_sel ) + contents.mid( start_sel + selectedText().length() ); - } - - int eot = contents.length(); - while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) { - eot--; - } - if ( eot == 0 ) { - contents = TQString(); - } else if ( pos >= eot ) { - if ( contents[ eot - 1 ] == ',' ) { - eot--; - } - contents.truncate( eot ); - contents += ", "; - pos = eot + 2; - } - - contents = contents.left( pos ) + newText + contents.mid( pos ); - setText( contents ); - setEdited( true ); - setCursorPosition( pos + newText.length() ); -} - -void AddresseeLineEdit::setText( const TQString & text ) -{ - ClickLineEdit::setText( text.stripWhiteSpace() ); -} - -void AddresseeLineEdit::paste() -{ - if ( m_useCompletion ) - m_smartPaste = true; - - KLineEdit::paste(); - m_smartPaste = false; -} - -void AddresseeLineEdit::mouseReleaseEvent( TQMouseEvent *e ) -{ - // reimplemented from TQLineEdit::mouseReleaseEvent() - if ( m_useCompletion - && TQApplication::tqclipboard()->supportsSelection() - && !isReadOnly() - && e->button() == Qt::MidButton ) { - m_smartPaste = true; - } - - KLineEdit::mouseReleaseEvent( e ); - m_smartPaste = false; -} - -void AddresseeLineEdit::dropEvent( TQDropEvent *e ) -{ - KURL::List uriList; - if ( !isReadOnly() ) { - if ( KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ) ) { - TQString contents = text(); - // remove trailing white space and comma - int eot = contents.length(); - while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) - eot--; - if ( eot == 0 ) - contents = TQString(); - else if ( contents[ eot - 1 ] == ',' ) { - eot--; - contents.truncate( eot ); - } - bool mailtoURL = false; - // append the mailto URLs - for ( KURL::List::Iterator it = uriList.begin(); - it != uriList.end(); ++it ) { - if ( !contents.isEmpty() ) - contents.append( ", " ); - KURL u( *it ); - if ( u.protocol() == "mailto" ) { - mailtoURL = true; - contents.append( (*it).path() ); - } - } - if ( mailtoURL ) { - setText( contents ); - setEdited( true ); - return; - } - } else { - // Let's see if this drop contains a comma separated list of emails - TQString dropData = TQString::fromUtf8( e->tqencodedData( "text/plain" ) ); - TQStringList addrs = splitEmailAddrList( dropData ); - if ( addrs.count() > 0 ) { - setText( normalizeAddressesAndDecodeIDNs( dropData ) ); - setEdited( true ); - return; - } - } - } - - if ( m_useCompletion ) - m_smartPaste = true; - TQLineEdit::dropEvent( e ); - m_smartPaste = false; -} - -void AddresseeLineEdit::cursorAtEnd() -{ - setCursorPosition( text().length() ); -} - -void AddresseeLineEdit::enableCompletion( bool enable ) -{ - m_useCompletion = enable; -} - -void AddresseeLineEdit::doCompletion( bool ctrlT ) -{ - m_lastSearchMode = ctrlT; - - KGlobalSettings::Completion mode = completionMode(); - - if ( mode == KGlobalSettings::CompletionNone ) - return; - - if ( s_addressesDirty ) { - loadContacts(); // read from local address book - s_completion->setOrder( completionOrder() ); - } - - // cursor at end of string - or Ctrl+T pressed for substring completion? - if ( ctrlT ) { - const TQStringList completions = getAdjustedCompletionItems( false ); - - if ( completions.count() > 1 ) - ; //m_previousAddresses = prevAddr; - else if ( completions.count() == 1 ) - setText( m_previousAddresses + completions.first().stripWhiteSpace() ); - - setCompletedItems( completions, true ); // this makes sure the completion popup is closed if no matching items were found - - cursorAtEnd(); - setCompletionMode( mode ); //set back to previous mode - return; - } - - - switch ( mode ) { - case KGlobalSettings::CompletionPopupAuto: - { - if ( m_searchString.isEmpty() ) - break; - } - - case KGlobalSettings::CompletionPopup: - { - const TQStringList items = getAdjustedCompletionItems( true ); - setCompletedItems( items, false ); - break; - } - - case KGlobalSettings::CompletionShell: - { - TQString match = s_completion->makeCompletion( m_searchString ); - if ( !match.isNull() && match != m_searchString ) { - setText( m_previousAddresses + match ); - setEdited( true ); - cursorAtEnd(); - } - break; - } - - case KGlobalSettings::CompletionMan: // Short-Auto in fact - case KGlobalSettings::CompletionAuto: - { - //force autoSuggest in KLineEdit::keyPressed or setCompletedText will have no effect - setCompletionMode( completionMode() ); - - if ( !m_searchString.isEmpty() ) { - - //if only our \" is left, remove it since user has not typed it either - if ( m_searchExtended && m_searchString == "\"" ){ - m_searchExtended = false; - m_searchString = TQString(); - setText( m_previousAddresses ); - break; - } - - TQString match = s_completion->makeCompletion( m_searchString ); - - if ( !match.isEmpty() ) { - if ( match != m_searchString ) { - TQString adds = m_previousAddresses + match; - setCompletedText( adds ); - } - } else { - if ( !m_searchString.startsWith( "\"" ) ) { - //try with quoted text, if user has not type one already - match = s_completion->makeCompletion( "\"" + m_searchString ); - if ( !match.isEmpty() && match != m_searchString ) { - m_searchString = "\"" + m_searchString; - m_searchExtended = true; - setText( m_previousAddresses + m_searchString ); - setCompletedText( m_previousAddresses + match ); - } - } else if ( m_searchExtended ) { - //our added \" does not work anymore, remove it - m_searchString = m_searchString.mid( 1 ); - m_searchExtended = false; - setText( m_previousAddresses + m_searchString ); - //now try again - match = s_completion->makeCompletion( m_searchString ); - if ( !match.isEmpty() && match != m_searchString ) { - TQString adds = m_previousAddresses + match; - setCompletedText( adds ); - } - } - } - } - break; - } - - case KGlobalSettings::CompletionNone: - default: // fall through - break; - } -} - -void AddresseeLineEdit::slotPopupCompletion( const TQString& completion ) -{ - setText( m_previousAddresses + completion.stripWhiteSpace() ); - cursorAtEnd(); -// slotMatched( m_previousAddresses + completion ); - updateSearchString(); -} - -void AddresseeLineEdit::slotReturnPressed( const TQString& item ) -{ - Q_UNUSED( item ); - TQListBoxItem* i = completionBox()->selectedItem(); - if ( i != 0 ) - slotPopupCompletion( i->text() ); -} - -void AddresseeLineEdit::loadContacts() -{ - s_completion->clear(); - s_completionItemMap->clear(); - s_addressesDirty = false; - //m_contactMap.clear(); - - TQApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while - - KConfig config( "kpimcompletionorder" ); // The weights for non-imap kabc resources is there. - config.setGroup( "CompletionWeights" ); - - KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); - // Can't just use the addressbook's iterator, we need to know which subresource - // is behind which contact. - TQPtrList<KABC::Resource> resources( addressBook->resources() ); - for( TQPtrListIterator<KABC::Resource> resit( resources ); *resit; ++resit ) { - KABC::Resource* resource = *resit; - KPIM::ResourceABC* resabc = dynamic_cast<ResourceABC *>( resource ); - if ( resabc ) { // IMAP KABC resource; need to associate each contact with the subresource - const TQMap<TQString, TQString> uidToResourceMap = resabc->uidToResourceMap(); - KABC::Resource::Iterator it; - for ( it = resource->begin(); it != resource->end(); ++it ) { - TQString uid = (*it).uid(); - TQMap<TQString, TQString>::const_iterator wit = uidToResourceMap.find( uid ); - const TQString subresourceLabel = resabc->subresourceLabel( *wit ); - const int weight = ( wit != uidToResourceMap.end() ) ? resabc->subresourceCompletionWeight( *wit ) : 80; - const int idx = addCompletionSource( subresourceLabel, weight ); - - //kdDebug(5300) << (*it).fullEmail() << " subres=" << *wit << " weight=" << weight << endl; - addContact( *it, weight, idx ); - } - } else { // KABC non-imap resource - int weight = config.readNumEntry( resource->identifier(), 60 ); - int sourceIndex = addCompletionSource( resource->resourceName(), weight ); - KABC::Resource::Iterator it; - for ( it = resource->begin(); it != resource->end(); ++it ) { - addContact( *it, weight, sourceIndex ); - } - } - } - -#ifndef KDEPIM_NEW_DISTRLISTS // new distr lists are normal contact, already done above - int weight = config.readNumEntry( "DistributionLists", 60 ); - KABC::DistributionListManager manager( addressBook ); - manager.load(); - const TQStringList distLists = manager.listNames(); - TQStringList::const_iterator listIt; - int idx = addCompletionSource( i18n( "Distribution Lists" ) ); - for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) { - - //for KGlobalSettings::CompletionAuto - addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx ); - - //for CompletionShell, CompletionPopup - TQStringList sl( (*listIt).simplifyWhiteSpace() ); - addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx, &sl ); - - } -#endif - - TQApplication::restoreOverrideCursor(); - - if ( !m_addressBookConnected ) { - connect( addressBook, TQT_SIGNAL( addressBookChanged( AddressBook* ) ), TQT_SLOT( loadContacts() ) ); - m_addressBookConnected = true; - } -} - -void AddresseeLineEdit::addContact( const KABC::Addressee& addr, int weight, int source ) -{ -#ifdef KDEPIM_NEW_DISTRLISTS - if ( KPIM::DistributionList::isDistributionList( addr ) ) { - //kdDebug(5300) << "AddresseeLineEdit::addContact() distribution list \"" << addr.formattedName() << "\" weight=" << weight << endl; - - if ( m_allowDistLists ) { - //for CompletionAuto - addCompletionItem( addr.formattedName(), weight, source ); - - //for CompletionShell, CompletionPopup - TQStringList sl( addr.formattedName() ); - addCompletionItem( addr.formattedName(), weight, source, &sl ); - } - - return; - } -#endif - //m_contactMap.insert( addr.realName(), addr ); - const TQStringList emails = addr.emails(); - TQStringList::ConstIterator it; - const int prefEmailWeight = 1; //increment weight by prefEmailWeight - int isPrefEmail = prefEmailWeight; //first in list is preferredEmail - for ( it = emails.begin(); it != emails.end(); ++it ) { - //TODO: highlight preferredEmail - const TQString email( (*it) ); - const TQString givenName = addr.givenName(); - const TQString familyName= addr.familyName(); - const TQString nickName = addr.nickName(); - const TQString domain = email.mid( email.find( '@' ) + 1 ); - TQString fullEmail = addr.fullEmail( email ); - //TODO: let user decide what fields to use in lookup, e.g. company, city, ... - - //for CompletionAuto - if ( givenName.isEmpty() && familyName.isEmpty() ) { - addCompletionItem( fullEmail, weight + isPrefEmail, source ); // use whatever is there - } else { - const TQString byFirstName= "\"" + givenName + " " + familyName + "\" <" + email + ">"; - const TQString byLastName = "\"" + familyName + ", " + givenName + "\" <" + email + ">"; - addCompletionItem( byFirstName, weight + isPrefEmail, source ); - addCompletionItem( byLastName, weight + isPrefEmail, source ); - } - - addCompletionItem( email, weight + isPrefEmail, source ); - - if ( !nickName.isEmpty() ){ - const TQString byNick = "\"" + nickName + "\" <" + email + ">"; - addCompletionItem( byNick, weight + isPrefEmail, source ); - } - - if ( !domain.isEmpty() ){ - const TQString byDomain = "\"" + domain + " " + familyName + " " + givenName + "\" <" + email + ">"; - addCompletionItem( byDomain, weight + isPrefEmail, source ); - } - - //for CompletionShell, CompletionPopup - TQStringList keyWords; - const TQString realName = addr.realName(); - - if ( !givenName.isEmpty() && !familyName.isEmpty() ) { - keyWords.append( givenName + " " + familyName ); - keyWords.append( familyName + " " + givenName ); - keyWords.append( familyName + ", " + givenName); - }else if ( !givenName.isEmpty() ) - keyWords.append( givenName ); - else if ( !familyName.isEmpty() ) - keyWords.append( familyName ); - - if ( !nickName.isEmpty() ) - keyWords.append( nickName ); - - if ( !realName.isEmpty() ) - keyWords.append( realName ); - - if ( !domain.isEmpty() ) - keyWords.append( domain ); - - keyWords.append( email ); - - /* KMailCompletion does not have knowledge about identities, it stores emails and - * keywords for each email. KMailCompletion::allMatches does a lookup on the - * keywords and returns an ordered list of emails. In order to get the preferred - * email before others for each identity we use this little trick. - * We remove the <blank> in getAdjustedCompletionItems. - */ - if ( isPrefEmail == prefEmailWeight ) - fullEmail.replace( " <", " <" ); - - addCompletionItem( fullEmail, weight + isPrefEmail, source, &keyWords ); - isPrefEmail = 0; - -#if 0 - int len = (*it).length(); - if ( len == 0 ) continue; - if( '\0' == (*it)[len-1] ) - --len; - const TQString tmp = (*it).left( len ); - const TQString fullEmail = addr.fullEmail( tmp ); - //kdDebug(5300) << "AddresseeLineEdit::addContact() \"" << fullEmail << "\" weight=" << weight << endl; - addCompletionItem( fullEmail.simplifyWhiteSpace(), weight, source ); - // Try to guess the last name: if found, we add an extra - // entry to the list to make sure completion works even - // if the user starts by typing in the last name. - TQString name( addr.realName().simplifyWhiteSpace() ); - if( name.endsWith("\"") ) - name.truncate( name.length()-1 ); - if( name.startsWith("\"") ) - name = name.mid( 1 ); - - // While we're here also add "email (full name)" for completion on the email - if ( !name.isEmpty() ) - addCompletionItem( addr.preferredEmail() + " (" + name + ")", weight, source ); - - bool bDone = false; - int i = -1; - while( ( i = name.findRev(' ') ) > 1 && !bDone ) { - TQString sLastName( name.mid( i+1 ) ); - if( ! sLastName.isEmpty() && - 2 <= sLastName.length() && // last names must be at least 2 chars long - ! sLastName.endsWith(".") ) { // last names must not end with a dot (like "Jr." or "Sr.") - name.truncate( i ); - if( !name.isEmpty() ){ - sLastName.prepend( "\"" ); - sLastName.append( ", " + name + "\" <" ); - } - TQString sExtraEntry( sLastName ); - sExtraEntry.append( tmp.isEmpty() ? addr.preferredEmail() : tmp ); - sExtraEntry.append( ">" ); - //kdDebug(5300) << "AddresseeLineEdit::addContact() added extra \"" << sExtraEntry.simplifyWhiteSpace() << "\" weight=" << weight << endl; - addCompletionItem( sExtraEntry.simplifyWhiteSpace(), weight, source ); - bDone = true; - } - if( !bDone ) { - name.truncate( i ); - if( name.endsWith("\"") ) - name.truncate( name.length()-1 ); - } - } -#endif - } -} - -void AddresseeLineEdit::addCompletionItem( const TQString& string, int weight, int completionItemSource, const TQStringList * keyWords ) -{ - // Check if there is an exact match for item already, and use the max weight if so. - // Since there's no way to get the information from KCompletion, we have to keep our own TQMap - CompletionItemsMap::iterator it = s_completionItemMap->find( string ); - if ( it != s_completionItemMap->end() ) { - weight = TQMAX( ( *it ).first, weight ); - ( *it ).first = weight; - } else { - s_completionItemMap->insert( string, tqMakePair( weight, completionItemSource ) ); - } - if ( keyWords == 0 ) - s_completion->addItem( string, weight ); - else - s_completion->addItemWithKeys( string, weight, keyWords ); -} - -void AddresseeLineEdit::slotStartLDAPLookup() -{ - KGlobalSettings::Completion mode = completionMode(); - - if ( mode == KGlobalSettings::CompletionNone ) - return; - - if ( !s_LDAPSearch->isAvailable() ) { - return; - } - if ( s_LDAPLineEdit != this ) - return; - - startLoadingLDAPEntries(); -} - -void AddresseeLineEdit::stopLDAPLookup() -{ - s_LDAPSearch->cancelSearch(); - s_LDAPLineEdit = NULL; -} - -void AddresseeLineEdit::startLoadingLDAPEntries() -{ - TQString s( *s_LDAPText ); - // TODO cache last? - TQString prevAddr; - int n = s.findRev( ',' ); - if ( n >= 0 ) { - prevAddr = s.left( n + 1 ) + ' '; - s = s.mid( n + 1, 255 ).stripWhiteSpace(); - } - - if ( s.isEmpty() ) - return; - - //loadContacts(); // TODO reuse these? - s_LDAPSearch->startSearch( s ); -} - -void AddresseeLineEdit::slotLDAPSearchData( const KPIM::LdapResultList& adrs ) -{ - if ( adrs.isEmpty() || s_LDAPLineEdit != this ) - return; - - for ( KPIM::LdapResultList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) { - KABC::Addressee addr; - addr.setNameFromString( (*it).name ); - addr.setEmails( (*it).email ); - - if ( !s_ldapClientToCompletionSourceMap->contains( (*it).clientNumber ) ) - updateLDAPWeights(); // we got results from a new source, so update the completion sources - - addContact( addr, (*it).completionWeight, (*s_ldapClientToCompletionSourceMap)[ (*it ).clientNumber ] ); - } - - if ( (hasFocus() || completionBox()->hasFocus() ) - && completionMode() != KGlobalSettings::CompletionNone - && completionMode() != KGlobalSettings::CompletionShell ) { - setText( m_previousAddresses + m_searchString ); - // only complete again if the user didn't change the selection while we were waiting - // otherwise the completion box will be closed - if ( m_searchString.stripWhiteSpace() != completionBox()->currentText().stripWhiteSpace() ) - doCompletion( m_lastSearchMode ); - } -} - -void AddresseeLineEdit::setCompletedItems( const TQStringList& items, bool autoSuggest ) -{ - KCompletionBox* completionBox = this->completionBox(); - - if ( !items.isEmpty() && - !(items.count() == 1 && m_searchString == items.first()) ) - { - TQString oldCurrentText = completionBox->currentText(); - TQListBoxItem *itemUnderMouse = completionBox->itemAt( - completionBox->viewport()->mapFromGlobal(TQCursor::pos()) ); - TQString oldTextUnderMouse; - TQPoint oldPosOfItemUnderMouse; - if ( itemUnderMouse ) { - oldTextUnderMouse = itemUnderMouse->text(); - oldPosOfItemUnderMouse = completionBox->tqitemRect( itemUnderMouse ).topLeft(); - } - - completionBox->setItems( items ); - - if ( !completionBox->isVisible() ) { - if ( !m_searchString.isEmpty() ) - completionBox->setCancelledText( m_searchString ); - completionBox->popup(); - // we have to install the event filter after popup(), since that - // calls show(), and that's where KCompletionBox installs its filter. - // We want to be first, though, so do it now. - if ( s_completion->order() == KCompletion::Weighted ) - tqApp->installEventFilter( this ); - } - - // Try to re-select what was selected before, otherrwise use the first - // item, if there is one - TQListBoxItem* item = 0; - if ( oldCurrentText.isEmpty() - || ( item = completionBox->findItem( oldCurrentText ) ) == 0 ) { - item = completionBox->item( 1 ); - } - if ( item ) - { - if ( itemUnderMouse ) { - TQListBoxItem *newItemUnderMouse = completionBox->findItem( oldTextUnderMouse ); - // if the mouse was over an item, before, but now that's elsewhere, - // move the cursor, so folks don't accidently click the wrong item - if ( newItemUnderMouse ) { - TQRect r = completionBox->tqitemRect( newItemUnderMouse ); - TQPoint target = r.topLeft(); - if ( oldPosOfItemUnderMouse != target ) { - target.setX( target.x() + r.width()/2 ); - TQCursor::setPos( completionBox->viewport()->mapToGlobal(target) ); - } - } - } - completionBox->blockSignals( true ); - completionBox->setSelected( item, true ); - completionBox->setCurrentItem( item ); - completionBox->ensureCurrentVisible(); - - completionBox->blockSignals( false ); - } - - if ( autoSuggest ) - { - int index = items.first().find( m_searchString ); - TQString newText = items.first().mid( index ); - setUserSelection(false); - setCompletedText(newText,true); - } - } - else - { - if ( completionBox && completionBox->isVisible() ) { - completionBox->hide(); - completionBox->setItems( TQStringList() ); - } - } -} - -TQPopupMenu* AddresseeLineEdit::createPopupMenu() -{ - TQPopupMenu *menu = KLineEdit::createPopupMenu(); - if ( !menu ) - return 0; - - if ( m_useCompletion ){ - menu->setItemVisible( ShortAutoCompletion, false ); - menu->setItemVisible( PopupAutoCompletion, false ); - menu->insertItem( i18n( "Configure Completion Order..." ), - this, TQT_SLOT( slotEditCompletionOrder() ) ); - } - return menu; -} - -void AddresseeLineEdit::slotEditCompletionOrder() -{ - init(); // for s_LDAPSearch - CompletionOrderEditor editor( s_LDAPSearch, this ); - editor.exec(); - if ( m_useCompletion ) { - updateLDAPWeights(); - s_addressesDirty = true; - } -} - -void KPIM::AddresseeLineEdit::slotIMAPCompletionOrderChanged() -{ - if ( m_useCompletion ) - s_addressesDirty = true; -} - -void KPIM::AddresseeLineEdit::slotUserCancelled( const TQString& cancelText ) -{ - if ( s_LDAPSearch && s_LDAPLineEdit == this ) - stopLDAPLookup(); - userCancelled( m_previousAddresses + cancelText ); // in KLineEdit -} - -void AddresseeLineEdit::updateSearchString() -{ - m_searchString = text(); - - int n = -1; - bool inQuote = false; - uint searchStringLength = m_searchString.length(); - for ( uint i = 0; i < searchStringLength; ++i ) { - if ( m_searchString[ i ] == '"' ) { - inQuote = !inQuote; - } - if ( m_searchString[ i ] == '\\' && - (i + 1) < searchStringLength && m_searchString[ i + 1 ] == '"' ) { - ++i; - } - if ( inQuote ) { - continue; - } - if ( i < searchStringLength && - ( m_searchString[ i ] == ',' || - ( m_useSemiColonAsSeparator && m_searchString[ i ] == ';' ) ) ) { - n = i; - } - } - - if ( n >= 0 ) { - ++n; // Go past the "," - - int len = m_searchString.length(); - - // Increment past any whitespace... - while ( n < len && m_searchString[ n ].isSpace() ) - ++n; - - m_previousAddresses = m_searchString.left( n ); - m_searchString = m_searchString.mid( n ).stripWhiteSpace(); - } else { - m_previousAddresses = TQString(); - } -} - -void KPIM::AddresseeLineEdit::slotCompletion() -{ - // Called by KLineEdit's keyPressEvent for CompletionModes Auto,Popup -> new text, update search string - // not called for CompletionShell, this is been taken care of in AddresseeLineEdit::keyPressEvent - updateSearchString(); - if ( completionBox() ) - completionBox()->setCancelledText( m_searchString ); - doCompletion( false ); -} - -// not cached, to make sure we get an up-to-date value when it changes -KCompletion::CompOrder KPIM::AddresseeLineEdit::completionOrder() -{ - KConfig config( "kpimcompletionorder" ); - config.setGroup( "General" ); - const TQString order = config.readEntry( "CompletionOrder", "Weighted" ); - - if ( order == "Weighted" ) - return KCompletion::Weighted; - else - return KCompletion::Sorted; -} - -int KPIM::AddresseeLineEdit::addCompletionSource( const TQString &source, int weight ) -{ - TQMap<TQString,int>::iterator it = s_completionSourceWeights->find( source ); - if ( it == s_completionSourceWeights->end() ) - s_completionSourceWeights->insert( source, weight ); - else - (*s_completionSourceWeights)[source] = weight; - - int sourceIndex = s_completionSources->findIndex( source ); - if ( sourceIndex == -1 ) { - s_completionSources->append( source ); - return s_completionSources->size() - 1; - } - else - return sourceIndex; -} - -bool KPIM::AddresseeLineEdit::eventFilter(TQObject *obj, TQEvent *e) -{ - if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(completionBox()) ) { - if ( e->type() == TQEvent::MouseButtonPress || - e->type() == TQEvent::MouseMove || - e->type() == TQEvent::MouseButtonRelease || - e->type() == TQEvent::MouseButtonDblClick ) { - TQMouseEvent* me = TQT_TQMOUSEEVENT( e ); - // find list box item at the event position - TQListBoxItem *item = completionBox()->itemAt( me->pos() ); - if ( !item ) { - // In the case of a mouse move outside of the box we don't want - // the parent to fuzzy select a header by mistake. - bool eat = e->type() == TQEvent::MouseMove; - return eat; - } - // avoid selection of headers on button press, or move or release while - // a button is pressed - if ( e->type() == TQEvent::MouseButtonPress - || me->state() & Qt::LeftButton || me->state() & Qt::MidButton - || me->state() & Qt::RightButton ) { - if ( itemIsHeader(item) ) { - return true; // eat the event, we don't want anything to happen - } else { - // if we are not on one of the group heading, make sure the item - // below or above is selected, not the heading, inadvertedly, due - // to fuzzy auto-selection from TQListBox - completionBox()->setCurrentItem( item ); - completionBox()->setSelected( completionBox()->index( item ), true ); - if ( e->type() == TQEvent::MouseMove ) - return true; // avoid fuzzy selection behavior - } - } - } - } - if ( ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(this) ) && - ( e->type() == TQEvent::AccelOverride ) ) { - TQKeyEvent *ke = TQT_TQKEYEVENT( e ); - if ( ke->key() == Key_Up || ke->key() == Key_Down || ke->key() == Key_Tab ) { - ke->accept(); - return true; - } - } - if ( ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(this) ) && - ( e->type() == TQEvent::KeyPress || e->type() == TQEvent::KeyRelease ) && - completionBox()->isVisible() ) { - TQKeyEvent *ke = TQT_TQKEYEVENT( e ); - int currentIndex = completionBox()->currentItem(); - if ( currentIndex < 0 ) { - return true; - } - - if ( ke->key() == Key_Up ) { - //kdDebug() << "EVENTFILTER: Key_Up currentIndex=" << currentIndex << endl; - // figure out if the item we would be moving to is one we want - // to ignore. If so, go one further - TQListBoxItem *itemAbove = completionBox()->item( currentIndex ); - if ( itemAbove && itemIsHeader(itemAbove) ) { - // there is a header above us, check if there is even further up - // and if so go one up, so it'll be selected - if ( currentIndex > 0 && completionBox()->item( currentIndex - 1 ) ) { - //kdDebug() << "EVENTFILTER: Key_Up -> skipping " << currentIndex - 1 << endl; - completionBox()->setCurrentItem( itemAbove->prev() ); - completionBox()->setSelected( currentIndex - 1, true ); - } else if ( currentIndex == 0 ) { - // nothing to skip to, let's stay where we are, but make sure the - // first header becomes visible, if we are the first real entry - completionBox()->ensureVisible( 0, 0 ); - //Kolab issue 2941: be sure to add email even if it's the only element. - if ( itemIsHeader( completionBox()->item( currentIndex ) ) ) { - currentIndex++; - } - completionBox()->setCurrentItem( itemAbove ); - completionBox()->setSelected( currentIndex, true ); - } - return true; - } - } else if ( ke->key() == Key_Down ) { - // same strategy for downwards - //kdDebug() << "EVENTFILTER: Key_Down. currentIndex=" << currentIndex << endl; - TQListBoxItem *itemBelow = completionBox()->item( currentIndex ); - if ( itemBelow && itemIsHeader( itemBelow ) ) { - if ( completionBox()->item( currentIndex + 1 ) ) { - //kdDebug() << "EVENTFILTER: Key_Down -> skipping " << currentIndex+1 << endl; - completionBox()->setCurrentItem( itemBelow->next() ); - completionBox()->setSelected( currentIndex + 1, true ); - } else { - // nothing to skip to, let's stay where we are - completionBox()->setCurrentItem( itemBelow ); - completionBox()->setSelected( currentIndex, true ); - } - return true; - } - // special case of the last and only item in the list needing selection - if ( !itemBelow && currentIndex == 1 ) { - completionBox()->setSelected( currentIndex, true ); - } - // special case of the initial selection, which is unfortunately a header. - // Setting it to selected tricks KCompletionBox into not treating is special - // and selecting making it current, instead of the one below. - TQListBoxItem *item = completionBox()->item( currentIndex ); - if ( item && itemIsHeader(item) ) { - completionBox()->setSelected( currentIndex, true ); - } - } else if ( e->type() == TQEvent::KeyRelease && - ( ke->key() == Key_Tab || ke->key() == Key_Backtab ) ) { - //kdDebug() << "EVENTFILTER: Key_Tab. currentIndex=" << currentIndex << endl; - /// first, find the header of the current section - TQListBoxItem *myHeader = 0; - const int iterationstep = ke->key() == Key_Tab ? 1 : -1; - int i = TQMIN( TQMAX( currentIndex - iterationstep, 0 ), completionBox()->count() - 1 ); - while ( i>=0 ) { - if ( itemIsHeader( completionBox()->item(i) ) ) { - myHeader = completionBox()->item( i ); - break; - } - i--; - } - Q_ASSERT( myHeader ); // we should always be able to find a header - - // find the next header (searching backwards, for Key_Backtab) - TQListBoxItem *nextHeader = 0; - // when iterating forward, start at the currentindex, when backwards, - // one up from our header, or at the end - uint j; - if ( ke->key() == Key_Tab ) { - j = currentIndex; - } else { - i = completionBox()->index( myHeader ); - if ( i == 0 ) { - j = completionBox()->count() - 1; - } else { - j = ( i - 1 ) % completionBox()->count(); - } - } - while ( ( nextHeader = completionBox()->item( j ) ) && nextHeader != myHeader ) { - if ( itemIsHeader(nextHeader) ) { - break; - } - j = (j + iterationstep) % completionBox()->count(); - } - if ( nextHeader && nextHeader != myHeader ) { - TQListBoxItem *item = completionBox()->item( j + 1 ); - if ( item && !itemIsHeader(item) ) { - completionBox()->setSelected( item, true ); - completionBox()->setCurrentItem( item ); - completionBox()->ensureCurrentVisible(); - } - } - return true; - } - } - return ClickLineEdit::eventFilter( obj, e ); -} - -class SourceWithWeight { - public: - int weight; // the weight of the source - TQString sourceName; // the name of the source, e.g. "LDAP Server" - int index; // index into s_completionSources - - bool operator< ( const SourceWithWeight &other ) { - if ( weight > other.weight ) - return true; - if ( weight < other.weight ) - return false; - return sourceName < other.sourceName; - } -}; - -const TQStringList KPIM::AddresseeLineEdit::getAdjustedCompletionItems( bool fullSearch ) -{ - TQStringList items = fullSearch ? - s_completion->allMatches( m_searchString ) - : s_completion->substringCompletion( m_searchString ); - - // For weighted mode, the algorithm is the following: - // In the first loop, we add each item to its section (there is one section per completion source) - // We also add spaces in front of the items. - // The sections are appended to the items list. - // In the second loop, we then walk through the sections and add all the items in there to the - // sorted item list, which is the final result. - // - // The algo for non-weighted mode is different. - - int lastSourceIndex = -1; - unsigned int i = 0; - - // Maps indices of the items list, which are section headers/source items, - // to a TQStringList which are the items of that section/source. - TQMap<int, TQStringList> sections; - TQStringList sortedItems; - for ( TQStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i ) { - CompletionItemsMap::const_iterator cit = s_completionItemMap->find(*it); - if ( cit == s_completionItemMap->end() ) - continue; - int idx = (*cit).second; - - if ( s_completion->order() == KCompletion::Weighted ) { - if ( lastSourceIndex == -1 || lastSourceIndex != idx ) { - const TQString sourceLabel( (*s_completionSources)[idx] ); - if ( sections.find(idx) == sections.end() ) { - items.insert( it, sourceLabel ); - } - lastSourceIndex = idx; - } - (*it) = (*it).prepend( s_completionItemIndentString ); - // remove preferred email sort <blank> added in addContact() - (*it).replace( " <", " <" ); - } - sections[idx].append( *it ); - - if ( s_completion->order() == KCompletion::Sorted ) { - sortedItems.append( *it ); - } - } - - if ( s_completion->order() == KCompletion::Weighted ) { - - // Sort the sections - TQValueList<SourceWithWeight> sourcesAndWeights; - for ( uint i = 0; i < s_completionSources->size(); i++ ) { - SourceWithWeight sww; - sww.sourceName = (*s_completionSources)[i]; - sww.weight = (*s_completionSourceWeights)[sww.sourceName]; - sww.index = i; - sourcesAndWeights.append( sww ); - } - qHeapSort( sourcesAndWeights ); - - // Add the sections and their items to the final sortedItems result list - for( uint i = 0; i < sourcesAndWeights.size(); i++ ) { - TQStringList sectionItems = sections[sourcesAndWeights[i].index]; - if ( !sectionItems.isEmpty() ) { - sortedItems.append( sourcesAndWeights[i].sourceName ); - TQStringList sectionItems = sections[sourcesAndWeights[i].index]; - for ( TQStringList::Iterator sit( sectionItems.begin() ), send( sectionItems.end() ); - sit != send; ++sit ) { - sortedItems.append( *sit ); - } - } - } - } else { - sortedItems.sort(); - } - return sortedItems; -} -#include "addresseelineedit.moc" |