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