diff options
Diffstat (limited to 'tdeabc/addresslineedit.cpp')
-rw-r--r-- | tdeabc/addresslineedit.cpp | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/tdeabc/addresslineedit.cpp b/tdeabc/addresslineedit.cpp new file mode 100644 index 000000000..dc7a889a1 --- /dev/null +++ b/tdeabc/addresslineedit.cpp @@ -0,0 +1,610 @@ +/* + This file is part of libkabc. + 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> + + 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. +*/ + +// $Id$ + +#include "addresslineedit.h" + +#include <tqapplication.h> +#include <tqobject.h> +#include <tqptrlist.h> +#include <tqregexp.h> +#include <tqevent.h> +#include <tqdragobject.h> + +#include <tdecompletionbox.h> +#include <tdeconfig.h> +#include <kcursor.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> +#include <tdestdaccel.h> +#include <kurldrag.h> + +#include <tdeabc/stdaddressbook.h> +#include <tdeabc/distributionlist.h> +#include "ldapclient.h" + +#include <kdebug.h> + +//============================================================================= +// +// Class AddressLineEdit +// +//============================================================================= + + +using namespace KABC; + +TDECompletion * AddressLineEdit::s_completion = 0L; +bool AddressLineEdit::s_addressesDirty = false; +TQTimer* AddressLineEdit::s_LDAPTimer = 0L; +LdapSearch* AddressLineEdit::s_LDAPSearch = 0L; +TQString* AddressLineEdit::s_LDAPText = 0L; +AddressLineEdit* AddressLineEdit::s_LDAPLineEdit = 0L; +TDEConfig *AddressLineEdit::s_config = 0L; + +static KStaticDeleter<TDECompletion> completionDeleter; +static KStaticDeleter<TQTimer> ldapTimerDeleter; +static KStaticDeleter<LdapSearch> ldapSearchDeleter; +static KStaticDeleter<TQString> ldapTextDeleter; +static KStaticDeleter<TDEConfig> configDeleter; + +AddressLineEdit::AddressLineEdit(TQWidget* parent, + bool useCompletion, + const char *name) + : KLineEdit(parent,name) +{ + m_useCompletion = useCompletion; + m_completionInitialized = false; + m_smartPaste = false; + + init(); + + // Whenever a new AddressLineEdit is created (== a new composer is created), + // we set a dirty flag to reload the addresses upon the first completion. + // The address completions are shared between all AddressLineEdits. + // Is there a signal that tells us about addressbook updates? + if (m_useCompletion) + s_addressesDirty = true; +} + + +//----------------------------------------------------------------------------- +void AddressLineEdit::init() +{ + if ( !s_completion ) { + completionDeleter.setObject( s_completion, new TDECompletion() ); + s_completion->setOrder( TDECompletion::Sorted ); + s_completion->setIgnoreCase( true ); + } + + if( m_useCompletion ) { + if( !s_LDAPTimer ) { + ldapTimerDeleter.setObject( s_LDAPTimer, new TQTimer ); + ldapSearchDeleter.setObject( s_LDAPSearch, new LdapSearch ); + ldapTextDeleter.setObject( s_LDAPText, new TQString ); + } + connect( s_LDAPTimer, TQT_SIGNAL( timeout()), TQT_SLOT( slotStartLDAPLookup())); + connect( s_LDAPSearch, TQT_SIGNAL( searchData( const TQStringList& )), + TQT_SLOT( slotLDAPSearchData( const TQStringList& ))); + } + + if ( m_useCompletion && !m_completionInitialized ) + { + setCompletionObject( s_completion, false ); // we handle it ourself + connect( this, TQT_SIGNAL( completion(const TQString&)), + this, TQT_SLOT(slotCompletion() )); + + TDECompletionBox *box = completionBox(); + connect( box, TQT_SIGNAL( highlighted( const TQString& )), + this, TQT_SLOT( slotPopupCompletion( const TQString& ) )); + connect( box, TQT_SIGNAL( userCancelled( const TQString& )), + TQT_SLOT( userCancelled( const TQString& ))); + + m_completionInitialized = true; // don't connect muliple times. That's + // ugly, tho, better have completionBox() + // virtual in KDE 4 + // Why? This is only called once. Why should this be called more + // than once? And why was this protected? + } +} + +//----------------------------------------------------------------------------- +AddressLineEdit::~AddressLineEdit() +{ +} + +//----------------------------------------------------------------------------- + +TDEConfig* AddressLineEdit::config() +{ + if ( !s_config ) + configDeleter.setObject( s_config, new TDEConfig( "kabldaprc", false, false ) ); // Open read-write, no kdeglobals + + return s_config; +} + +void AddressLineEdit::setFont( const TQFont& font ) +{ + KLineEdit::setFont( font ); + if ( m_useCompletion ) + completionBox()->setFont( font ); +} + +//----------------------------------------------------------------------------- +void AddressLineEdit::keyPressEvent(TQKeyEvent *e) +{ + bool accept = false; + + if (TDEStdAccel::shortcut(TDEStdAccel::SubstringCompletion).contains(KKey(e))) + { + doCompletion(true); + accept = true; + } + else if (TDEStdAccel::shortcut(TDEStdAccel::TextCompletion).contains(KKey(e))) + { + int len = text().length(); + + if (len == cursorPosition()) // at End? + { + doCompletion(true); + accept = true; + } + } + + if( !accept ) + KLineEdit::keyPressEvent( e ); + + if( e->isAccepted()) + { + if( m_useCompletion && s_LDAPTimer != NULL ) + { + if( *s_LDAPText != text()) + stopLDAPLookup(); + *s_LDAPText = text(); + s_LDAPLineEdit = this; + s_LDAPTimer->start( 500, true ); + } + } +} + +void AddressLineEdit::mouseReleaseEvent( TQMouseEvent * e ) +{ + if (m_useCompletion && (e->button() == Qt::MidButton)) + { + m_smartPaste = true; + KLineEdit::mouseReleaseEvent(e); + m_smartPaste = false; + return; + } + KLineEdit::mouseReleaseEvent(e); +} + +void AddressLineEdit::insert(const TQString &t) +{ + if (!m_smartPaste) + { + KLineEdit::insert(t); + return; + } + TQString newText = t.stripWhiteSpace(); + if (newText.isEmpty()) + return; + + // remove newlines in the to-be-pasted string as well as an eventual + // mailto: protocol + newText.replace( TQRegExp("\r?\n"), ", " ); + if ( newText.startsWith( "mailto:" ) ) + { + KURL u(newText); + newText = u.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 end_sel = 0; + int pos = cursorPosition(); + if (getSelection(&start_sel, &end_sel)) + { + // Cut away the selection. + if (pos > end_sel) + pos -= (end_sel - start_sel); + else if (pos > start_sel) + pos = start_sel; + contents = contents.left(start_sel) + contents.right(end_sel+1); + } + + int eot = contents.length(); + while ((eot > 0) && contents[eot-1].isSpace()) eot--; + if (eot == 0) + { + contents = TQString::null; + } + 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); + setCursorPosition(pos+newText.length()); +} + +void AddressLineEdit::paste() +{ + if (m_useCompletion) + m_smartPaste = true; + KLineEdit::paste(); + m_smartPaste = false; +} + +//----------------------------------------------------------------------------- +void AddressLineEdit::cursorAtEnd() +{ + setCursorPosition( text().length() ); +} + +//----------------------------------------------------------------------------- +void AddressLineEdit::enableCompletion(bool enable) +{ + m_useCompletion = enable; +} + +//----------------------------------------------------------------------------- +void AddressLineEdit::doCompletion(bool ctrlT) +{ + if ( !m_useCompletion ) + return; + + TQString prevAddr; + + TQString s(text()); + int n = s.findRev(','); + + if (n >= 0) + { + n++; // Go past the "," + + int len = s.length(); + + // Increment past any whitespace... + while( n < len && s[n].isSpace() ) + n++; + + prevAddr = s.left(n); + s = s.mid(n,255).stripWhiteSpace(); + } + + if ( s_addressesDirty ) + loadAddresses(); + + if ( ctrlT ) + { + TQStringList completions = s_completion->substringCompletion( s ); + if (completions.count() > 1) { + m_previousAddresses = prevAddr; + setCompletedItems( completions ); + } + else if (completions.count() == 1) + setText(prevAddr + completions.first()); + + cursorAtEnd(); + return; + } + + TDEGlobalSettings::Completion mode = completionMode(); + + switch ( mode ) + { + case TDEGlobalSettings::CompletionPopupAuto: + { + if (s.isEmpty()) + break; + } + case TDEGlobalSettings::CompletionPopup: + { + m_previousAddresses = prevAddr; + TQStringList items = s_completion->allMatches( s ); + items += s_completion->allMatches( "\"" + s ); + items += s_completion->substringCompletion( '<' + s ); + uint beforeDollarCompletionCount = items.count(); + + if( s.find( ' ' ) == -1 ) // one word, possibly given name + items += s_completion->allMatches( "$$" + s ); + + if ( !items.isEmpty() ) + { + if ( items.count() > beforeDollarCompletionCount ) + { + // remove the '$$whatever$' part + for( TQStringList::Iterator it = items.begin(); + it != items.end(); + ++it ) + { + int pos = (*it).find( '$', 2 ); + if( pos < 0 ) // ??? + continue; + (*it)=(*it).mid( pos + 1 ); + } + } + + items = removeMailDupes( items ); + + // We do not want KLineEdit::setCompletedItems to perform text + // completion (suggestion) since it does not know how to deal + // with providing proper completions for different items on the + // same line, e.g. comma-separated list of email addresses. + bool autoSuggest = (mode != TDEGlobalSettings::CompletionPopupAuto); + setCompletedItems( items, autoSuggest ); + + if (!autoSuggest) + { + int index = items.first().find( s ); + TQString newText = prevAddr + items.first().mid( index ); + //kdDebug() << "OLD TEXT: " << text() << endl; + //kdDebug() << "NEW TEXT: " << newText << endl; + setUserSelection(false); + setCompletedText(newText,true); + } + } + + break; + } + + case TDEGlobalSettings::CompletionShell: + { + TQString match = s_completion->makeCompletion( s ); + if ( !match.isNull() && match != s ) + { + setText( prevAddr + match ); + cursorAtEnd(); + } + break; + } + + case TDEGlobalSettings::CompletionMan: // Short-Auto in fact + case TDEGlobalSettings::CompletionAuto: + { + if (!s.isEmpty()) + { + TQString match = s_completion->makeCompletion( s ); + if ( !match.isNull() && match != s ) + { + TQString adds = prevAddr + match; + setCompletedText( adds ); + } + break; + } + } + case TDEGlobalSettings::CompletionNone: + default: // fall through + break; + } +} + +//----------------------------------------------------------------------------- +void AddressLineEdit::slotPopupCompletion( const TQString& completion ) +{ + setText( m_previousAddresses + completion ); + cursorAtEnd(); +} + +//----------------------------------------------------------------------------- +void AddressLineEdit::loadAddresses() +{ + s_completion->clear(); + s_addressesDirty = false; + + TQStringList adrs = addresses(); + for( TQStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it) + addAddress( *it ); +} + +void AddressLineEdit::addAddress( const TQString& adr ) +{ + s_completion->addItem( adr ); + int pos = adr.find( '<' ); + if( pos >= 0 ) + { + ++pos; + int pos2 = adr.find( pos, '>' ); + if( pos2 >= 0 ) + s_completion->addItem( adr.mid( pos, pos2 - pos )); + } +} + +void AddressLineEdit::slotStartLDAPLookup() +{ + if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this ) + return; + startLoadingLDAPEntries(); +} + +void AddressLineEdit::stopLDAPLookup() +{ + s_LDAPSearch->cancelSearch(); + s_LDAPLineEdit = NULL; +} + +void AddressLineEdit::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.length() == 0 ) + return; + + loadAddresses(); // TODO reuse these? + s_LDAPSearch->startSearch( s ); +} + +void AddressLineEdit::slotLDAPSearchData( const TQStringList& adrs ) +{ + if( s_LDAPLineEdit != this ) + return; + for( TQStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) { + TQString name(*it); + int pos = name.find( " <" ); + int pos_comma = name.find( ',' ); + // put name in quotes, if we have a comma in the name + if (pos>0 && pos_comma>0 && pos_comma<pos) { + name.insert(pos, '\"'); + name.prepend('\"'); + } + addAddress( name ); + } + + if( hasFocus() || completionBox()->hasFocus()) + { + if( completionMode() != TDEGlobalSettings::CompletionNone ) + { + doCompletion( false ); + } + } +} + +TQStringList AddressLineEdit::removeMailDupes( const TQStringList& adrs ) +{ + TQStringList src = adrs; + qHeapSort( src ); + TQString last; + for( TQStringList::Iterator it = src.begin(); it != src.end(); ) { + if( *it == last ) + { + it = src.remove( it ); + continue; // dupe + } + last = *it; + ++it; + } + return src; +} + +//----------------------------------------------------------------------------- +void AddressLineEdit::dropEvent(TQDropEvent *e) +{ + KURL::List uriList; + if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList )) + { + TQString ct = text(); + KURL::List::Iterator it = uriList.begin(); + for (; it != uriList.end(); ++it) + { + if (!ct.isEmpty()) ct.append(", "); + KURL u(*it); + if ((*it).protocol() == "mailto") + ct.append( (*it).path() ); + else + ct.append( (*it).url() ); + } + setText(ct); + setEdited( true ); + } + else { + if (m_useCompletion) + m_smartPaste = true; + TQLineEdit::dropEvent(e); + m_smartPaste = false; + } +} + + +TQStringList AddressLineEdit::addresses() +{ + TQApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while + + TQStringList result; + TQString space(" "); + TQRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]"); + TQString endQuote("\" "); + TQString addr, email; + + KABC::AddressBook *addressBook = KABC::StdAddressBook::self(); + KABC::AddressBook::Iterator it; + for( it = addressBook->begin(); it != addressBook->end(); ++it ) { + TQStringList emails = (*it).emails(); + + TQString n = (*it).prefix() + space + + (*it).givenName() + space + + (*it).additionalName() + space + + (*it).familyName() + space + + (*it).suffix(); + + n = n.simplifyWhiteSpace(); + + TQStringList::ConstIterator mit; + + for ( mit = emails.begin(); mit != emails.end(); ++mit ) { + email = *mit; + if (!email.isEmpty()) { + if (n.isEmpty() || (email.find( '<' ) != -1)) + addr = TQString::null; + else { /* do we really need quotes around this name ? */ + if (n.find(needQuotes) != -1) + addr = '"' + n + endQuote; + else + addr = n + space; + } + + if (!addr.isEmpty() && (email.find( '<' ) == -1) + && (email.find( '>' ) == -1) + && (email.find( ',' ) == -1)) + addr += '<' + email + '>'; + else + addr += email; + addr = addr.stripWhiteSpace(); + result.append( addr ); + } + } + } + + KABC::DistributionListManager manager( addressBook ); + manager.load(); + result += manager.listNames(); + + TQApplication::restoreOverrideCursor(); + + return result; +} + +#include "addresslineedit.moc" |