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(QString)), 00090 mParent, SLOT(slotCompletion()) ); 00091 00092 KCompletionBox *box = mParent->completionBox(); 00093 mParent->connect( box, SIGNAL(currentTextChanged(QString)), 00094 mParent, SLOT(slotPopupCompletion(QString)) ); 00095 mParent->connect( box, SIGNAL(userCancelled(QString)), 00096 SLOT(userCancelled(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"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:26:07 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:26:07 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.