00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "addresslineedit.h"
00027
00028 #include <qapplication.h>
00029 #include <qobject.h>
00030 #include <qptrlist.h>
00031 #include <qregexp.h>
00032 #include <qevent.h>
00033 #include <qdragobject.h>
00034
00035 #include <kcompletionbox.h>
00036 #include <kconfig.h>
00037 #include <kcursor.h>
00038 #include <kstandarddirs.h>
00039 #include <kstaticdeleter.h>
00040 #include <kstdaccel.h>
00041 #include <kurldrag.h>
00042
00043 #include <kabc/stdaddressbook.h>
00044 #include <kabc/distributionlist.h>
00045 #include "ldapclient.h"
00046
00047 #include <kdebug.h>
00048
00049
00050
00051
00052
00053
00054
00055
00056 using namespace KABC;
00057
00058 KCompletion * AddressLineEdit::s_completion = 0L;
00059 bool AddressLineEdit::s_addressesDirty = false;
00060 QTimer* AddressLineEdit::s_LDAPTimer = 0L;
00061 LdapSearch* AddressLineEdit::s_LDAPSearch = 0L;
00062 QString* AddressLineEdit::s_LDAPText = 0L;
00063 AddressLineEdit* AddressLineEdit::s_LDAPLineEdit = 0L;
00064 KConfig *AddressLineEdit::s_config = 0L;
00065
00066 static KStaticDeleter<KCompletion> completionDeleter;
00067 static KStaticDeleter<QTimer> ldapTimerDeleter;
00068 static KStaticDeleter<LdapSearch> ldapSearchDeleter;
00069 static KStaticDeleter<QString> ldapTextDeleter;
00070 static KStaticDeleter<KConfig> configDeleter;
00071
00072 AddressLineEdit::AddressLineEdit(QWidget* parent,
00073 bool useCompletion,
00074 const char *name)
00075 : KLineEdit(parent,name)
00076 {
00077 m_useCompletion = useCompletion;
00078 m_completionInitialized = false;
00079 m_smartPaste = false;
00080
00081 init();
00082
00083
00084
00085
00086
00087 if (m_useCompletion)
00088 s_addressesDirty = true;
00089 }
00090
00091
00092
00093 void AddressLineEdit::init()
00094 {
00095 if ( !s_completion ) {
00096 completionDeleter.setObject( s_completion, new KCompletion() );
00097 s_completion->setOrder( KCompletion::Sorted );
00098 s_completion->setIgnoreCase( true );
00099 }
00100
00101 if( m_useCompletion ) {
00102 if( !s_LDAPTimer ) {
00103 ldapTimerDeleter.setObject( s_LDAPTimer, new QTimer );
00104 ldapSearchDeleter.setObject( s_LDAPSearch, new LdapSearch );
00105 ldapTextDeleter.setObject( s_LDAPText, new QString );
00106 }
00107 connect( s_LDAPTimer, SIGNAL( timeout()), SLOT( slotStartLDAPLookup()));
00108 connect( s_LDAPSearch, SIGNAL( searchData( const QStringList& )),
00109 SLOT( slotLDAPSearchData( const QStringList& )));
00110 }
00111
00112 if ( m_useCompletion && !m_completionInitialized )
00113 {
00114 setCompletionObject( s_completion, false );
00115 connect( this, SIGNAL( completion(const QString&)),
00116 this, SLOT(slotCompletion() ));
00117
00118 KCompletionBox *box = completionBox();
00119 connect( box, SIGNAL( highlighted( const QString& )),
00120 this, SLOT( slotPopupCompletion( const QString& ) ));
00121 connect( box, SIGNAL( userCancelled( const QString& )),
00122 SLOT( userCancelled( const QString& )));
00123
00124 m_completionInitialized = true;
00125
00126
00127
00128
00129 }
00130 }
00131
00132
00133 AddressLineEdit::~AddressLineEdit()
00134 {
00135 }
00136
00137
00138
00139 KConfig* AddressLineEdit::config()
00140 {
00141 if ( !s_config )
00142 configDeleter.setObject( s_config, new KConfig( locateLocal( "config",
00143 "kabldaprc" ) ) );
00144
00145 return s_config;
00146 }
00147
00148 void AddressLineEdit::setFont( const QFont& font )
00149 {
00150 KLineEdit::setFont( font );
00151 if ( m_useCompletion )
00152 completionBox()->setFont( font );
00153 }
00154
00155
00156 void AddressLineEdit::keyPressEvent(QKeyEvent *e)
00157 {
00158 bool accept = false;
00159
00160 if (KStdAccel::shortcut(KStdAccel::SubstringCompletion).contains(KKey(e)))
00161 {
00162 doCompletion(true);
00163 accept = true;
00164 }
00165 else if (KStdAccel::shortcut(KStdAccel::TextCompletion).contains(KKey(e)))
00166 {
00167 int len = text().length();
00168
00169 if (len == cursorPosition())
00170 {
00171 doCompletion(true);
00172 accept = true;
00173 }
00174 }
00175
00176 if( !accept )
00177 KLineEdit::keyPressEvent( e );
00178
00179 if( e->isAccepted())
00180 {
00181 if( m_useCompletion && s_LDAPTimer != NULL )
00182 {
00183 if( *s_LDAPText != text())
00184 stopLDAPLookup();
00185 *s_LDAPText = text();
00186 s_LDAPLineEdit = this;
00187 s_LDAPTimer->start( 500, true );
00188 }
00189 }
00190 }
00191
00192 void AddressLineEdit::mouseReleaseEvent( QMouseEvent * e )
00193 {
00194 if (m_useCompletion && (e->button() == MidButton))
00195 {
00196 m_smartPaste = true;
00197 KLineEdit::mouseReleaseEvent(e);
00198 m_smartPaste = false;
00199 return;
00200 }
00201 KLineEdit::mouseReleaseEvent(e);
00202 }
00203
00204 void AddressLineEdit::insert(const QString &t)
00205 {
00206 if (!m_smartPaste)
00207 {
00208 KLineEdit::insert(t);
00209 return;
00210 }
00211 QString newText = t.stripWhiteSpace();
00212 if (newText.isEmpty())
00213 return;
00214
00215
00216
00217 newText.replace( QRegExp("\r?\n"), ", " );
00218 if ( newText.startsWith( "mailto:" ) )
00219 {
00220 KURL u(newText);
00221 newText = u.path();
00222 }
00223 else if (newText.find(" at ") != -1)
00224 {
00225
00226 newText.replace( " at ", "@" );
newText.replace( " dot ", "." );
00227 }
00228 else if (newText.find("(at)") != -1)
00229 {
00230 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 += ", ";
00231 pos = eot+2;
00232 }
00233
00234 contents = contents.left(pos)+newText+contents.mid(pos);
00235 setText(contents);
00236 setCursorPosition(pos+newText.length());
00237 }
00238
00239 void AddressLineEdit::paste()
00240 {
00241 if (m_useCompletion)
00242 m_smartPaste = true;
00243 KLineEdit::paste();
00244 m_smartPaste = false;
00245 }
00246
00247
00248 void AddressLineEdit::cursorAtEnd()
00249 {
00250 setCursorPosition( text().length() );
00251 }
00252
00253
00254 void AddressLineEdit::enableCompletion(bool enable)
00255 {
00256 m_useCompletion = enable;
00257 }
00258
00259
00260 void AddressLineEdit::doCompletion(bool ctrlT)
00261 {
00262 if ( !m_useCompletion )
00263 return;
00264
00265 QString prevAddr;
00266
00267 QString s(text());
00268 int n = s.findRev(',');
00269
00270 if (n >= 0)
00271 {
00272 n++;
00273
00274 int len = s.length();
00275
00276
00277 while( n < len && s[n].isSpace() )
00278 n++;
00279
00280 prevAddr = s.left(n);
00281 s = s.mid(n,255).stripWhiteSpace();
00282 }
00283
00284 if ( s_addressesDirty )
00285 loadAddresses();
00286
00287 if ( ctrlT )
00288 {
00289 QStringList completions = s_completion->substringCompletion( s );
00290 if (completions.count() > 1) {
00291 m_previousAddresses = prevAddr;
00292 setCompletedItems( completions );
00293 }
00294 else if (completions.count() == 1)
00295 setText(prevAddr + completions.first());
00296
00297 cursorAtEnd();
00298 return;
00299 }
00300
00301 KGlobalSettings::Completion mode = completionMode();
00302
00303 switch ( mode )
00304 {
00305 case KGlobalSettings::CompletionPopupAuto:
00306 {
00307 if (s.isEmpty())
00308 break;
00309 }
00310 case KGlobalSettings::CompletionPopup:
00311 {
00312 m_previousAddresses = prevAddr;
00313 QStringList items = s_completion->allMatches( s );
00314 items += s_completion->allMatches( "\"" + s );
00315 items += s_completion->substringCompletion( '<' + s );
00316 uint beforeDollarCompletionCount = items.count();
00317
00318 if( s.find( ' ' ) == -1 )
00319 items += s_completion->allMatches( "$$" + s );
00320
00321 if ( !items.isEmpty() )
00322 {
00323 if ( items.count() > beforeDollarCompletionCount )
00324 {
00325
00326 for( QStringList::Iterator it = items.begin();
00327 it != items.end();
00328 ++it )
00329 {
00330 int pos = (*it).find( '$', 2 );
00331 if( pos < 0 )
00332 continue;
00333 (*it)=(*it).mid( pos + 1 );
00334 }
00335 }
00336
00337 items = removeMailDupes( items );
00338
00339
00340
00341
00342
00343 bool autoSuggest = (mode != KGlobalSettings::CompletionPopupAuto);
00344 setCompletedItems( items, autoSuggest );
00345
00346 if (!autoSuggest)
00347 {
00348 int index = items.first().find( s );
00349 QString newText = prevAddr + items.first().mid( index );
00350
00351
00352 setUserSelection(false);
00353 setCompletedText(newText,true);
00354 }
00355 }
00356
00357 break;
00358 }
00359
00360 case KGlobalSettings::CompletionShell:
00361 {
00362 QString match = s_completion->makeCompletion( s );
00363 if ( !match.isNull() && match != s )
00364 {
00365 setText( prevAddr + match );
00366 cursorAtEnd();
00367 }
00368 break;
00369 }
00370
00371 case KGlobalSettings::CompletionMan:
00372 case KGlobalSettings::CompletionAuto:
00373 {
00374 if (!s.isEmpty())
00375 {
00376 QString match = s_completion->makeCompletion( s );
00377 if ( !match.isNull() && match != s )
00378 {
00379 QString adds = prevAddr + match;
00380 setCompletedText( adds );
00381 }
00382 break;
00383 }
00384 }
00385 case KGlobalSettings::CompletionNone:
00386 default:
00387 break;
00388 }
00389 }
00390
00391
00392 void AddressLineEdit::slotPopupCompletion( const QString& completion )
00393 {
00394 setText( m_previousAddresses + completion );
00395 cursorAtEnd();
00396 }
00397
00398
00399 void AddressLineEdit::loadAddresses()
00400 {
00401 s_completion->clear();
00402 s_addressesDirty = false;
00403
00404 QStringList adrs = addresses();
00405 for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it)
00406 addAddress( *it );
00407 }
00408
00409 void AddressLineEdit::addAddress( const QString& adr )
00410 {
00411 s_completion->addItem( adr );
00412 int pos = adr.find( '<' );
00413 if( pos >= 0 )
00414 {
00415 ++pos;
00416 int pos2 = adr.find( pos, '>' );
00417 if( pos2 >= 0 )
00418 s_completion->addItem( adr.mid( pos, pos2 - pos ));
00419 }
00420 }
00421
00422 void AddressLineEdit::slotStartLDAPLookup()
00423 {
00424 if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this )
00425 return;
00426 startLoadingLDAPEntries();
00427 }
00428
00429 void AddressLineEdit::stopLDAPLookup()
00430 {
00431 s_LDAPSearch->cancelSearch();
00432 s_LDAPLineEdit = NULL;
00433 }
00434
00435 void AddressLineEdit::startLoadingLDAPEntries()
00436 {
00437 QString s( *s_LDAPText );
00438
00439 QString prevAddr;
00440 int n = s.findRev(',');
00441 if (n>= 0)
00442 {
00443 prevAddr = s.left(n+1) + ' ';
00444 s = s.mid(n+1,255).stripWhiteSpace();
00445 }
00446 if( s.length() == 0 )
00447 return;
00448
00449 loadAddresses();
00450 s_LDAPSearch->startSearch( s );
00451 }
00452
00453 void AddressLineEdit::slotLDAPSearchData( const QStringList& adrs )
00454 {
00455 if( s_LDAPLineEdit != this )
00456 return;
00457 for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) {
00458 QString name(*it);
00459 int pos = name.find( " <" );
00460 int pos_comma = name.find( ',' );
00461
00462 if (pos>0 && pos_comma>0 && pos_comma<pos) {
00463 name.insert(pos, '\"');
00464 name.prepend('\"');
00465 }
00466 addAddress( name );
00467 }
00468
00469 if( hasFocus() || completionBox()->hasFocus())
00470 {
00471 if( completionMode() != KGlobalSettings::CompletionNone )
00472 {
00473 doCompletion( false );
00474 }
00475 }
00476 }
00477
00478 QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs )
00479 {
00480 QStringList src = adrs;
00481 qHeapSort( src );
00482 QString last;
00483 for( QStringList::Iterator it = src.begin(); it != src.end(); ) {
00484 if( *it == last )
00485 {
00486 it = src.remove( it );
00487 continue;
00488 }
00489 last = *it;
00490 ++it;
00491 }
00492 return src;
00493 }
00494
00495
00496 void AddressLineEdit::dropEvent(QDropEvent *e)
00497 {
00498 KURL::List uriList;
00499 if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ))
00500 {
00501 QString ct = text();
00502 KURL::List::Iterator it = uriList.begin();
00503 for (; it != uriList.end(); ++it)
00504 {
00505 if (!ct.isEmpty()) ct.append(", ");
00506 KURL u(*it);
00507 if ((*it).protocol() == "mailto")
00508 ct.append( (*it).path() );
00509 else
00510 ct.append( (*it).url() );
00511 }
00512 setText(ct);
00513 setEdited( true );
00514 }
00515 else {
00516 if (m_useCompletion)
00517 m_smartPaste = true;
00518 QLineEdit::dropEvent(e);
00519 m_smartPaste = false;
00520 }
00521 }
00522
00523
00524 QStringList AddressLineEdit::addresses()
00525 {
00526 QApplication::setOverrideCursor( KCursor::waitCursor() );
00527
00528 QStringList result;
00529 QString space(" ");
00530 QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]");
00531 QString endQuote("\" ");
00532 QString addr, email;
00533
00534 KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
00535 KABC::AddressBook::Iterator it;
00536 for( it = addressBook->begin(); it != addressBook->end(); ++it ) {
00537 QStringList emails = (*it).emails();
00538
00539 QString n = (*it).prefix() + space +
00540 (*it).givenName() + space +
00541 (*it).additionalName() + space +
00542 (*it).familyName() + space +
00543 (*it).suffix();
00544
00545 n = n.simplifyWhiteSpace();
00546
00547 QStringList::ConstIterator mit;
00548
00549 for ( mit = emails.begin(); mit != emails.end(); ++mit ) {
00550 email = *mit;
00551 if (!email.isEmpty()) {
00552 if (n.isEmpty() || (email.find( '<' ) != -1))
00553 addr = QString::null;
00554 else {
00555 if (n.find(needQuotes) != -1)
00556 addr = '"' + n + endQuote;
00557 else
00558 addr = n + space;
00559 }
00560
00561 if (!addr.isEmpty() && (email.find( '<' ) == -1)
00562 && (email.find( '>' ) == -1)
00563 && (email.find( ',' ) == -1))
00564 addr += '<' + email + '>';
00565 else
00566 addr += email;
00567 addr = addr.stripWhiteSpace();
00568 result.append( addr );
00569 }
00570 }
00571 }
00572
00573 KABC::DistributionListManager manager( addressBook );
00574 manager.load();
00575 result += manager.listNames();
00576
00577 QApplication::restoreOverrideCursor();
00578
00579 return result;
00580 }
00581
00582 #include "addresslineedit.moc"
00583