kabc Library API Documentation

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,v 1.18 2003/12/11 10:28:35 waba Exp $
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( slotSetTextAsEdited( 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( locateLocal( "config",
00143                              "kabldaprc" ) ) );
00144 
00145   return s_config;
00146 }
00147 
00148 void AddressLineEdit::setFont( const QFont& font )
00149 {
00150     KLineEdit::setFont( font );
00151     if ( m_useCompletion )
00152         completionBox()->setFont( font );
00153 }
00154 
00155 //-----------------------------------------------------------------------------
00156 void AddressLineEdit::keyPressEvent(QKeyEvent *e)
00157 {
00158     bool accept = false;
00159 
00160     if (KStdAccel::shortcut(KStdAccel::SubstringCompletion).contains(KKey(e)))
00161     {
00162       doCompletion(true);
00163       accept = true;
00164     }
00165     else if (e->state()==ControlButton && e->key() == Key_Right)
00166     {
00167       if ((int)text().length() == cursorPosition()) // at End?
00168       {
00169         doCompletion(true);
00170     accept = true;
00171       }
00172     }
00173     else if (e->state()==ControlButton && e->key() == Key_V)
00174     {
00175       if (m_useCompletion)
00176          m_smartPaste = true;
00177       paste();
00178       m_smartPaste = false;
00179       accept = true;
00180     }
00181 
00182     if( !accept )
00183         KLineEdit::keyPressEvent( e );
00184 
00185     if( e->isAccepted())
00186     {
00187         if( m_useCompletion && s_LDAPTimer != NULL )
00188     {
00189             if( *s_LDAPText != text())
00190                 stopLDAPLookup();
00191         *s_LDAPText = text();
00192         s_LDAPLineEdit = this;
00193         s_LDAPTimer->start( 500, true );
00194     }
00195     }
00196 }
00197 
00198 void AddressLineEdit::mouseReleaseEvent( QMouseEvent * e )
00199 {
00200    if (m_useCompletion && (e->button() == MidButton))
00201    {
00202       m_smartPaste = true;
00203       KLineEdit::mouseReleaseEvent(e);
00204       m_smartPaste = false;
00205       return;
00206    }
00207    KLineEdit::mouseReleaseEvent(e);
00208 }
00209 
00210 void AddressLineEdit::insert(const QString &t)
00211 {
00212     if (!m_smartPaste)
00213     {
00214        KLineEdit::insert(t);
00215        return;
00216     }
00217     QString newText = t.stripWhiteSpace();
00218     if (newText.isEmpty())
00219        return;
00220 
00221     // remove newlines in the to-be-pasted string as well as an eventual
00222     // mailto: protocol
00223     newText.replace( QRegExp("\r?\n"), ", " );
00224     if ( newText.startsWith( "mailto:" ) )
00225     {
00226       KURL u(newText);
00227       newText = u.path();
00228     }
00229     else if (newText.find(" at ") != -1)
00230     {
00231        // Anti-spam stuff
00232        newText.replace( " at ", "@" );
00233        newText.replace( " dot ", "." );
00234     }
00235     else if (newText.find("(at)") != -1)
00236     {
00237       newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" );
00238     }
00239 
00240     QString contents = text();
00241     int start_sel = 0;
00242     int end_sel = 0;
00243     int pos = cursorPosition();
00244     if (getSelection(&start_sel, &end_sel))
00245     {
00246        // Cut away the selection.
00247        if (pos > end_sel)
00248           pos -= (end_sel - start_sel);
00249        else if (pos > start_sel)
00250           pos = start_sel;
00251        contents = contents.left(start_sel) + contents.right(end_sel+1);
00252     }
00253 
00254     int eot = contents.length();
00255     while ((eot > 0) && contents[eot-1].isSpace()) eot--;
00256     if (eot == 0)
00257     {
00258        contents = QString::null;
00259     }
00260     else if (pos >= eot)
00261     {
00262        if (contents[eot-1] == ',')
00263           eot--;
00264        contents.truncate(eot);
00265        contents += ", ";
00266        pos = eot+2;
00267     }
00268 
00269     contents = contents.left(pos)+newText+contents.mid(pos);
00270     slotSetTextAsEdited(contents);
00271     setCursorPosition(pos+newText.length());
00272 }
00273 
00274 void AddressLineEdit::paste()
00275 {
00276     if (m_useCompletion)
00277        m_smartPaste = true;
00278     KLineEdit::paste();
00279     m_smartPaste = false;
00280 }
00281 
00282 //-----------------------------------------------------------------------------
00283 void AddressLineEdit::cursorAtEnd()
00284 {
00285     setCursorPosition( text().length() );
00286 }
00287 
00288 //-----------------------------------------------------------------------------
00289 void AddressLineEdit::enableCompletion(bool enable)
00290 {
00291   m_useCompletion = enable;
00292 }
00293 
00294 //-----------------------------------------------------------------------------
00295 void AddressLineEdit::doCompletion(bool ctrlT)
00296 {
00297     if ( !m_useCompletion )
00298         return;
00299 
00300     QString s(text());
00301     QString prevAddr;
00302     int n = s.findRev(',');
00303     if (n>= 0)
00304     {
00305         prevAddr = s.left(n+1) + ' ';
00306         s = s.mid(n+1,255).stripWhiteSpace();
00307     }
00308 
00309     KCompletionBox *box = completionBox();
00310 
00311     if ( s.isEmpty() )
00312     {
00313         box->hide();
00314         return;
00315     }
00316 
00317     KGlobalSettings::Completion  mode = completionMode();
00318 
00319     if ( s_addressesDirty )
00320         loadAddresses();
00321 
00322     if ( ctrlT )
00323     {
00324         QStringList completions = s_completion->substringCompletion( s );
00325         if (completions.count() > 1) {
00326             m_previousAddresses = prevAddr;
00327             box->setItems( completions );
00328             box->setCancelledText( text() );
00329             box->popup();
00330         }
00331         else if (completions.count() == 1)
00332             slotSetTextAsEdited(prevAddr + completions.first());
00333         else
00334             box->hide();
00335 
00336         cursorAtEnd();
00337         return;
00338     }
00339 
00340     switch ( mode )
00341     {
00342         case KGlobalSettings::CompletionPopup:
00343         {
00344             m_previousAddresses = prevAddr;
00345             QStringList items = s_completion->allMatches( s );
00346             items += s_completion->allMatches( "\"" + s );
00347             items += s_completion->substringCompletion( '<' + s );
00348             uint beforeDollarCompletionCount = items.count();
00349 
00350             if( s.find( ' ' ) == -1 ) // one word, possibly given name
00351                 items += s_completion->allMatches( "$$" + s );
00352 
00353             if ( items.isEmpty() )
00354                 box->hide();
00355             else
00356             {
00357                 if ( items.count() > beforeDollarCompletionCount )
00358                 {
00359                     // remove the '$$whatever$' part                    
00360                     for( QStringList::Iterator it = items.begin();
00361                          it != items.end();
00362                          ++it )
00363                     { 
00364                         int pos = (*it).find( '$', 2 );
00365                         if( pos < 0 ) // ???
00366                             continue;
00367                         (*it)=(*it).mid( pos + 1 );
00368                     }
00369                 }
00370 
00371                 items = removeMailDupes( items );
00372                 box->setItems( items );
00373                 box->setCancelledText( text() );
00374                 box->popup();
00375             }
00376 
00377             break;
00378         }
00379 
00380         case KGlobalSettings::CompletionShell:
00381         {
00382             QString match = s_completion->makeCompletion( s );
00383             if ( !match.isNull() && match != s )
00384             {
00385                 slotSetTextAsEdited( prevAddr + match );
00386                 cursorAtEnd();
00387             }
00388             break;
00389         }
00390 
00391         case KGlobalSettings::CompletionMan: // Short-Auto in fact
00392         case KGlobalSettings::CompletionAuto:
00393         {
00394             QString match = s_completion->makeCompletion( s );
00395             if ( !match.isNull() && match != s )
00396             {
00397                 QString adds = prevAddr + match;
00398                 int curPos = cursorPosition();
00399                 validateAndSet( adds, curPos, curPos, adds.length() );
00400             }
00401             break;
00402         }
00403 
00404         default: // fall through
00405         case KGlobalSettings::CompletionNone:
00406             break;
00407     }
00408 }
00409 
00410 //-----------------------------------------------------------------------------
00411 void AddressLineEdit::slotPopupCompletion( const QString& completion )
00412 {
00413     slotSetTextAsEdited( m_previousAddresses + completion );
00414     cursorAtEnd();
00415 }
00416 
00417 //-----------------------------------------------------------------------------
00418 void AddressLineEdit::loadAddresses()
00419 {
00420     s_completion->clear();
00421     s_addressesDirty = false;
00422 
00423     QStringList adrs = addresses();
00424     for( QStringList::ConstIterator it = adrs.begin();
00425      it != adrs.end();
00426      ++it)
00427         addAddress( *it );
00428 }
00429 
00430 void AddressLineEdit::addAddress( const QString& adr )
00431 {
00432     s_completion->addItem( adr );
00433     int pos = adr.find( '<' );
00434     if( pos >= 0 )
00435     {
00436         ++pos;
00437         int pos2 = adr.find( pos, '>' );
00438         if( pos2 >= 0 )
00439             s_completion->addItem( adr.mid( pos, pos2 - pos ));
00440     }
00441 }
00442 
00443 void AddressLineEdit::slotStartLDAPLookup()
00444 {
00445     if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this )
00446     return;
00447     startLoadingLDAPEntries();
00448 }
00449 
00450 void AddressLineEdit::stopLDAPLookup()
00451 {
00452     s_LDAPSearch->cancelSearch();
00453     s_LDAPLineEdit = NULL;
00454 }
00455 
00456 void AddressLineEdit::startLoadingLDAPEntries()
00457 {
00458     QString s( *s_LDAPText );
00459     // TODO cache last?
00460     QString prevAddr;
00461     int n = s.findRev(',');
00462     if (n>= 0)
00463     {
00464         prevAddr = s.left(n+1) + ' ';
00465         s = s.mid(n+1,255).stripWhiteSpace();
00466     }
00467     if( s.length() == 0 )
00468     return;
00469     loadAddresses(); // TODO reuse these?
00470     s_LDAPSearch->startSearch( s );
00471 }
00472 
00473 void AddressLineEdit::slotLDAPSearchData( const QStringList& adrs )
00474 {
00475     if( s_LDAPLineEdit != this )
00476         return;
00477     for( QStringList::ConstIterator it = adrs.begin();
00478      it != adrs.end();
00479      ++it ) {
00480     QString name(*it);
00481     int pos = name.find( " <" );
00482     int pos_comma = name.find( ',' );
00483     // put name in quotes, if we have a comma in the name
00484     if (pos>0 && pos_comma>0 && pos_comma<pos) {
00485         name.insert(pos, '\"');
00486         name.prepend('\"');
00487     }
00488     addAddress( name );
00489     }
00490     if( hasFocus() || completionBox()->hasFocus())
00491     {
00492         if( completionMode() != KGlobalSettings::CompletionNone )
00493         {
00494             doCompletion( false );
00495         }
00496     }
00497 }
00498 
00499 void AddressLineEdit::slotSetTextAsEdited( const QString& text )
00500 {
00501     setText( text );
00502     setEdited( true );
00503 }
00504 
00505 QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs )
00506 {
00507     QStringList src = adrs;
00508     qHeapSort( src );
00509     QString last;
00510     for( QStringList::Iterator it = src.begin();
00511      it != src.end();
00512      )
00513     {
00514     if( *it == last )
00515     {
00516         it = src.remove( it );
00517         continue; // dupe
00518     }
00519     last = *it;
00520     ++it;
00521     }
00522     return src;
00523 }
00524 
00525 //-----------------------------------------------------------------------------
00526 void AddressLineEdit::dropEvent(QDropEvent *e)
00527 {
00528   KURL::List uriList;
00529   if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ))
00530   {
00531     QString ct = text();
00532     KURL::List::Iterator it = uriList.begin();
00533     for (; it != uriList.end(); ++it)
00534     {
00535       if (!ct.isEmpty()) ct.append(", ");
00536       KURL u(*it);
00537       if ((*it).protocol() == "mailto")
00538           ct.append( (*it).path() );
00539       else
00540           ct.append( (*it).url() );
00541     }
00542     setText(ct);
00543     setEdited( true );
00544   }
00545   else {
00546     if (m_useCompletion)
00547        m_smartPaste = true;
00548     QLineEdit::dropEvent(e);
00549     m_smartPaste = false;
00550   }
00551 }
00552 
00553 
00554 QStringList AddressLineEdit::addresses()
00555 {
00556   QApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while
00557 
00558   QStringList result;
00559   QString space(" ");
00560   QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]");
00561   QString endQuote("\" ");
00562   QString addr, email;
00563 
00564   KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
00565   KABC::AddressBook::Iterator it;
00566   for( it = addressBook->begin(); it != addressBook->end(); ++it ) {
00567     QStringList emails = (*it).emails();
00568     QString n = (*it).prefix() + space +
00569         (*it).givenName() + space +
00570         (*it).additionalName() + space +
00571         (*it).familyName() + space +
00572         (*it).suffix();
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   KABC::DistributionListManager manager( addressBook );
00601   manager.load();
00602   result += manager.listNames();
00603 
00604   QApplication::restoreOverrideCursor();
00605 
00606   return result;
00607 }
00608 
00609 #include "addresslineedit.moc"
KDE Logo
This file is part of the documentation for kabc Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 30 05:21:03 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003