• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

kabc

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., 51 Franklin Street, Fifth Floor,
00021     Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "addresslineedit.h"
00025 
00026 #include <QtGui/QApplication>
00027 #include <QtGui/QKeyEvent>
00028 #include <QtGui/QMouseEvent>
00029 #include <QtCore/QObject>
00030 #include <QtCore/QRegExp>
00031 
00032 #include <kcompletionbox.h>
00033 #include <kconfig.h>
00034 #include <kcursor.h>
00035 #include <kdebug.h>
00036 #include <kstandarddirs.h>
00037 #include <kstandardshortcut.h>
00038 
00039 #include "stdaddressbook.h"
00040 
00041 //=============================================================================
00042 //
00043 //   Class  AddressLineEdit
00044 //
00045 //=============================================================================
00046 
00047 using namespace KABC;
00048 
00049 class AddressLineEdit::Private
00050 {
00051   public:
00052     Private( AddressLineEdit *parent )
00053       : mParent( parent ),
00054         mCompletionInitialized( false ),
00055         mSmartPaste( false )
00056     {
00057       init();
00058     }
00059 
00060     void init();
00061     QStringList addresses();
00062     QStringList removeMailDupes( const QStringList &adrs );
00063 
00064     void slotCompletion() { mParent->doCompletion( false ); }
00065     void slotPopupCompletion( const QString &completion );
00066 
00067     AddressLineEdit *mParent;
00068     QString mPreviousAddresses;
00069     bool mUseCompletion;
00070     bool mCompletionInitialized;
00071     bool mSmartPaste;
00072 
00073     static bool sAddressesDirty;
00074     static bool initialized;
00075 };
00076 bool AddressLineEdit::Private::initialized = false;
00077 K_GLOBAL_STATIC( KCompletion, sCompletion )
00078 
00079 void AddressLineEdit::Private::init()
00080 {
00081   if ( !Private::initialized ) {
00082       Private::initialized = true;
00083       sCompletion->setOrder( KCompletion::Sorted );
00084       sCompletion->setIgnoreCase( true );
00085   }
00086 
00087   if ( mUseCompletion && !mCompletionInitialized ) {
00088       mParent->setCompletionObject( sCompletion, false ); // we handle it ourself
00089       mParent->connect( mParent, SIGNAL( completion( const QString& ) ),
00090                         mParent, SLOT( slotCompletion() ) );
00091 
00092       KCompletionBox *box = mParent->completionBox();
00093       mParent->connect( box, SIGNAL( currentTextChanged( const QString& ) ),
00094                         mParent, SLOT( slotPopupCompletion( const QString& ) ) );
00095       mParent->connect( box, SIGNAL( userCancelled( const QString& ) ),
00096                         SLOT( userCancelled( const QString& ) ) );
00097 
00098       mCompletionInitialized = true; // don't connect muliple times. That's
00099                                       // ugly, tho, better have completionBox()
00100                                       // virtual in KDE 4
00101       // Why? This is only called once. Why should this be called more
00102       // than once? And why was this protected?
00103   }
00104 }
00105 
00106 QStringList AddressLineEdit::Private::addresses()
00107 {
00108   QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) ); // loading might take a while
00109 
00110   QStringList result;
00111   QLatin1String space( " " );
00112   QRegExp needQuotes( QLatin1String( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) );
00113   QLatin1String endQuote( "\" " );
00114   QString addr, email;
00115 
00116   KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
00117   KABC::AddressBook::Iterator it;
00118   for ( it = addressBook->begin(); it != addressBook->end(); ++it ) {
00119     QStringList emails = (*it).emails();
00120 
00121     QString n = (*it).prefix() + space +
00122                 (*it).givenName() + space +
00123                 (*it).additionalName() + space +
00124                 (*it).familyName() + space +
00125                 (*it).suffix();
00126 
00127     n = n.simplified();
00128 
00129     QStringList::ConstIterator mit;
00130 
00131     for ( mit = emails.constBegin(); mit != emails.constEnd(); ++mit ) {
00132       email = *mit;
00133       if ( !email.isEmpty() ) {
00134         if ( n.isEmpty() || ( email.indexOf( QLatin1Char( '<' ) ) != -1 ) ) {
00135           addr.clear();
00136         } else { /* do we really need quotes around this name ? */
00137           if ( n.indexOf( needQuotes ) != -1 ) {
00138             addr = QLatin1Char( '"' ) + n + endQuote;
00139           } else {
00140             addr = n + space;
00141           }
00142         }
00143 
00144         if ( !addr.isEmpty() && ( email.indexOf( QLatin1Char( '<' ) ) == -1 ) &&
00145              ( email.indexOf( QLatin1Char( '>' ) ) == -1 ) &&
00146              ( email.indexOf( QLatin1Char( ',' ) ) == -1 ) ) {
00147           addr += QLatin1Char( '<' ) + email + QLatin1Char( '>' );
00148         } else {
00149           addr += email;
00150         }
00151         addr = addr.trimmed();
00152         result.append( addr );
00153       }
00154     }
00155   }
00156 
00157   result += addressBook->allDistributionListNames();
00158 
00159   QApplication::restoreOverrideCursor();
00160 
00161   return result;
00162 }
00163 
00164 QStringList AddressLineEdit::Private::removeMailDupes( const QStringList &addrs )
00165 {
00166   QStringList src( addrs );
00167   qSort( src );
00168 
00169   QString last;
00170   for ( QStringList::Iterator it = src.begin(); it != src.end(); ) {
00171     if ( *it == last ) {
00172       it = src.erase( it );
00173       continue; // dupe
00174     }
00175 
00176     last = *it;
00177     ++it;
00178   }
00179 
00180   return src;
00181 }
00182 
00183 void AddressLineEdit::Private::slotPopupCompletion( const QString &completion )
00184 {
00185   mParent->setText( mPreviousAddresses + completion );
00186   mParent->cursorAtEnd();
00187 }
00188 
00189 bool AddressLineEdit::Private::sAddressesDirty = false;
00190 
00191 AddressLineEdit::AddressLineEdit( QWidget *parent, bool useCompletion )
00192   : KLineEdit( parent ), d( new Private( this ) )
00193 {
00194   d->mUseCompletion = useCompletion;
00195 
00196   // Whenever a new AddressLineEdit is created (== a new composer is created),
00197   // we set a dirty flag to reload the addresses upon the first completion.
00198   // The address completions are shared between all AddressLineEdits.
00199   // Is there a signal that tells us about addressbook updates?
00200   if ( d->mUseCompletion ) {
00201     d->sAddressesDirty = true;
00202   }
00203 }
00204 
00205 //-----------------------------------------------------------------------------
00206 AddressLineEdit::~AddressLineEdit()
00207 {
00208   delete d;
00209 }
00210 
00211 //-----------------------------------------------------------------------------
00212 
00213 void AddressLineEdit::setFont( const QFont &font )
00214 {
00215   KLineEdit::setFont( font );
00216   if ( d->mUseCompletion ) {
00217     completionBox()->setFont( font );
00218   }
00219 }
00220 
00221 //-----------------------------------------------------------------------------
00222 void AddressLineEdit::keyPressEvent( QKeyEvent *event )
00223 {
00224   bool accept = false;
00225 
00226   if ( KStandardShortcut::shortcut( KStandardShortcut::SubstringCompletion ).
00227        contains( event->key() | event->modifiers() ) ) {
00228     doCompletion( true );
00229     accept = true;
00230   } else if ( KStandardShortcut::shortcut( KStandardShortcut::TextCompletion ).
00231               contains( event->key() | event->modifiers() ) ) {
00232     int len = text().length();
00233 
00234     if ( len == cursorPosition() ) { // at End?
00235       doCompletion( true );
00236       accept = true;
00237     }
00238   }
00239 
00240   if ( !accept ) {
00241     KLineEdit::keyPressEvent( event );
00242   }
00243 }
00244 
00245 void AddressLineEdit::mouseReleaseEvent( QMouseEvent *event )
00246 {
00247   if ( d->mUseCompletion && ( event->button() == Qt::MidButton ) ) {
00248     d->mSmartPaste = true;
00249     KLineEdit::mouseReleaseEvent( event );
00250     d->mSmartPaste = false;
00251     return;
00252   }
00253 
00254   KLineEdit::mouseReleaseEvent( event );
00255 }
00256 
00257 void AddressLineEdit::insert( const QString &oldText )
00258 {
00259   if ( !d->mSmartPaste ) {
00260     KLineEdit::insert( oldText );
00261     return;
00262   }
00263 
00264   QString newText = oldText.trimmed();
00265   if ( newText.isEmpty() ) {
00266     return;
00267   }
00268 
00269   // remove newlines in the to-be-pasted string as well as an eventual
00270   // mailto: protocol
00271   newText.replace( QRegExp( QLatin1String( "\r?\n" ) ), QLatin1String( ", " ) );
00272   if ( newText.startsWith( QLatin1String( "mailto:" ) ) ) {
00273     KUrl u( newText );
00274     newText = u.path();
00275   } else if ( newText.indexOf( QLatin1String( " at " ) ) != -1 ) {
00276     // Anti-spam stuff
00277     newText.replace( QLatin1String( " at " ), QLatin1String( "@" ) );
00278     newText.replace( QLatin1String( " dot " ), QLatin1String( "." ) );
00279   } else if ( newText.indexOf( QLatin1String( "(at)" ) ) != -1 ) {
00280     newText.replace( QRegExp( QLatin1String( "\\s*\\(at\\)\\s*" ) ), QLatin1String( "@" ) );
00281   }
00282 
00283   QString contents = text();
00284   int start_sel = 0;
00285   int end_sel = 0;
00286   int pos = cursorPosition();
00287   if ( !selectedText().isEmpty() ) {
00288     // Cut away the selection.
00289     if ( pos > end_sel ) {
00290       pos -= ( end_sel - start_sel );
00291     } else if ( pos > start_sel ) {
00292       pos = start_sel;
00293     }
00294     contents = contents.left( start_sel ) + contents.right( end_sel + 1 );
00295   }
00296 
00297   int eot = contents.length();
00298   while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) {
00299     eot--;
00300   }
00301 
00302   if ( eot == 0 ) {
00303     contents.clear();
00304   } else if ( pos >= eot ) {
00305     if ( contents[ eot - 1 ] == QLatin1Char( ',' ) ) {
00306       eot--;
00307     }
00308     contents.truncate( eot );
00309     contents += QLatin1String( ", " );
00310     pos = eot+2;
00311   }
00312 
00313   contents = contents.left( pos ) + newText + contents.mid( pos );
00314   setText( contents );
00315   setCursorPosition( pos + newText.length() );
00316 }
00317 
00318 void AddressLineEdit::paste()
00319 {
00320   if ( d->mUseCompletion ) {
00321     d->mSmartPaste = true;
00322   }
00323 
00324   KLineEdit::paste();
00325   d->mSmartPaste = false;
00326 }
00327 
00328 //-----------------------------------------------------------------------------
00329 void AddressLineEdit::cursorAtEnd()
00330 {
00331   setCursorPosition( text().length() );
00332 }
00333 
00334 //-----------------------------------------------------------------------------
00335 void AddressLineEdit::enableCompletion( bool enable )
00336 {
00337   d->mUseCompletion = enable;
00338 }
00339 
00340 //-----------------------------------------------------------------------------
00341 void AddressLineEdit::doCompletion( bool ctrlT )
00342 {
00343   if ( !d->mUseCompletion ) {
00344       return;
00345   }
00346 
00347   QString prevAddr;
00348 
00349   QString s( text() );
00350   int n = s.lastIndexOf( QLatin1Char( ',' ) );
00351 
00352   if ( n >= 0 ) {
00353     n++; // Go past the ","
00354 
00355     int len = s.length();
00356 
00357     // Increment past any whitespace...
00358     while ( n < len && s[ n ].isSpace() ) {
00359       n++;
00360     }
00361 
00362     prevAddr = s.left( n );
00363     s = s.mid( n, 255 ).trimmed();
00364   }
00365 
00366   if ( d->sAddressesDirty ) {
00367     loadAddresses();
00368   }
00369 
00370   if ( ctrlT ) {
00371     QStringList completions = sCompletion->substringCompletion( s );
00372     if ( completions.count() > 1 ) {
00373       d->mPreviousAddresses = prevAddr;
00374       setCompletedItems( completions );
00375     } else if ( completions.count() == 1 ) {
00376       setText( prevAddr + completions.first() );
00377     }
00378 
00379     cursorAtEnd();
00380     return;
00381   }
00382 
00383   KGlobalSettings::Completion mode = completionMode();
00384 
00385   switch ( mode ) {
00386     case KGlobalSettings::CompletionPopupAuto:
00387     {
00388       if ( s.isEmpty() ) {
00389         break;
00390       }
00391     }
00392     case KGlobalSettings::CompletionPopup:
00393     {
00394       d->mPreviousAddresses = prevAddr;
00395       QStringList items = sCompletion->allMatches( s );
00396       items += sCompletion->allMatches( QLatin1String( "\"" ) + s );
00397       items += sCompletion->substringCompletion( QLatin1Char( '<' ) + s );
00398       int beforeDollarCompletionCount = items.count();
00399 
00400       if ( s.indexOf( QLatin1Char( ' ' ) ) == -1 ) { // one word, possibly given name
00401         items += sCompletion->allMatches( QLatin1String( "$$" ) + s );
00402       }
00403 
00404       if ( !items.isEmpty() ) {
00405         if ( items.count() > beforeDollarCompletionCount ) {
00406           // remove the '$$whatever$' part
00407           for ( QStringList::Iterator it = items.begin();
00408                 it != items.end(); ++it ) {
00409             int pos = (*it).indexOf( QLatin1Char( '$' ), 2 );
00410             if ( pos < 0 ) { // ???
00411               continue;
00412             }
00413             (*it) = (*it).mid( pos + 1 );
00414           }
00415         }
00416 
00417         items = d->removeMailDupes( items );
00418 
00419         // We do not want KLineEdit::setCompletedItems to perform text
00420         // completion (suggestion) since it does not know how to deal
00421         // with providing proper completions for different items on the
00422         // same line, e.g. comma-separated list of email addresses.
00423         bool autoSuggest = ( mode != KGlobalSettings::CompletionPopupAuto );
00424         setCompletedItems( items, autoSuggest );
00425 
00426         if ( !autoSuggest ) {
00427           int index = items.first().indexOf( s );
00428           QString newText = prevAddr + items.first().mid( index );
00429           //kDebug() << "OLD TEXT:" << text();
00430           //kDebug() << "NEW TEXT:" << newText;
00431           setUserSelection( false );
00432           setCompletedText( newText, true );
00433         }
00434       }
00435 
00436       break;
00437     }
00438 
00439     case KGlobalSettings::CompletionShell:
00440     {
00441       QString match = sCompletion->makeCompletion( s );
00442       if ( !match.isNull() && match != s ) {
00443         setText( prevAddr + match );
00444         cursorAtEnd();
00445       }
00446       break;
00447     }
00448 
00449     case KGlobalSettings::CompletionMan: // Short-Auto in fact
00450     case KGlobalSettings::CompletionAuto:
00451     {
00452       if ( !s.isEmpty() ) {
00453         QString match = sCompletion->makeCompletion( s );
00454         if ( !match.isNull() && match != s ) {
00455           setCompletedText( prevAddr + match );
00456         }
00457 
00458         break;
00459       }
00460     }
00461     case KGlobalSettings::CompletionNone:
00462     default: // fall through
00463         break;
00464   }
00465 }
00466 
00467 //-----------------------------------------------------------------------------
00468 void AddressLineEdit::loadAddresses()
00469 {
00470   sCompletion->clear();
00471   d->sAddressesDirty = false;
00472 
00473   const QStringList addrs = d->addresses();
00474   for ( QStringList::ConstIterator it = addrs.begin(); it != addrs.end(); ++it ) {
00475     addAddress( *it );
00476   }
00477 }
00478 
00479 void AddressLineEdit::addAddress( const QString &addr )
00480 {
00481   sCompletion->addItem( addr );
00482 
00483   int pos = addr.indexOf( QLatin1Char( '<' ) );
00484   if ( pos >= 0 ) {
00485     ++pos;
00486     int pos2 = addr.indexOf( QLatin1Char( '>' ), pos );
00487     if ( pos2 >= 0 ) {
00488       sCompletion->addItem( addr.mid( pos, pos2 - pos ) );
00489     }
00490   }
00491 }
00492 
00493 //-----------------------------------------------------------------------------
00494 void AddressLineEdit::dropEvent( QDropEvent *event )
00495 {
00496   const KUrl::List uriList = KUrl::List::fromMimeData( event->mimeData() );
00497   if ( !uriList.isEmpty() ) {
00498     QString ct = text();
00499     KUrl::List::ConstIterator it = uriList.begin();
00500     for ( ; it != uriList.end(); ++it ) {
00501       if ( !ct.isEmpty() ) {
00502         ct.append( QLatin1String( ", " ) );
00503       }
00504 
00505       KUrl u( *it );
00506       if ( (*it).protocol() == QLatin1String( "mailto" ) ) {
00507         ct.append( (*it).path() );
00508       } else {
00509         ct.append( (*it).url() );
00510       }
00511     }
00512     setText( ct );
00513     setModified( true );
00514   } else {
00515     if ( d->mUseCompletion ) {
00516        d->mSmartPaste = true;
00517     }
00518 
00519     KLineEdit::dropEvent( event );
00520     d->mSmartPaste = false;
00521   }
00522 }
00523 
00524 #include "addresslineedit.moc"

kabc

Skip menu "kabc"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal