00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
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 );
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;
00099
00100
00101
00102
00103 }
00104 }
00105
00106 QStringList AddressLineEdit::Private::addresses()
00107 {
00108 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
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 {
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;
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
00197
00198
00199
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() ) {
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
00270
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
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
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++;
00354
00355 int len = s.length();
00356
00357
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 ) {
00401 items += sCompletion->allMatches( QLatin1String( "$$" ) + s );
00402 }
00403
00404 if ( !items.isEmpty() ) {
00405 if ( items.count() > beforeDollarCompletionCount ) {
00406
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
00420
00421
00422
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
00430
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:
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:
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"