summaryrefslogtreecommitdiffstats
path: root/kabc/addresslineedit.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kabc/addresslineedit.cpp
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kabc/addresslineedit.cpp')
-rw-r--r--kabc/addresslineedit.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/kabc/addresslineedit.cpp b/kabc/addresslineedit.cpp
new file mode 100644
index 000000000..8e20c7975
--- /dev/null
+++ b/kabc/addresslineedit.cpp
@@ -0,0 +1,610 @@
+/*
+ This file is part of libkabc.
+ Copyright (c) 2002 Helge Deller <[email protected]>
+ 2002 Lubos Lunak <[email protected]>
+ 2001,2003 Carsten Pfeiffer <[email protected]>
+ 2001 Waldo Bastian <[email protected]>
+
+ 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 <qapplication.h>
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qregexp.h>
+#include <qevent.h>
+#include <qdragobject.h>
+
+#include <kcompletionbox.h>
+#include <kconfig.h>
+#include <kcursor.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <kstdaccel.h>
+#include <kurldrag.h>
+
+#include <kabc/stdaddressbook.h>
+#include <kabc/distributionlist.h>
+#include "ldapclient.h"
+
+#include <kdebug.h>
+
+//=============================================================================
+//
+// Class AddressLineEdit
+//
+//=============================================================================
+
+
+using namespace KABC;
+
+KCompletion * AddressLineEdit::s_completion = 0L;
+bool AddressLineEdit::s_addressesDirty = false;
+QTimer* AddressLineEdit::s_LDAPTimer = 0L;
+LdapSearch* AddressLineEdit::s_LDAPSearch = 0L;
+QString* AddressLineEdit::s_LDAPText = 0L;
+AddressLineEdit* AddressLineEdit::s_LDAPLineEdit = 0L;
+KConfig *AddressLineEdit::s_config = 0L;
+
+static KStaticDeleter<KCompletion> completionDeleter;
+static KStaticDeleter<QTimer> ldapTimerDeleter;
+static KStaticDeleter<LdapSearch> ldapSearchDeleter;
+static KStaticDeleter<QString> ldapTextDeleter;
+static KStaticDeleter<KConfig> configDeleter;
+
+AddressLineEdit::AddressLineEdit(QWidget* 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 KCompletion() );
+ s_completion->setOrder( KCompletion::Sorted );
+ s_completion->setIgnoreCase( true );
+ }
+
+ if( m_useCompletion ) {
+ if( !s_LDAPTimer ) {
+ ldapTimerDeleter.setObject( s_LDAPTimer, new QTimer );
+ ldapSearchDeleter.setObject( s_LDAPSearch, new LdapSearch );
+ ldapTextDeleter.setObject( s_LDAPText, new QString );
+ }
+ connect( s_LDAPTimer, SIGNAL( timeout()), SLOT( slotStartLDAPLookup()));
+ connect( s_LDAPSearch, SIGNAL( searchData( const QStringList& )),
+ SLOT( slotLDAPSearchData( const QStringList& )));
+ }
+
+ if ( m_useCompletion && !m_completionInitialized )
+ {
+ setCompletionObject( s_completion, false ); // we handle it ourself
+ connect( this, SIGNAL( completion(const QString&)),
+ this, SLOT(slotCompletion() ));
+
+ KCompletionBox *box = completionBox();
+ connect( box, SIGNAL( highlighted( const QString& )),
+ this, SLOT( slotPopupCompletion( const QString& ) ));
+ connect( box, SIGNAL( userCancelled( const QString& )),
+ SLOT( userCancelled( const QString& )));
+
+ 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()
+{
+}
+
+//-----------------------------------------------------------------------------
+
+KConfig* AddressLineEdit::config()
+{
+ if ( !s_config )
+ configDeleter.setObject( s_config, new KConfig( "kabldaprc", false, false ) ); // Open read-write, no kdeglobals
+
+ return s_config;
+}
+
+void AddressLineEdit::setFont( const QFont& font )
+{
+ KLineEdit::setFont( font );
+ if ( m_useCompletion )
+ completionBox()->setFont( font );
+}
+
+//-----------------------------------------------------------------------------
+void AddressLineEdit::keyPressEvent(QKeyEvent *e)
+{
+ bool accept = false;
+
+ if (KStdAccel::shortcut(KStdAccel::SubstringCompletion).contains(KKey(e)))
+ {
+ doCompletion(true);
+ accept = true;
+ }
+ else if (KStdAccel::shortcut(KStdAccel::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( QMouseEvent * e )
+{
+ if (m_useCompletion && (e->button() == MidButton))
+ {
+ m_smartPaste = true;
+ KLineEdit::mouseReleaseEvent(e);
+ m_smartPaste = false;
+ return;
+ }
+ KLineEdit::mouseReleaseEvent(e);
+}
+
+void AddressLineEdit::insert(const QString &t)
+{
+ if (!m_smartPaste)
+ {
+ KLineEdit::insert(t);
+ return;
+ }
+ QString newText = t.stripWhiteSpace();
+ if (newText.isEmpty())
+ return;
+
+ // remove newlines in the to-be-pasted string as well as an eventual
+ // mailto: protocol
+ newText.replace( QRegExp("\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( QRegExp("\\s*\\(at\\)\\s*"), "@" );
+ }
+
+ QString 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 = QString::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;
+
+ QString prevAddr;
+
+ QString 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 )
+ {
+ QStringList 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;
+ }
+
+ KGlobalSettings::Completion mode = completionMode();
+
+ switch ( mode )
+ {
+ case KGlobalSettings::CompletionPopupAuto:
+ {
+ if (s.isEmpty())
+ break;
+ }
+ case KGlobalSettings::CompletionPopup:
+ {
+ m_previousAddresses = prevAddr;
+ QStringList 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( QStringList::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 != KGlobalSettings::CompletionPopupAuto);
+ setCompletedItems( items, autoSuggest );
+
+ if (!autoSuggest)
+ {
+ int index = items.first().find( s );
+ QString newText = prevAddr + items.first().mid( index );
+ //kdDebug() << "OLD TEXT: " << text() << endl;
+ //kdDebug() << "NEW TEXT: " << newText << endl;
+ setUserSelection(false);
+ setCompletedText(newText,true);
+ }
+ }
+
+ break;
+ }
+
+ case KGlobalSettings::CompletionShell:
+ {
+ QString match = s_completion->makeCompletion( s );
+ if ( !match.isNull() && match != s )
+ {
+ setText( prevAddr + match );
+ cursorAtEnd();
+ }
+ break;
+ }
+
+ case KGlobalSettings::CompletionMan: // Short-Auto in fact
+ case KGlobalSettings::CompletionAuto:
+ {
+ if (!s.isEmpty())
+ {
+ QString match = s_completion->makeCompletion( s );
+ if ( !match.isNull() && match != s )
+ {
+ QString adds = prevAddr + match;
+ setCompletedText( adds );
+ }
+ break;
+ }
+ }
+ case KGlobalSettings::CompletionNone:
+ default: // fall through
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+void AddressLineEdit::slotPopupCompletion( const QString& completion )
+{
+ setText( m_previousAddresses + completion );
+ cursorAtEnd();
+}
+
+//-----------------------------------------------------------------------------
+void AddressLineEdit::loadAddresses()
+{
+ s_completion->clear();
+ s_addressesDirty = false;
+
+ QStringList adrs = addresses();
+ for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it)
+ addAddress( *it );
+}
+
+void AddressLineEdit::addAddress( const QString& 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()
+{
+ QString s( *s_LDAPText );
+ // TODO cache last?
+ QString 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 QStringList& adrs )
+{
+ if( s_LDAPLineEdit != this )
+ return;
+ for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) {
+ QString 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() != KGlobalSettings::CompletionNone )
+ {
+ doCompletion( false );
+ }
+ }
+}
+
+QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs )
+{
+ QStringList src = adrs;
+ qHeapSort( src );
+ QString last;
+ for( QStringList::Iterator it = src.begin(); it != src.end(); ) {
+ if( *it == last )
+ {
+ it = src.remove( it );
+ continue; // dupe
+ }
+ last = *it;
+ ++it;
+ }
+ return src;
+}
+
+//-----------------------------------------------------------------------------
+void AddressLineEdit::dropEvent(QDropEvent *e)
+{
+ KURL::List uriList;
+ if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ))
+ {
+ QString 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;
+ QLineEdit::dropEvent(e);
+ m_smartPaste = false;
+ }
+}
+
+
+QStringList AddressLineEdit::addresses()
+{
+ QApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while
+
+ QStringList result;
+ QString space(" ");
+ QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]");
+ QString endQuote("\" ");
+ QString addr, email;
+
+ KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
+ KABC::AddressBook::Iterator it;
+ for( it = addressBook->begin(); it != addressBook->end(); ++it ) {
+ QStringList emails = (*it).emails();
+
+ QString n = (*it).prefix() + space +
+ (*it).givenName() + space +
+ (*it).additionalName() + space +
+ (*it).familyName() + space +
+ (*it).suffix();
+
+ n = n.simplifyWhiteSpace();
+
+ QStringList::ConstIterator mit;
+
+ for ( mit = emails.begin(); mit != emails.end(); ++mit ) {
+ email = *mit;
+ if (!email.isEmpty()) {
+ if (n.isEmpty() || (email.find( '<' ) != -1))
+ addr = QString::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();
+
+ QApplication::restoreOverrideCursor();
+
+ return result;
+}
+
+#include "addresslineedit.moc"