kabc Library: addresslineedit.cpp Source File (kabc Library)

| # kabc Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# addresslineedit.cpp

    00001 /*
    00002     This file is part of libkabc.
    00003     Copyright (c) 2002 Helge Deller <deller@gmx.de>
    00004                   2002 Lubos Lunak <llunak@suse.cz>
    00005                   2001,2003 Carsten Pfeiffer <pfeiffer@kde.org>
    00006                   2001 Waldo Bastian <bastian@kde.org>
    00007
    00008     This library is free software; you can redistribute it and/or
    00009     modify it under the terms of the GNU Library General Public
    00010     License as published by the Free Software Foundation; either
    00011     version 2 of the License, or (at your option) any later version.
    00012
    00013     This library is distributed in the hope that it will be useful,
    00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00016     Library General Public License for more details.
    00017
    00018     You should have received a copy of the GNU Library General Public License
    00019     along with this library; see the file COPYING.LIB.  If not, write to
    00020     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00021     Boston, MA 02111-1307, USA.
    00022 */
    00023
    00024 // $Id: addresslineedit.cpp 389974 2005-02-16 17:45:28Z waba $
    00025
    00026 #include "addresslineedit.h"
    00027
    00028 #include <qapplication.h>
    00029 #include <qobject.h>
    00030 #include <qptrlist.h>
    00031 #include <qregexp.h>
    00032 #include <qevent.h>
    00033 #include <qdragobject.h>
    00034
    00035 #include <kcompletionbox.h>
    00036 #include <kconfig.h>
    00037 #include <kcursor.h>
    00038 #include <kstandarddirs.h>
    00039 #include <kstaticdeleter.h>
    00040 #include <kstdaccel.h>
    00041 #include <kurldrag.h>
    00042
    00043 #include <kabc/stdaddressbook.h>
    00044 #include <kabc/distributionlist.h>
    00045 #include "ldapclient.h"
    00046
    00047 #include <kdebug.h>
    00048
    00049 //=============================================================================
    00050 //
    00051 //   Class  AddressLineEdit
    00052 //
    00053 //=============================================================================
    00054
    00055
    00056 using namespace KABC;
    00057
    00058 KCompletion * AddressLineEdit::s_completion = 0L;
    00059 bool AddressLineEdit::s_addressesDirty = false;
    00060 QTimer* AddressLineEdit::s_LDAPTimer = 0L;
    00061 LdapSearch* AddressLineEdit::s_LDAPSearch = 0L;
    00062 QString* AddressLineEdit::s_LDAPText = 0L;
    00063 AddressLineEdit* AddressLineEdit::s_LDAPLineEdit = 0L;
    00064 KConfig *AddressLineEdit::s_config = 0L;
    00065
    00066 static KStaticDeleter<KCompletion> completionDeleter;
    00067 static KStaticDeleter<QTimer> ldapTimerDeleter;
    00068 static KStaticDeleter<LdapSearch> ldapSearchDeleter;
    00069 static KStaticDeleter<QString> ldapTextDeleter;
    00070 static KStaticDeleter<KConfig> configDeleter;
    00071
    00072 AddressLineEdit::AddressLineEdit(QWidget* parent,
    00073         bool useCompletion,
    00074         const char *name)
    00075     : KLineEdit(parent,name)
    00076 {
    00077   m_useCompletion = useCompletion;
    00078   m_completionInitialized = false;
    00079   m_smartPaste = false;
    00080
    00081   init();
    00082
    00083   // Whenever a new AddressLineEdit is created (== a new composer is created),
    00084   // we set a dirty flag to reload the addresses upon the first completion.
    00085   // The address completions are shared between all AddressLineEdits.
    00086   // Is there a signal that tells us about addressbook updates?
    00087   if (m_useCompletion)
    00088     s_addressesDirty = true;
    00089 }
    00090
    00091
    00092 //-----------------------------------------------------------------------------
    00093 void AddressLineEdit::init()
    00094 {
    00095   if ( !s_completion ) {
    00096       completionDeleter.setObject( s_completion, new KCompletion() );
    00097       s_completion->setOrder( KCompletion::Sorted );
    00098       s_completion->setIgnoreCase( true );
    00099   }
    00100
    00101   if( m_useCompletion ) {
    00102       if( !s_LDAPTimer ) {
    00103         ldapTimerDeleter.setObject( s_LDAPTimer, new QTimer );
    00104         ldapSearchDeleter.setObject( s_LDAPSearch, new LdapSearch );
    00105         ldapTextDeleter.setObject( s_LDAPText, new QString );
    00106       }
    00107       connect( s_LDAPTimer, SIGNAL( timeout()), SLOT( slotStartLDAPLookup()));
    00108       connect( s_LDAPSearch, SIGNAL( searchData( const QStringList& )),
    00109         SLOT( slotLDAPSearchData( const QStringList& )));
    00110   }
    00111
    00112   if ( m_useCompletion && !m_completionInitialized )
    00113   {
    00114       setCompletionObject( s_completion, false ); // we handle it ourself
    00115       connect( this, SIGNAL( completion(const QString&)),
    00116                this, SLOT(slotCompletion() ));
    00117
    00118       KCompletionBox *box = completionBox();
    00119       connect( box, SIGNAL( highlighted( const QString& )),
    00120                this, SLOT( slotPopupCompletion( const QString& ) ));
    00121       connect( box, SIGNAL( userCancelled( const QString& )),
    00122                SLOT( userCancelled( const QString& )));
    00123
    00124       m_completionInitialized = true; // don't connect muliple times. That's
    00125                                       // ugly, tho, better have completionBox()
    00126                                       // virtual in KDE 4
    00127       // Why? This is only called once. Why should this be called more
    00128       // than once? And why was this protected?
    00129   }
    00130 }
    00131
    00132 //-----------------------------------------------------------------------------
    00133 AddressLineEdit::~AddressLineEdit()
    00134 {
    00135 }
    00136
    00137 //-----------------------------------------------------------------------------
    00138
    00139 KConfig* AddressLineEdit::config()
    00140 {
    00141   if ( !s_config )
    00142     configDeleter.setObject( s_config, new KConfig( "kabldaprc", false, false ) ); // Open read-write, no kdeglobals
    00143
    00144   return s_config;
    00145 }
    00146
    00147 void AddressLineEdit::setFont( const QFont& font )
    00148 {
    00149     KLineEdit::setFont( font );
    00150     if ( m_useCompletion )
    00151         completionBox()->setFont( font );
    00152 }
    00153
    00154 //-----------------------------------------------------------------------------
    00155 void AddressLineEdit::keyPressEvent(QKeyEvent *e)
    00156 {
    00157     bool accept = false;
    00158
    00159     if (KStdAccel::shortcut(KStdAccel::SubstringCompletion).contains(KKey(e)))
    00160     {
    00161         doCompletion(true);
    00162         accept = true;
    00163     }
    00164     else if (KStdAccel::shortcut(KStdAccel::TextCompletion).contains(KKey(e)))
    00165     {
    00166         int len = text().length();
    00167
    00168         if (len == cursorPosition()) // at End?
    00169         {
    00170             doCompletion(true);
    00171             accept = true;
    00172         }
    00173     }
    00174
    00175     if( !accept )
    00176         KLineEdit::keyPressEvent( e );
    00177
    00178     if( e->isAccepted())
    00179     {
    00180         if( m_useCompletion && s_LDAPTimer != NULL )
    00181         {
    00182             if( *s_LDAPText != text())
    00183                 stopLDAPLookup();
    00184             *s_LDAPText = text();
    00185             s_LDAPLineEdit = this;
    00186             s_LDAPTimer->start( 500, true );
    00187         }
    00188     }
    00189 }
    00190
    00191 void AddressLineEdit::mouseReleaseEvent( QMouseEvent * e )
    00192 {
    00193    if (m_useCompletion && (e->button() == MidButton))
    00194    {
    00195       m_smartPaste = true;
    00196       KLineEdit::mouseReleaseEvent(e);
    00197       m_smartPaste = false;
    00198       return;
    00199    }
    00200    KLineEdit::mouseReleaseEvent(e);
    00201 }
    00202
    00203 void AddressLineEdit::insert(const QString &t)
    00204 {
    00205     if (!m_smartPaste)
    00206     {
    00207        KLineEdit::insert(t);
    00208        return;
    00209     }
    00210     QString newText = t.stripWhiteSpace();
    00211     if (newText.isEmpty())
    00212        return;
    00213
    00214     // remove newlines in the to-be-pasted string as well as an eventual
    00215     // mailto: protocol
    00216     newText.replace( QRegExp("\r?\n"), ", " );
    00217     if ( newText.startsWith( "mailto:" ) )
    00218     {
    00219       KURL u(newText);
    00220       newText = u.path();
    00221     }
    00222     else if (newText.find(" at ") != -1)
    00223     {
    00224        // Anti-spam stuff
    00225        newText.replace( " at ", "@" );
    00226        newText.replace( " dot ", "." );
    00227     }
    00228     else if (newText.find("(at)") != -1)
    00229     {
    00230       newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" );
    00231     }
    00232
    00233     QString contents = text();
    00234     int start_sel = 0;
    00235     int end_sel = 0;
    00236     int pos = cursorPosition();
    00237     if (getSelection(&start_sel, &end_sel))
    00238     {
    00239        // Cut away the selection.
    00240        if (pos > end_sel)
    00241           pos -= (end_sel - start_sel);
    00242        else if (pos > start_sel)
    00243           pos = start_sel;
    00244        contents = contents.left(start_sel) + contents.right(end_sel+1);
    00245     }
    00246
    00247     int eot = contents.length();
    00248     while ((eot > 0) && contents[eot-1].isSpace()) eot--;
    00249     if (eot == 0)
    00250     {
    00251        contents = QString::null;
    00252     }
    00253     else if (pos >= eot)
    00254     {
    00255        if (contents[eot-1] == ',')
    00256           eot--;
    00257        contents.truncate(eot);
    00258        contents += ", ";
    00259        pos = eot+2;
    00260     }
    00261
    00262     contents = contents.left(pos)+newText+contents.mid(pos);
    00263     setText(contents);
    00264     setCursorPosition(pos+newText.length());
    00265 }
    00266
    00267 void AddressLineEdit::paste()
    00268 {
    00269     if (m_useCompletion)
    00270        m_smartPaste = true;
    00271     KLineEdit::paste();
    00272     m_smartPaste = false;
    00273 }
    00274
    00275 //-----------------------------------------------------------------------------
    00276 void AddressLineEdit::cursorAtEnd()
    00277 {
    00278     setCursorPosition( text().length() );
    00279 }
    00280
    00281 //-----------------------------------------------------------------------------
    00282 void AddressLineEdit::enableCompletion(bool enable)
    00283 {
    00284   m_useCompletion = enable;
    00285 }
    00286
    00287 //-----------------------------------------------------------------------------
    00288 void AddressLineEdit::doCompletion(bool ctrlT)
    00289 {
    00290     if ( !m_useCompletion )
    00291         return;
    00292
    00293     QString prevAddr;
    00294
    00295     QString s(text());
    00296     int n = s.findRev(',');
    00297
    00298     if (n >= 0)
    00299     {
    00300         n++; // Go past the ","
    00301
    00302         int len = s.length();
    00303
    00304         // Increment past any whitespace...
    00305         while( n < len && s[n].isSpace() )
    00306           n++;
    00307
    00308         prevAddr = s.left(n);
    00309         s = s.mid(n,255).stripWhiteSpace();
    00310     }
    00311
    00312     if ( s_addressesDirty )
    00313         loadAddresses();
    00314
    00315     if ( ctrlT )
    00316     {
    00317         QStringList completions = s_completion->substringCompletion( s );
    00318         if (completions.count() > 1) {
    00319             m_previousAddresses = prevAddr;
    00320             setCompletedItems( completions );
    00321         }
    00322         else if (completions.count() == 1)
    00323             setText(prevAddr + completions.first());
    00324
    00325         cursorAtEnd();
    00326         return;
    00327     }
    00328
    00329     KGlobalSettings::Completion  mode = completionMode();
    00330
    00331     switch ( mode )
    00332     {
    00333         case KGlobalSettings::CompletionPopupAuto:
    00334         {
    00335             if (s.isEmpty())
    00336                 break;
    00337         }
    00338         case KGlobalSettings::CompletionPopup:
    00339         {
    00340             m_previousAddresses = prevAddr;
    00341             QStringList items = s_completion->allMatches( s );
    00342             items += s_completion->allMatches( "\"" + s );
    00343             items += s_completion->substringCompletion( '<' + s );
    00344             uint beforeDollarCompletionCount = items.count();
    00345
    00346             if( s.find( ' ' ) == -1 ) // one word, possibly given name
    00347                 items += s_completion->allMatches( "$$" + s );
    00348
    00349             if ( !items.isEmpty() )
    00350             {
    00351                 if ( items.count() > beforeDollarCompletionCount )
    00352                 {
    00353                     // remove the '$$whatever$' part
    00354                     for( QStringList::Iterator it = items.begin();
    00355                          it != items.end();
    00356                          ++it )
    00357                     {
    00358                         int pos = (*it).find( '$', 2 );
    00359                         if( pos < 0 ) // ???
    00360                             continue;
    00361                         (*it)=(*it).mid( pos + 1 );
    00362                     }
    00363                 }
    00364
    00365                 items = removeMailDupes( items );
    00366
    00367                 // We do not want KLineEdit::setCompletedItems to perform text
    00368                 // completion (suggestion) since it does not know how to deal
    00369                 // with providing proper completions for different items on the
    00370                 // same line, e.g. comma-separated list of email addresses.
    00371                 bool autoSuggest = (mode != KGlobalSettings::CompletionPopupAuto);
    00372                 setCompletedItems( items, autoSuggest );
    00373
    00374                 if (!autoSuggest)
    00375                 {
    00376                     int index = items.first().find( s );
    00377                     QString newText = prevAddr + items.first().mid( index );
    00378                     //kdDebug() << "OLD TEXT: " << text() << endl;
    00379                     //kdDebug() << "NEW TEXT: " << newText << endl;
    00380                     setUserSelection(false);
    00381                     setCompletedText(newText,true);
    00382                 }
    00383             }
    00384
    00385             break;
    00386         }
    00387
    00388         case KGlobalSettings::CompletionShell:
    00389         {
    00390             QString match = s_completion->makeCompletion( s );
    00391             if ( !match.isNull() && match != s )
    00392             {
    00393                 setText( prevAddr + match );
    00394                 cursorAtEnd();
    00395             }
    00396             break;
    00397         }
    00398
    00399         case KGlobalSettings::CompletionMan: // Short-Auto in fact
    00400         case KGlobalSettings::CompletionAuto:
    00401         {
    00402             if (!s.isEmpty())
    00403             {
    00404                 QString match = s_completion->makeCompletion( s );
    00405                 if ( !match.isNull() && match != s )
    00406                 {
    00407                   QString adds = prevAddr + match;
    00408                   setCompletedText( adds );
    00409                 }
    00410                 break;
    00411             }
    00412         }
    00413         case KGlobalSettings::CompletionNone:
    00414         default: // fall through
    00415             break;
    00416     }
    00417 }
    00418
    00419 //-----------------------------------------------------------------------------
    00420 void AddressLineEdit::slotPopupCompletion( const QString& completion )
    00421 {
    00422     setText( m_previousAddresses + completion );
    00423     cursorAtEnd();
    00424 }
    00425
    00426 //-----------------------------------------------------------------------------
    00427 void AddressLineEdit::loadAddresses()
    00428 {
    00429     s_completion->clear();
    00430     s_addressesDirty = false;
    00431
    00432     QStringList adrs = addresses();
    00433     for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it)
    00434         addAddress( *it );
    00435 }
    00436
    00437 void AddressLineEdit::addAddress( const QString& adr )
    00438 {
    00439     s_completion->addItem( adr );
    00440     int pos = adr.find( '<' );
    00441     if( pos >= 0 )
    00442     {
    00443         ++pos;
    00444         int pos2 = adr.find( pos, '>' );
    00445         if( pos2 >= 0 )
    00446             s_completion->addItem( adr.mid( pos, pos2 - pos ));
    00447     }
    00448 }
    00449
    00450 void AddressLineEdit::slotStartLDAPLookup()
    00451 {
    00452     if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this )
    00453         return;
    00454     startLoadingLDAPEntries();
    00455 }
    00456
    00457 void AddressLineEdit::stopLDAPLookup()
    00458 {
    00459     s_LDAPSearch->cancelSearch();
    00460     s_LDAPLineEdit = NULL;
    00461 }
    00462
    00463 void AddressLineEdit::startLoadingLDAPEntries()
    00464 {
    00465     QString s( *s_LDAPText );
    00466     // TODO cache last?
    00467     QString prevAddr;
    00468     int n = s.findRev(',');
    00469     if (n>= 0)
    00470     {
    00471         prevAddr = s.left(n+1) + ' ';
    00472         s = s.mid(n+1,255).stripWhiteSpace();
    00473     }
    00474     if( s.length() == 0 )
    00475         return;
    00476
    00477     loadAddresses(); // TODO reuse these?
    00478     s_LDAPSearch->startSearch( s );
    00479 }
    00480
    00481 void AddressLineEdit::slotLDAPSearchData( const QStringList& adrs )
    00482 {
    00483     if( s_LDAPLineEdit != this )
    00484         return;
    00485     for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) {
    00486         QString name(*it);
    00487         int pos = name.find( " <" );
    00488         int pos_comma = name.find( ',' );
    00489         // put name in quotes, if we have a comma in the name
    00490         if (pos>0 && pos_comma>0 && pos_comma<pos) {
    00491           name.insert(pos, '\"');
    00492           name.prepend('\"');
    00493         }
    00494         addAddress( name );
    00495     }
    00496
    00497     if( hasFocus() || completionBox()->hasFocus())
    00498     {
    00499         if( completionMode() != KGlobalSettings::CompletionNone )
    00500         {
    00501             doCompletion( false );
    00502         }
    00503     }
    00504 }
    00505
    00506 QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs )
    00507 {
    00508     QStringList src = adrs;
    00509     qHeapSort( src );
    00510     QString last;
    00511     for( QStringList::Iterator it = src.begin(); it != src.end(); ) {
    00512         if( *it == last )
    00513         {
    00514             it = src.remove( it );
    00515             continue; // dupe
    00516         }
    00517         last = *it;
    00518         ++it;
    00519     }
    00520     return src;
    00521 }
    00522
    00523 //-----------------------------------------------------------------------------
    00524 void AddressLineEdit::dropEvent(QDropEvent *e)
    00525 {
    00526   KURL::List uriList;
    00527   if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ))
    00528   {
    00529     QString ct = text();
    00530     KURL::List::Iterator it = uriList.begin();
    00531     for (; it != uriList.end(); ++it)
    00532     {
    00533       if (!ct.isEmpty()) ct.append(", ");
    00534       KURL u(*it);
    00535       if ((*it).protocol() == "mailto")
    00536           ct.append( (*it).path() );
    00537       else
    00538           ct.append( (*it).url() );
    00539     }
    00540     setText(ct);
    00541     setEdited( true );
    00542   }
    00543   else {
    00544     if (m_useCompletion)
    00545        m_smartPaste = true;
    00546     QLineEdit::dropEvent(e);
    00547     m_smartPaste = false;
    00548   }
    00549 }
    00550
    00551
    00552 QStringList AddressLineEdit::addresses()
    00553 {
    00554   QApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while
    00555
    00556   QStringList result;
    00557   QString space(" ");
    00558   QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]");
    00559   QString endQuote("\" ");
    00560   QString addr, email;
    00561
    00562   KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
    00563   KABC::AddressBook::Iterator it;
    00564   for( it = addressBook->begin(); it != addressBook->end(); ++it ) {
    00565     QStringList emails = (*it).emails();
    00566
    00567     QString n = (*it).prefix() + space +
    00568                 (*it).givenName() + space +
    00569                 (*it).additionalName() + space +
    00570                 (*it).familyName() + space +
    00571                 (*it).suffix();
    00572
    00573     n = n.simplifyWhiteSpace();
    00574
    00575     QStringList::ConstIterator mit;
    00576
    00577     for ( mit = emails.begin(); mit != emails.end(); ++mit ) {
    00578       email = *mit;
    00579       if (!email.isEmpty()) {
    00580         if (n.isEmpty() || (email.find( '<' ) != -1))
    00581           addr = QString::null;
    00582         else { /* do we really need quotes around this name ? */
    00583                 if (n.find(needQuotes) != -1)
    00584             addr = '"' + n + endQuote;
    00585           else
    00586             addr = n + space;
    00587         }
    00588
    00589         if (!addr.isEmpty() && (email.find( '<' ) == -1)
    00590             && (email.find( '>' ) == -1)
    00591             && (email.find( ',' ) == -1))
    00592           addr += '<' + email + '>';
    00593         else
    00594           addr += email;
    00595         addr = addr.stripWhiteSpace();
    00596         result.append( addr );
    00597       }
    00598     }
    00599   }
    00600
    00601   KABC::DistributionListManager manager( addressBook );
    00602   manager.load();
    00603   result += manager.listNames();
    00604
    00605   QApplication::restoreOverrideCursor();
    00606
    00607   return result;
    00608 }
    00609
    00610 #include "addresslineedit.moc"

This file is part of the documentation for kabc Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:31:29 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003