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