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 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.6.2.5 2003/07/22 06:28:05 deller Exp $
00025 
00026 #include "addresslineedit.h"
00027 
00028 #include <qobject.h>
00029 #include <qptrlist.h>
00030 #include <qregexp.h>
00031 #include <qevent.h>
00032 #include <qdragobject.h>
00033 
00034 #include <kcompletionbox.h>
00035 #include <kstdaccel.h>
00036 #include <kurldrag.h>
00037 
00038 #include <kabc/stdaddressbook.h>
00039 #include <kabc/distributionlist.h>
00040 #include <kurldrag.h>
00041 #include "ldapclient.h"
00042 
00043 #include <kdebug.h>
00044 
00045 //=============================================================================
00046 //
00047 //   Class  AddressLineEdit
00048 //
00049 //=============================================================================
00050 
00051 
00052 using namespace KABC;
00053 
00054 KCompletion * AddressLineEdit::s_completion = 0L;
00055 bool AddressLineEdit::s_addressesDirty = false;
00056 QTimer* AddressLineEdit::s_LDAPTimer = 0L;
00057 LdapSearch* AddressLineEdit::s_LDAPSearch = 0L;
00058 QString* AddressLineEdit::s_LDAPText = 0L;
00059 AddressLineEdit* AddressLineEdit::s_LDAPLineEdit = 0L;
00060 
00061 AddressLineEdit::AddressLineEdit(QWidget* parent,
00062         bool useCompletion,
00063         const char *name)
00064     : KLineEdit(parent,name)
00065 {
00066   m_useCompletion = useCompletion;
00067   m_completionInitialized = false;
00068   m_smartPaste = false;
00069 
00070   init();
00071 
00072   // Whenever a new AddressLineEdit is created (== a new composer is created),
00073   // we set a dirty flag to reload the addresses upon the first completion.
00074   // The address completions are shared between all AddressLineEdits.
00075   // Is there a signal that tells us about addressbook updates?
00076   if (m_useCompletion)
00077     s_addressesDirty = true;
00078 }
00079 
00080 
00081 //-----------------------------------------------------------------------------
00082 void AddressLineEdit::init()
00083 {
00084   if ( !s_completion ) {
00085       s_completion = new KCompletion();
00086       s_completion->setOrder( KCompletion::Sorted );
00087       s_completion->setIgnoreCase( true );
00088   }
00089 
00090   if( m_useCompletion ) {
00091       if( !s_LDAPTimer ) {
00092         s_LDAPTimer = new QTimer;
00093         s_LDAPSearch = new LdapSearch;
00094         s_LDAPText = new QString;
00095       }
00096       connect( s_LDAPTimer, SIGNAL( timeout()), SLOT( slotStartLDAPLookup()));
00097       connect( s_LDAPSearch, SIGNAL( searchData( const QStringList& )),
00098         SLOT( slotLDAPSearchData( const QStringList& )));
00099   }
00100 
00101   if ( m_useCompletion && !m_completionInitialized )
00102   {
00103       setCompletionObject( s_completion, false ); // we handle it ourself
00104       connect( this, SIGNAL( completion(const QString&)),
00105                this, SLOT(slotCompletion() ));
00106 
00107       KCompletionBox *box = completionBox();
00108       connect( box, SIGNAL( highlighted( const QString& )),
00109                this, SLOT( slotPopupCompletion( const QString& ) ));
00110       connect( box, SIGNAL( userCancelled( const QString& )),
00111                SLOT( slotSetTextAsEdited( const QString& )));
00112 
00113       m_completionInitialized = true; // don't connect muliple times. That's
00114                                       // ugly, tho, better have completionBox()
00115                                       // virtual in KDE 4
00116       // Why? This is only called once. Why should this be called more
00117       // than once? And why was this protected?
00118       // And while I'm at it: who deletes all those static objects? (pfeiffer)
00119   }
00120 }
00121 
00122 //-----------------------------------------------------------------------------
00123 AddressLineEdit::~AddressLineEdit()
00124 {
00125 }
00126 
00127 //-----------------------------------------------------------------------------
00128 void AddressLineEdit::setFont( const QFont& font )
00129 {
00130     KLineEdit::setFont( font );
00131     if ( m_useCompletion )
00132         completionBox()->setFont( font );
00133 }
00134 
00135 //-----------------------------------------------------------------------------
00136 void AddressLineEdit::keyPressEvent(QKeyEvent *e)
00137 {
00138     bool accept = false;
00139 
00140     if (KStdAccel::shortcut(KStdAccel::SubstringCompletion).contains(KKey(e)))
00141     {
00142       doCompletion(true);
00143       accept = true;
00144     }
00145     else if (e->state()==ControlButton && e->key() == Key_Right)
00146     {
00147       if ((int)text().length() == cursorPosition()) // at End?
00148       {
00149         doCompletion(true);
00150     accept = true;
00151       }
00152     }
00153     else if (e->state()==ControlButton && e->key() == Key_V)
00154     {
00155       if (m_useCompletion)
00156          m_smartPaste = true;
00157       paste();
00158       m_smartPaste = false;
00159       accept = true;
00160     }
00161 
00162     if( !accept )
00163         KLineEdit::keyPressEvent( e );
00164 
00165     if( e->isAccepted())
00166     {
00167         if( m_useCompletion && s_LDAPTimer != NULL )
00168     {
00169             if( *s_LDAPText != text())
00170                 stopLDAPLookup();
00171         *s_LDAPText = text();
00172         s_LDAPLineEdit = this;
00173         s_LDAPTimer->start( 500, true );
00174     }
00175     }
00176 }
00177 
00178 void AddressLineEdit::mouseReleaseEvent( QMouseEvent * e )
00179 {
00180    if (m_useCompletion && (e->button() == MidButton))
00181    {
00182       m_smartPaste = true;
00183       KLineEdit::mouseReleaseEvent(e);
00184       m_smartPaste = false;
00185       return;
00186    }
00187    KLineEdit::mouseReleaseEvent(e);
00188 }
00189 
00190 void AddressLineEdit::insert(const QString &t)
00191 {
00192     if (!m_smartPaste)
00193     {
00194        KLineEdit::insert(t);
00195        return;
00196     }
00197     QString newText = t.stripWhiteSpace();
00198     if (newText.isEmpty())
00199        return;
00200 
00201     // remove newlines in the to-be-pasted string as well as an eventual
00202     // mailto: protocol
00203     newText.replace( QRegExp("\r?\n"), " " );
00204     if ( newText.startsWith( "mailto:" ) )
00205         newText.remove( 0, 7 );
00206     else if (newText.contains(" at "))
00207     {
00208        // Anti-spam stuff
00209        newText.replace( QRegExp(" at "), "@" );
       newText.replace( QRegExp(" dot "), "." );
00210     }
00211     else if (newText.contains("(at)"))
00212     {
00213       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 += ", ";
00214        pos = eot+2;
00215     }
00216 
00217     contents = contents.left(pos)+newText+contents.mid(pos);
00218     slotSetTextAsEdited(contents);
00219     setCursorPosition(pos+newText.length());
00220 }
00221 
00222 void AddressLineEdit::paste()
00223 {
00224     if (m_useCompletion)
00225        m_smartPaste = true;
00226     KLineEdit::paste();
00227     m_smartPaste = false;
00228 }
00229 
00230 //-----------------------------------------------------------------------------
00231 void AddressLineEdit::cursorAtEnd()
00232 {
00233     setCursorPosition( text().length() );
00234 }
00235 
00236 //-----------------------------------------------------------------------------
00237 void AddressLineEdit::enableCompletion(bool enable)
00238 {
00239   m_useCompletion = enable;
00240 }
00241 
00242 //-----------------------------------------------------------------------------
00243 void AddressLineEdit::doCompletion(bool ctrlT)
00244 {
00245     if ( !m_useCompletion )
00246         return;
00247 
00248     QString s(text());
00249     QString prevAddr;
00250     int n = s.findRev(',');
00251     if (n>= 0)
00252     {
00253         prevAddr = s.left(n+1) + ' ';
00254         s = s.mid(n+1,255).stripWhiteSpace();
00255     }
00256 
00257     KCompletionBox *box = completionBox();
00258 
00259     if ( s.isEmpty() )
00260     {
00261         box->hide();
00262         return;
00263     }
00264 
00265     KGlobalSettings::Completion  mode = completionMode();
00266 
00267     if ( s_addressesDirty )
00268         loadAddresses();
00269 
00270     if ( ctrlT )
00271     {
00272         QStringList completions = s_completion->substringCompletion( s );
00273         if (completions.count() > 1) {
00274             m_previousAddresses = prevAddr;
00275             box->setItems( completions );
00276             box->setCancelledText( text() );
00277             box->popup();
00278         }
00279         else if (completions.count() == 1)
00280             slotSetTextAsEdited(prevAddr + completions.first());
00281         else
00282             box->hide();
00283 
00284         cursorAtEnd();
00285         return;
00286     }
00287 
00288     switch ( mode )
00289     {
00290         case KGlobalSettings::CompletionPopup:
00291         {
00292                 m_previousAddresses = prevAddr;
00293         QStringList items = s_completion->allMatches( s );
00294                 items += s_completion->allMatches( "\"" + s );
00295         items += s_completion->substringCompletion( '<' + s );
00296             uint beforeDollarCompletionCount = items.count();
00297             
00298         if( !s.contains( ' ' )) // one word, possibly given name
00299             items += s_completion->allMatches( "$$" + s );
00300             
00301             if ( items.isEmpty() )
00302                 box->hide();
00303             else
00304             {
00305                 if ( items.count() > beforeDollarCompletionCount )
00306                 {
00307                     // remove the '$$whatever$' part                    
00308             for( QStringList::Iterator it = items.begin();
00309              it != items.end();
00310              ++it )
00311                     { 
00312             int pos = (*it).find( '$', 2 );
00313             if( pos < 0 ) // ???
00314                 continue;
00315             (*it)=(*it).mid( pos + 1 );
00316         }
00317                 }
00318                 
00319         items = removeMailDupes( items );
00320         box->setItems( items );
00321                 box->setCancelledText( text() );
00322                 box->popup();
00323             }
00324 
00325             break;
00326         }
00327 
00328         case KGlobalSettings::CompletionShell:
00329         {
00330             QString match = s_completion->makeCompletion( s );
00331             if ( !match.isNull() && match != s )
00332             {
00333                 slotSetTextAsEdited( prevAddr + match );
00334         cursorAtEnd();
00335             }
00336             break;
00337         }
00338 
00339         case KGlobalSettings::CompletionMan: // Short-Auto in fact
00340         case KGlobalSettings::CompletionAuto:
00341         {
00342             QString match = s_completion->makeCompletion( s );
00343             if ( !match.isNull() && match != s )
00344             {
00345                 QString adds = prevAddr + match;
00346                 int curPos = cursorPosition();
00347                 validateAndSet( adds, curPos, curPos, adds.length() );
00348             }
00349             break;
00350         }
00351 
00352         default: // fall through
00353         case KGlobalSettings::CompletionNone:
00354             break;
00355     }
00356 }
00357 
00358 //-----------------------------------------------------------------------------
00359 void AddressLineEdit::slotPopupCompletion( const QString& completion )
00360 {
00361     slotSetTextAsEdited( m_previousAddresses + completion );
00362     cursorAtEnd();
00363 }
00364 
00365 //-----------------------------------------------------------------------------
00366 void AddressLineEdit::loadAddresses()
00367 {
00368     s_completion->clear();
00369     s_addressesDirty = false;
00370 
00371     QStringList adrs = addresses();
00372     for( QStringList::ConstIterator it = adrs.begin();
00373      it != adrs.end();
00374      ++it)
00375         addAddress( *it );
00376 }
00377 
00378 void AddressLineEdit::addAddress( const QString& adr )
00379 {
00380     s_completion->addItem( adr );
00381     int pos = adr.find( '<' );
00382     if( pos >= 0 )
00383     {
00384         ++pos;
00385         int pos2 = adr.find( pos, '>' );
00386         if( pos2 >= 0 )
00387             s_completion->addItem( adr.mid( pos, pos2 - pos ));
00388     }
00389 }
00390 
00391 void AddressLineEdit::slotStartLDAPLookup()
00392 {
00393     if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this )
00394     return;
00395     startLoadingLDAPEntries();
00396 }
00397 
00398 void AddressLineEdit::stopLDAPLookup()
00399 {
00400     s_LDAPSearch->cancelSearch();
00401     s_LDAPLineEdit = NULL;
00402 }
00403 
00404 void AddressLineEdit::startLoadingLDAPEntries()
00405 {
00406     QString s( *s_LDAPText );
00407     // TODO cache last?
00408     QString prevAddr;
00409     int n = s.findRev(',');
00410     if (n>= 0)
00411     {
00412         prevAddr = s.left(n+1) + ' ';
00413         s = s.mid(n+1,255).stripWhiteSpace();
00414     }
00415     if( s.length() == 0 )
00416     return;
00417     loadAddresses(); // TODO reuse these?
00418     s_LDAPSearch->startSearch( s );
00419 }
00420 
00421 void AddressLineEdit::slotLDAPSearchData( const QStringList& adrs )
00422 {
00423     if( s_LDAPLineEdit != this )
00424         return;
00425     for( QStringList::ConstIterator it = adrs.begin();
00426      it != adrs.end();
00427      ++it ) {
00428     QString name(*it);
00429     int pos = name.find( " <" );
00430     int pos_comma = name.find( ',' );
00431     // put name in quotes, if we have a comma in the name
00432     if (pos>0 && pos_comma>0 && pos_comma<pos) {
00433         name.insert(pos, '\"');
00434         name.prepend('\"');
00435     }
00436     addAddress( name );
00437     }
00438     if( hasFocus() || completionBox()->hasFocus())
00439     {
00440         if( completionMode() != KGlobalSettings::CompletionNone )
00441         {
00442             doCompletion( false );
00443         }
00444     }
00445 }
00446 
00447 void AddressLineEdit::slotSetTextAsEdited( const QString& text )
00448 {
00449     setText( text );
00450     setEdited( true );
00451 }
00452 
00453 QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs )
00454 {
00455     QStringList src = adrs;
00456     qHeapSort( src );
00457     QString last;
00458     for( QStringList::Iterator it = src.begin();
00459      it != src.end();
00460      )
00461     {
00462     if( *it == last )
00463     {
00464         it = src.remove( it );
00465         continue; // dupe
00466     }
00467     last = *it;
00468     ++it;
00469     }
00470     return src;
00471 }
00472 
00473 //-----------------------------------------------------------------------------
00474 void AddressLineEdit::dropEvent(QDropEvent *e)
00475 {
00476   KURL::List uriList;
00477   if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ))
00478   {
00479     QString ct = text();
00480     KURL::List::Iterator it = uriList.begin();
00481     for (; it != uriList.end(); ++it)
00482     {
00483       if (!ct.isEmpty()) ct.append(", ");
00484       KURL u(*it);
00485       if ((*it).protocol() == "mailto")
00486           ct.append( (*it).path() );
00487       else
00488           ct.append( (*it).url() );
00489     }
00490     setText(ct);
00491     setEdited( true );
00492   }
00493   else {
00494     if (m_useCompletion)
00495        m_smartPaste = true;
00496     QLineEdit::dropEvent(e);
00497     m_smartPaste = false;
00498   }
00499 }
00500 
00501 
00502 QStringList AddressLineEdit::addresses()
00503 {
00504   QStringList result;
00505 
00506   KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
00507   KABC::AddressBook::Iterator it;
00508   for( it = addressBook->begin(); it != addressBook->end(); ++it ) {
00509     QStringList emails = (*it).emails();
00510     QString n = (*it).prefix() + " " +
00511         (*it).givenName() + " " +
00512         (*it).additionalName() + " " +
00513             (*it).familyName() + " " +
00514         (*it).suffix();
00515     n = n.simplifyWhiteSpace();
00516 
00517     QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]");
00518     QString endQuote("\" ");
00519     QString empty = "";
00520     QStringList::ConstIterator mit;
00521     QString addr, email;
00522 
00523     for ( mit = emails.begin(); mit != emails.end(); ++mit ) {
00524       email = *mit;
00525       if (!email.isEmpty()) {
00526     if (n.isEmpty() || (email.find( '<' ) != -1))
00527       addr = empty;
00528     else { /* do we really need quotes around this name ? */
00529           if (n.find(needQuotes) != -1)
00530         addr = '"' + n + endQuote;
00531       else
00532         addr = n + ' ';
00533     }
00534 
00535     if (!addr.isEmpty() && (email.find( '<' ) == -1)
00536         && (email.find( '>' ) == -1)
00537         && (email.find( ',' ) == -1))
00538       addr += '<' + email + '>';
00539     else
00540       addr += email;
00541     addr = addr.stripWhiteSpace();
00542     result.append( addr );
00543       }
00544     }
00545   }
00546   KABC::DistributionListManager manager( addressBook );
00547   manager.load();
00548 
00549   QStringList names = manager.listNames();
00550   QStringList::Iterator jt;
00551   for ( jt = names.begin(); jt != names.end(); ++jt)
00552     result.append( *jt );
00553   result.sort();
00554 
00555   return result;
00556 }
00557 
00558 #include "addresslineedit.moc"
00559 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.3.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Fri Feb 11 03:59:58 2005 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001