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.2 2002/11/28 15:00:52 lunakl 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     QString match;
00271     int curPos = cursorPosition();
00272     if ( mode != KGlobalSettings::CompletionNone )
00273     {
00274         match = s_completion->makeCompletion( s );
00275         if (match.isNull() && mode == KGlobalSettings::CompletionPopup)
00276           match = s_completion->makeCompletion( "\"" + s );
00277         if (match.isNull() && mode == KGlobalSettings::CompletionPopup)
00278           match = s_completion->makeCompletion( "$$" + s );
00279     }
00280 
00281     kdDebug() << "** completion for: " << s << " : " << match << endl;
00282 
00283     if ( ctrlT )
00284     {
00285         QStringList addresses = s_completion->items();
00286         QStringList::Iterator it = addresses.begin();
00287         QStringList completions;
00288         for (; it != addresses.end(); ++it)
00289         {
00290             if ((*it).find(s,0,false) >= 0)
00291                 completions.append( *it );
00292         }
00293 
00294         if (completions.count() > 1) {
00295             m_previousAddresses = prevAddr;
00296             box->setItems( completions );
00297             box->setCancelledText( text() );
00298             box->popup();
00299         }
00300         else if (completions.count() == 1)
00301             slotSetTextAsEdited(prevAddr + completions.first());
00302         else
00303             box->hide();
00304 
00305         cursorAtEnd();
00306         return;
00307     }
00308 
00309     switch ( mode )
00310     {
00311         case KGlobalSettings::CompletionPopup:
00312         {
00313             if ( !match.isNull() )
00314             {
00315                 m_previousAddresses = prevAddr;
00316         QStringList items = s_completion->allMatches( s );
00317                 items += s_completion->allMatches( "\"" + s );
00318         items += s_completion->substringCompletion( '<' + s );
00319         if( !s.contains( ' ' )) // one word, possibly given name
00320             items += s_completion->allMatches( "$$" + s );
00321             for( QStringList::Iterator it = items.begin();
00322              it != items.end();
00323              ++it )
00324         { // remove the '$$whatever$' part
00325             int pos = (*it).find( '$', 2 );
00326             if( pos < 0 ) // ???
00327                 continue;
00328             (*it)=(*it).mid( pos + 1 );
00329         }
00330         items = removeMailDupes( items );
00331         box->setItems( items );
00332                 box->setCancelledText( text() );
00333                 box->popup();
00334             }
00335             else
00336                 box->hide();
00337 
00338             break;
00339         }
00340 
00341         case KGlobalSettings::CompletionShell:
00342         {
00343             if ( !match.isNull() && match != s )
00344             {
00345                 slotSetTextAsEdited( prevAddr + match );
00346         cursorAtEnd();
00347             }
00348             break;
00349         }
00350 
00351         case KGlobalSettings::CompletionMan: // Short-Auto in fact
00352         case KGlobalSettings::CompletionAuto:
00353         {
00354             if ( !match.isNull() && match != s )
00355             {
00356                 QString adds = prevAddr + match;
00357                 validateAndSet( adds, curPos, curPos, adds.length() );
00358             }
00359             break;
00360         }
00361 
00362         default: // fall through
00363         case KGlobalSettings::CompletionNone:
00364             break;
00365     }
00366 }
00367 
00368 //-----------------------------------------------------------------------------
00369 void AddressLineEdit::slotPopupCompletion( const QString& completion )
00370 {
00371     slotSetTextAsEdited( m_previousAddresses + completion );
00372     cursorAtEnd();
00373 }
00374 
00375 //-----------------------------------------------------------------------------
00376 void AddressLineEdit::loadAddresses()
00377 {
00378     s_completion->clear();
00379     s_addressesDirty = false;
00380 
00381     QStringList adrs = addresses();
00382     for( QStringList::ConstIterator it = adrs.begin();
00383      it != adrs.end();
00384      ++it)
00385         addAddress( *it );
00386 }
00387 
00388 void AddressLineEdit::addAddress( const QString& adr )
00389 {
00390     s_completion->addItem( adr );
00391     int pos = adr.find( '<' );
00392     if( pos >= 0 )
00393     {
00394         ++pos;
00395         int pos2 = adr.find( pos, '>' );
00396         if( pos2 >= 0 )
00397             s_completion->addItem( adr.mid( pos, pos2 - pos ));
00398     }
00399 }
00400 
00401 void AddressLineEdit::slotStartLDAPLookup()
00402 {
00403     if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this )
00404     return;
00405     startLoadingLDAPEntries();
00406 }
00407 
00408 void AddressLineEdit::stopLDAPLookup()
00409 {
00410     s_LDAPSearch->cancelSearch();
00411     s_LDAPLineEdit = NULL;
00412 }
00413 
00414 void AddressLineEdit::startLoadingLDAPEntries()
00415 {
00416     QString s( *s_LDAPText );
00417     // TODO cache last?
00418     QString prevAddr;
00419     int n = s.findRev(',');
00420     if (n>= 0)
00421     {
00422         prevAddr = s.left(n+1) + ' ';
00423         s = s.mid(n+1,255).stripWhiteSpace();
00424     }
00425     if( s.length() == 0 )
00426     return;
00427     loadAddresses(); // TODO reuse these?
00428     s_LDAPSearch->startSearch( s );
00429 }
00430 
00431 void AddressLineEdit::slotLDAPSearchData( const QStringList& adrs )
00432 {
00433     if( s_LDAPLineEdit != this )
00434         return;
00435     for( QStringList::ConstIterator it = adrs.begin();
00436      it != adrs.end();
00437      ++it )
00438     addAddress( *it );
00439     if( hasFocus() || completionBox()->hasFocus())
00440     {
00441         // avoid ShellCompletion too, as it would set the first
00442         // match as the edited text, not only offer it
00443         if( completionMode() != KGlobalSettings::CompletionShell
00444             && completionMode() != KGlobalSettings::CompletionNone )
00445         {
00446             doCompletion( false );
00447         }
00448     }
00449 }
00450 
00451 void AddressLineEdit::slotSetTextAsEdited( const QString& text )
00452 {
00453     setText( text );
00454     setEdited( true );
00455 }
00456 
00457 QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs )
00458 {
00459     QStringList src = adrs;
00460     qHeapSort( src );
00461     QString last;
00462     for( QStringList::Iterator it = src.begin();
00463      it != src.end();
00464      )
00465     {
00466     if( *it == last )
00467     {
00468         it = src.remove( it );
00469         continue; // dupe
00470     }
00471     last = *it;
00472     ++it;
00473     }
00474     return src;
00475 }
00476 
00477 //-----------------------------------------------------------------------------
00478 void AddressLineEdit::dropEvent(QDropEvent *e)
00479 {
00480   KURL::List uriList;
00481   if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ))
00482   {
00483     QString ct = text();
00484     KURL::List::Iterator it = uriList.begin();
00485     for (; it != uriList.end(); ++it)
00486     {
00487       if (!ct.isEmpty()) ct.append(", ");
00488       KURL u(*it);
00489       if ((*it).protocol() == "mailto")
00490           ct.append( (*it).path() );
00491       else
00492           ct.append( (*it).url() );
00493     }
00494     setText(ct);
00495     setEdited( true );
00496   }
00497   else {
00498     if (m_useCompletion)
00499        m_smartPaste = true;
00500     QLineEdit::dropEvent(e);
00501     m_smartPaste = false;
00502   }
00503 }
00504 
00505 
00506 QStringList AddressLineEdit::addresses()
00507 {
00508   QStringList result;
00509 
00510   KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
00511   KABC::AddressBook::Iterator it;
00512   for( it = addressBook->begin(); it != addressBook->end(); ++it ) {
00513     QStringList emails = (*it).emails();
00514     QString n = (*it).prefix() + " " +
00515         (*it).givenName() + " " +
00516         (*it).additionalName() + " " +
00517             (*it).familyName() + " " +
00518         (*it).suffix();
00519     n = n.simplifyWhiteSpace();
00520 
00521     QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]");
00522     QString endQuote = "\" ";
00523     QString empty = "";
00524     QStringList::ConstIterator mit;
00525     QString addr, email;
00526 
00527     for ( mit = emails.begin(); mit != emails.end(); ++mit ) {
00528       email = *mit;
00529       if (!email.isEmpty()) {
00530     if (n.isEmpty() || (email.find( '<' ) != -1))
00531       addr = empty;
00532     else { /* do we really need quotes around this name ? */
00533           if (n.find(needQuotes) != -1)
00534         addr = '"' + n + endQuote;
00535       else
00536         addr = n + ' ';
00537     }
00538 
00539     if (!addr.isEmpty() && (email.find( '<' ) == -1)
00540         && (email.find( '>' ) == -1)
00541         && (email.find( ',' ) == -1))
00542       addr += '<' + email + '>';
00543     else
00544       addr += email;
00545     addr = addr.stripWhiteSpace();
00546     result.append( addr );
00547       }
00548     }
00549   }
00550   KABC::DistributionListManager manager( addressBook );
00551   manager.load();
00552 
00553   QStringList names = manager.listNames();
00554   QStringList::Iterator jt;
00555   for ( jt = names.begin(); jt != names.end(); ++jt)
00556     result.append( *jt );
00557   result.sort();
00558 
00559   return result;
00560 }
00561 
00562 #include "addresslineedit.moc"
00563 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Mon Oct 24 13:06:40 2011 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001