kabc Library API Documentation

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., 59 Temple Place - Suite 330, 00021 Boston, MA 02111-1307, USA. 00022 */ 00023 00024 // $Id: addresslineedit.cpp,v 1.20 2004/01/20 06:34:24 adawit Exp $ 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 // Class AddressLineEdit 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 // Whenever a new AddressLineEdit is created (== a new composer is created), 00084 // we set a dirty flag to reload the addresses upon the first completion. 00085 // The address completions are shared between all AddressLineEdits. 00086 // Is there a signal that tells us about addressbook updates? 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 ); // we handle it ourself 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; // don't connect muliple times. That's 00125 // ugly, tho, better have completionBox() 00126 // virtual in KDE 4 00127 // Why? This is only called once. Why should this be called more 00128 // than once? And why was this protected? 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()) // at End? 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 // remove newlines in the to-be-pasted string as well as an eventual 00216 // mailto: protocol 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 // Anti-spam stuff 00226 newText.replace( " at ", "@" ); 00227 newText.replace( " dot ", "." ); 00228 } 00229 else if (newText.find("(at)") != -1) 00230 { 00231 newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" ); 00232 } 00233 00234 QString contents = text(); 00235 int start_sel = 0; 00236 int end_sel = 0; 00237 int pos = cursorPosition(); 00238 if (getSelection(&start_sel, &end_sel)) 00239 { 00240 // Cut away the selection. 00241 if (pos > end_sel) 00242 pos -= (end_sel - start_sel); 00243 else if (pos > start_sel) 00244 pos = start_sel; 00245 contents = contents.left(start_sel) + contents.right(end_sel+1); 00246 } 00247 00248 int eot = contents.length(); 00249 while ((eot > 0) && contents[eot-1].isSpace()) eot--; 00250 if (eot == 0) 00251 { 00252 contents = QString::null; 00253 } 00254 else if (pos >= eot) 00255 { 00256 if (contents[eot-1] == ',') 00257 eot--; 00258 contents.truncate(eot); 00259 contents += ", "; 00260 pos = eot+2; 00261 } 00262 00263 contents = contents.left(pos)+newText+contents.mid(pos); 00264 setText(contents); 00265 setCursorPosition(pos+newText.length()); 00266 } 00267 00268 void AddressLineEdit::paste() 00269 { 00270 if (m_useCompletion) 00271 m_smartPaste = true; 00272 KLineEdit::paste(); 00273 m_smartPaste = false; 00274 } 00275 00276 //----------------------------------------------------------------------------- 00277 void AddressLineEdit::cursorAtEnd() 00278 { 00279 setCursorPosition( text().length() ); 00280 } 00281 00282 //----------------------------------------------------------------------------- 00283 void AddressLineEdit::enableCompletion(bool enable) 00284 { 00285 m_useCompletion = enable; 00286 } 00287 00288 //----------------------------------------------------------------------------- 00289 void AddressLineEdit::doCompletion(bool ctrlT) 00290 { 00291 if ( !m_useCompletion ) 00292 return; 00293 00294 QString prevAddr; 00295 00296 QString s(text()); 00297 int n = s.findRev(','); 00298 00299 if (n >= 0) 00300 { 00301 n++; // Go past the "," 00302 00303 int len = s.length(); 00304 00305 // Increment past any whitespace... 00306 while( n < len && s[n].isSpace() ) 00307 n++; 00308 00309 prevAddr = s.left(n); 00310 s = s.mid(n,255).stripWhiteSpace(); 00311 } 00312 00313 if ( s_addressesDirty ) 00314 loadAddresses(); 00315 00316 if ( ctrlT ) 00317 { 00318 QStringList completions = s_completion->substringCompletion( s ); 00319 if (completions.count() > 1) { 00320 m_previousAddresses = prevAddr; 00321 setCompletedItems( completions ); 00322 } 00323 else if (completions.count() == 1) 00324 setText(prevAddr + completions.first()); 00325 00326 cursorAtEnd(); 00327 return; 00328 } 00329 00330 KGlobalSettings::Completion mode = completionMode(); 00331 00332 switch ( mode ) 00333 { 00334 case KGlobalSettings::CompletionPopupAuto: 00335 { 00336 if (s.isEmpty()) 00337 break; 00338 } 00339 case KGlobalSettings::CompletionPopup: 00340 { 00341 m_previousAddresses = prevAddr; 00342 QStringList items = s_completion->allMatches( s ); 00343 items += s_completion->allMatches( "\"" + s ); 00344 items += s_completion->substringCompletion( '<' + s ); 00345 uint beforeDollarCompletionCount = items.count(); 00346 00347 if( s.find( ' ' ) == -1 ) // one word, possibly given name 00348 items += s_completion->allMatches( "$$" + s ); 00349 00350 if ( !items.isEmpty() ) 00351 { 00352 if ( items.count() > beforeDollarCompletionCount ) 00353 { 00354 // remove the '$$whatever$' part 00355 for( QStringList::Iterator it = items.begin(); 00356 it != items.end(); 00357 ++it ) 00358 { 00359 int pos = (*it).find( '$', 2 ); 00360 if( pos < 0 ) // ??? 00361 continue; 00362 (*it)=(*it).mid( pos + 1 ); 00363 } 00364 } 00365 00366 items = removeMailDupes( items ); 00367 00368 // We do not want KLineEdit::setCompletedItems to perform text 00369 // completion (suggestion) since it does not know how to deal 00370 // with providing proper completions for different items on the 00371 // same line, e.g. comma-separated list of email addresses. 00372 bool autoSuggest = (mode != KGlobalSettings::CompletionPopupAuto); 00373 setCompletedItems( items, autoSuggest ); 00374 00375 if (!autoSuggest) 00376 { 00377 int index = items.first().find( s ); 00378 QString newText = prevAddr + items.first().mid( index ); 00379 //kdDebug() << "OLD TEXT: " << text() << endl; 00380 //kdDebug() << "NEW TEXT: " << newText << endl; 00381 setUserSelection(false); 00382 setCompletedText(newText,true); 00383 } 00384 } 00385 00386 break; 00387 } 00388 00389 case KGlobalSettings::CompletionShell: 00390 { 00391 QString match = s_completion->makeCompletion( s ); 00392 if ( !match.isNull() && match != s ) 00393 { 00394 setText( prevAddr + match ); 00395 cursorAtEnd(); 00396 } 00397 break; 00398 } 00399 00400 case KGlobalSettings::CompletionMan: // Short-Auto in fact 00401 case KGlobalSettings::CompletionAuto: 00402 { 00403 if (!s.isEmpty()) 00404 { 00405 QString match = s_completion->makeCompletion( s ); 00406 if ( !match.isNull() && match != s ) 00407 { 00408 QString adds = prevAddr + match; 00409 setCompletedText( adds ); 00410 } 00411 break; 00412 } 00413 } 00414 case KGlobalSettings::CompletionNone: 00415 default: // fall through 00416 break; 00417 } 00418 } 00419 00420 //----------------------------------------------------------------------------- 00421 void AddressLineEdit::slotPopupCompletion( const QString& completion ) 00422 { 00423 setText( m_previousAddresses + completion ); 00424 cursorAtEnd(); 00425 } 00426 00427 //----------------------------------------------------------------------------- 00428 void AddressLineEdit::loadAddresses() 00429 { 00430 s_completion->clear(); 00431 s_addressesDirty = false; 00432 00433 QStringList adrs = addresses(); 00434 for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it) 00435 addAddress( *it ); 00436 } 00437 00438 void AddressLineEdit::addAddress( const QString& adr ) 00439 { 00440 s_completion->addItem( adr ); 00441 int pos = adr.find( '<' ); 00442 if( pos >= 0 ) 00443 { 00444 ++pos; 00445 int pos2 = adr.find( pos, '>' ); 00446 if( pos2 >= 0 ) 00447 s_completion->addItem( adr.mid( pos, pos2 - pos )); 00448 } 00449 } 00450 00451 void AddressLineEdit::slotStartLDAPLookup() 00452 { 00453 if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this ) 00454 return; 00455 startLoadingLDAPEntries(); 00456 } 00457 00458 void AddressLineEdit::stopLDAPLookup() 00459 { 00460 s_LDAPSearch->cancelSearch(); 00461 s_LDAPLineEdit = NULL; 00462 } 00463 00464 void AddressLineEdit::startLoadingLDAPEntries() 00465 { 00466 QString s( *s_LDAPText ); 00467 // TODO cache last? 00468 QString prevAddr; 00469 int n = s.findRev(','); 00470 if (n>= 0) 00471 { 00472 prevAddr = s.left(n+1) + ' '; 00473 s = s.mid(n+1,255).stripWhiteSpace(); 00474 } 00475 if( s.length() == 0 ) 00476 return; 00477 00478 loadAddresses(); // TODO reuse these? 00479 s_LDAPSearch->startSearch( s ); 00480 } 00481 00482 void AddressLineEdit::slotLDAPSearchData( const QStringList& adrs ) 00483 { 00484 if( s_LDAPLineEdit != this ) 00485 return; 00486 for( QStringList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) { 00487 QString name(*it); 00488 int pos = name.find( " <" ); 00489 int pos_comma = name.find( ',' ); 00490 // put name in quotes, if we have a comma in the name 00491 if (pos>0 && pos_comma>0 && pos_comma<pos) { 00492 name.insert(pos, '\"'); 00493 name.prepend('\"'); 00494 } 00495 addAddress( name ); 00496 } 00497 00498 if( hasFocus() || completionBox()->hasFocus()) 00499 { 00500 if( completionMode() != KGlobalSettings::CompletionNone ) 00501 { 00502 doCompletion( false ); 00503 } 00504 } 00505 } 00506 00507 QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs ) 00508 { 00509 QStringList src = adrs; 00510 qHeapSort( src ); 00511 QString last; 00512 for( QStringList::Iterator it = src.begin(); it != src.end(); ) { 00513 if( *it == last ) 00514 { 00515 it = src.remove( it ); 00516 continue; // dupe 00517 } 00518 last = *it; 00519 ++it; 00520 } 00521 return src; 00522 } 00523 00524 //----------------------------------------------------------------------------- 00525 void AddressLineEdit::dropEvent(QDropEvent *e) 00526 { 00527 KURL::List uriList; 00528 if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList )) 00529 { 00530 QString ct = text(); 00531 KURL::List::Iterator it = uriList.begin(); 00532 for (; it != uriList.end(); ++it) 00533 { 00534 if (!ct.isEmpty()) ct.append(", "); 00535 KURL u(*it); 00536 if ((*it).protocol() == "mailto") 00537 ct.append( (*it).path() ); 00538 else 00539 ct.append( (*it).url() ); 00540 } 00541 setText(ct); 00542 setEdited( true ); 00543 } 00544 else { 00545 if (m_useCompletion) 00546 m_smartPaste = true; 00547 QLineEdit::dropEvent(e); 00548 m_smartPaste = false; 00549 } 00550 } 00551 00552 00553 QStringList AddressLineEdit::addresses() 00554 { 00555 QApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while 00556 00557 QStringList result; 00558 QString space(" "); 00559 QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]"); 00560 QString endQuote("\" "); 00561 QString addr, email; 00562 00563 KABC::AddressBook *addressBook = KABC::StdAddressBook::self(); 00564 KABC::AddressBook::Iterator it; 00565 for( it = addressBook->begin(); it != addressBook->end(); ++it ) { 00566 QStringList emails = (*it).emails(); 00567 00568 QString n = (*it).prefix() + space + 00569 (*it).givenName() + space + 00570 (*it).additionalName() + space + 00571 (*it).familyName() + space + 00572 (*it).suffix(); 00573 00574 n = n.simplifyWhiteSpace(); 00575 00576 QStringList::ConstIterator mit; 00577 00578 for ( mit = emails.begin(); mit != emails.end(); ++mit ) { 00579 email = *mit; 00580 if (!email.isEmpty()) { 00581 if (n.isEmpty() || (email.find( '<' ) != -1)) 00582 addr = QString::null; 00583 else { /* do we really need quotes around this name ? */ 00584 if (n.find(needQuotes) != -1) 00585 addr = '"' + n + endQuote; 00586 else 00587 addr = n + space; 00588 } 00589 00590 if (!addr.isEmpty() && (email.find( '<' ) == -1) 00591 && (email.find( '>' ) == -1) 00592 && (email.find( ',' ) == -1)) 00593 addr += '<' + email + '>'; 00594 else 00595 addr += email; 00596 addr = addr.stripWhiteSpace(); 00597 result.append( addr ); 00598 } 00599 } 00600 } 00601 00602 KABC::DistributionListManager manager( addressBook ); 00603 manager.load(); 00604 result += manager.listNames(); 00605 00606 QApplication::restoreOverrideCursor(); 00607 00608 return result; 00609 } 00610 00611 #include "addresslineedit.moc"
KDE Logo
This file is part of the documentation for kabc Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:44:36 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003