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.18 2003/12/11 10:28:35 waba 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( slotSetTextAsEdited( 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 (e->state()==ControlButton && e->key() == Key_Right) 00166 { 00167 if ((int)text().length() == cursorPosition()) // at End? 00168 { 00169 doCompletion(true); 00170 accept = true; 00171 } 00172 } 00173 else if (e->state()==ControlButton && e->key() == Key_V) 00174 { 00175 if (m_useCompletion) 00176 m_smartPaste = true; 00177 paste(); 00178 m_smartPaste = false; 00179 accept = true; 00180 } 00181 00182 if( !accept ) 00183 KLineEdit::keyPressEvent( e ); 00184 00185 if( e->isAccepted()) 00186 { 00187 if( m_useCompletion && s_LDAPTimer != NULL ) 00188 { 00189 if( *s_LDAPText != text()) 00190 stopLDAPLookup(); 00191 *s_LDAPText = text(); 00192 s_LDAPLineEdit = this; 00193 s_LDAPTimer->start( 500, true ); 00194 } 00195 } 00196 } 00197 00198 void AddressLineEdit::mouseReleaseEvent( QMouseEvent * e ) 00199 { 00200 if (m_useCompletion && (e->button() == MidButton)) 00201 { 00202 m_smartPaste = true; 00203 KLineEdit::mouseReleaseEvent(e); 00204 m_smartPaste = false; 00205 return; 00206 } 00207 KLineEdit::mouseReleaseEvent(e); 00208 } 00209 00210 void AddressLineEdit::insert(const QString &t) 00211 { 00212 if (!m_smartPaste) 00213 { 00214 KLineEdit::insert(t); 00215 return; 00216 } 00217 QString newText = t.stripWhiteSpace(); 00218 if (newText.isEmpty()) 00219 return; 00220 00221 // remove newlines in the to-be-pasted string as well as an eventual 00222 // mailto: protocol 00223 newText.replace( QRegExp("\r?\n"), ", " ); 00224 if ( newText.startsWith( "mailto:" ) ) 00225 { 00226 KURL u(newText); 00227 newText = u.path(); 00228 } 00229 else if (newText.find(" at ") != -1) 00230 { 00231 // Anti-spam stuff 00232 newText.replace( " at ", "@" ); 00233 newText.replace( " dot ", "." ); 00234 } 00235 else if (newText.find("(at)") != -1) 00236 { 00237 newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" ); 00238 } 00239 00240 QString contents = text(); 00241 int start_sel = 0; 00242 int end_sel = 0; 00243 int pos = cursorPosition(); 00244 if (getSelection(&start_sel, &end_sel)) 00245 { 00246 // Cut away the selection. 00247 if (pos > end_sel) 00248 pos -= (end_sel - start_sel); 00249 else if (pos > start_sel) 00250 pos = start_sel; 00251 contents = contents.left(start_sel) + contents.right(end_sel+1); 00252 } 00253 00254 int eot = contents.length(); 00255 while ((eot > 0) && contents[eot-1].isSpace()) eot--; 00256 if (eot == 0) 00257 { 00258 contents = QString::null; 00259 } 00260 else if (pos >= eot) 00261 { 00262 if (contents[eot-1] == ',') 00263 eot--; 00264 contents.truncate(eot); 00265 contents += ", "; 00266 pos = eot+2; 00267 } 00268 00269 contents = contents.left(pos)+newText+contents.mid(pos); 00270 slotSetTextAsEdited(contents); 00271 setCursorPosition(pos+newText.length()); 00272 } 00273 00274 void AddressLineEdit::paste() 00275 { 00276 if (m_useCompletion) 00277 m_smartPaste = true; 00278 KLineEdit::paste(); 00279 m_smartPaste = false; 00280 } 00281 00282 //----------------------------------------------------------------------------- 00283 void AddressLineEdit::cursorAtEnd() 00284 { 00285 setCursorPosition( text().length() ); 00286 } 00287 00288 //----------------------------------------------------------------------------- 00289 void AddressLineEdit::enableCompletion(bool enable) 00290 { 00291 m_useCompletion = enable; 00292 } 00293 00294 //----------------------------------------------------------------------------- 00295 void AddressLineEdit::doCompletion(bool ctrlT) 00296 { 00297 if ( !m_useCompletion ) 00298 return; 00299 00300 QString s(text()); 00301 QString prevAddr; 00302 int n = s.findRev(','); 00303 if (n>= 0) 00304 { 00305 prevAddr = s.left(n+1) + ' '; 00306 s = s.mid(n+1,255).stripWhiteSpace(); 00307 } 00308 00309 KCompletionBox *box = completionBox(); 00310 00311 if ( s.isEmpty() ) 00312 { 00313 box->hide(); 00314 return; 00315 } 00316 00317 KGlobalSettings::Completion mode = completionMode(); 00318 00319 if ( s_addressesDirty ) 00320 loadAddresses(); 00321 00322 if ( ctrlT ) 00323 { 00324 QStringList completions = s_completion->substringCompletion( s ); 00325 if (completions.count() > 1) { 00326 m_previousAddresses = prevAddr; 00327 box->setItems( completions ); 00328 box->setCancelledText( text() ); 00329 box->popup(); 00330 } 00331 else if (completions.count() == 1) 00332 slotSetTextAsEdited(prevAddr + completions.first()); 00333 else 00334 box->hide(); 00335 00336 cursorAtEnd(); 00337 return; 00338 } 00339 00340 switch ( mode ) 00341 { 00342 case KGlobalSettings::CompletionPopup: 00343 { 00344 m_previousAddresses = prevAddr; 00345 QStringList items = s_completion->allMatches( s ); 00346 items += s_completion->allMatches( "\"" + s ); 00347 items += s_completion->substringCompletion( '<' + s ); 00348 uint beforeDollarCompletionCount = items.count(); 00349 00350 if( s.find( ' ' ) == -1 ) // one word, possibly given name 00351 items += s_completion->allMatches( "$$" + s ); 00352 00353 if ( items.isEmpty() ) 00354 box->hide(); 00355 else 00356 { 00357 if ( items.count() > beforeDollarCompletionCount ) 00358 { 00359 // remove the '$$whatever$' part 00360 for( QStringList::Iterator it = items.begin(); 00361 it != items.end(); 00362 ++it ) 00363 { 00364 int pos = (*it).find( '$', 2 ); 00365 if( pos < 0 ) // ??? 00366 continue; 00367 (*it)=(*it).mid( pos + 1 ); 00368 } 00369 } 00370 00371 items = removeMailDupes( items ); 00372 box->setItems( items ); 00373 box->setCancelledText( text() ); 00374 box->popup(); 00375 } 00376 00377 break; 00378 } 00379 00380 case KGlobalSettings::CompletionShell: 00381 { 00382 QString match = s_completion->makeCompletion( s ); 00383 if ( !match.isNull() && match != s ) 00384 { 00385 slotSetTextAsEdited( prevAddr + match ); 00386 cursorAtEnd(); 00387 } 00388 break; 00389 } 00390 00391 case KGlobalSettings::CompletionMan: // Short-Auto in fact 00392 case KGlobalSettings::CompletionAuto: 00393 { 00394 QString match = s_completion->makeCompletion( s ); 00395 if ( !match.isNull() && match != s ) 00396 { 00397 QString adds = prevAddr + match; 00398 int curPos = cursorPosition(); 00399 validateAndSet( adds, curPos, curPos, adds.length() ); 00400 } 00401 break; 00402 } 00403 00404 default: // fall through 00405 case KGlobalSettings::CompletionNone: 00406 break; 00407 } 00408 } 00409 00410 //----------------------------------------------------------------------------- 00411 void AddressLineEdit::slotPopupCompletion( const QString& completion ) 00412 { 00413 slotSetTextAsEdited( m_previousAddresses + completion ); 00414 cursorAtEnd(); 00415 } 00416 00417 //----------------------------------------------------------------------------- 00418 void AddressLineEdit::loadAddresses() 00419 { 00420 s_completion->clear(); 00421 s_addressesDirty = false; 00422 00423 QStringList adrs = addresses(); 00424 for( QStringList::ConstIterator it = adrs.begin(); 00425 it != adrs.end(); 00426 ++it) 00427 addAddress( *it ); 00428 } 00429 00430 void AddressLineEdit::addAddress( const QString& adr ) 00431 { 00432 s_completion->addItem( adr ); 00433 int pos = adr.find( '<' ); 00434 if( pos >= 0 ) 00435 { 00436 ++pos; 00437 int pos2 = adr.find( pos, '>' ); 00438 if( pos2 >= 0 ) 00439 s_completion->addItem( adr.mid( pos, pos2 - pos )); 00440 } 00441 } 00442 00443 void AddressLineEdit::slotStartLDAPLookup() 00444 { 00445 if( !s_LDAPSearch->isAvailable() || s_LDAPLineEdit != this ) 00446 return; 00447 startLoadingLDAPEntries(); 00448 } 00449 00450 void AddressLineEdit::stopLDAPLookup() 00451 { 00452 s_LDAPSearch->cancelSearch(); 00453 s_LDAPLineEdit = NULL; 00454 } 00455 00456 void AddressLineEdit::startLoadingLDAPEntries() 00457 { 00458 QString s( *s_LDAPText ); 00459 // TODO cache last? 00460 QString prevAddr; 00461 int n = s.findRev(','); 00462 if (n>= 0) 00463 { 00464 prevAddr = s.left(n+1) + ' '; 00465 s = s.mid(n+1,255).stripWhiteSpace(); 00466 } 00467 if( s.length() == 0 ) 00468 return; 00469 loadAddresses(); // TODO reuse these? 00470 s_LDAPSearch->startSearch( s ); 00471 } 00472 00473 void AddressLineEdit::slotLDAPSearchData( const QStringList& adrs ) 00474 { 00475 if( s_LDAPLineEdit != this ) 00476 return; 00477 for( QStringList::ConstIterator it = adrs.begin(); 00478 it != adrs.end(); 00479 ++it ) { 00480 QString name(*it); 00481 int pos = name.find( " <" ); 00482 int pos_comma = name.find( ',' ); 00483 // put name in quotes, if we have a comma in the name 00484 if (pos>0 && pos_comma>0 && pos_comma<pos) { 00485 name.insert(pos, '\"'); 00486 name.prepend('\"'); 00487 } 00488 addAddress( name ); 00489 } 00490 if( hasFocus() || completionBox()->hasFocus()) 00491 { 00492 if( completionMode() != KGlobalSettings::CompletionNone ) 00493 { 00494 doCompletion( false ); 00495 } 00496 } 00497 } 00498 00499 void AddressLineEdit::slotSetTextAsEdited( const QString& text ) 00500 { 00501 setText( text ); 00502 setEdited( true ); 00503 } 00504 00505 QStringList AddressLineEdit::removeMailDupes( const QStringList& adrs ) 00506 { 00507 QStringList src = adrs; 00508 qHeapSort( src ); 00509 QString last; 00510 for( QStringList::Iterator it = src.begin(); 00511 it != src.end(); 00512 ) 00513 { 00514 if( *it == last ) 00515 { 00516 it = src.remove( it ); 00517 continue; // dupe 00518 } 00519 last = *it; 00520 ++it; 00521 } 00522 return src; 00523 } 00524 00525 //----------------------------------------------------------------------------- 00526 void AddressLineEdit::dropEvent(QDropEvent *e) 00527 { 00528 KURL::List uriList; 00529 if(KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList )) 00530 { 00531 QString ct = text(); 00532 KURL::List::Iterator it = uriList.begin(); 00533 for (; it != uriList.end(); ++it) 00534 { 00535 if (!ct.isEmpty()) ct.append(", "); 00536 KURL u(*it); 00537 if ((*it).protocol() == "mailto") 00538 ct.append( (*it).path() ); 00539 else 00540 ct.append( (*it).url() ); 00541 } 00542 setText(ct); 00543 setEdited( true ); 00544 } 00545 else { 00546 if (m_useCompletion) 00547 m_smartPaste = true; 00548 QLineEdit::dropEvent(e); 00549 m_smartPaste = false; 00550 } 00551 } 00552 00553 00554 QStringList AddressLineEdit::addresses() 00555 { 00556 QApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while 00557 00558 QStringList result; 00559 QString space(" "); 00560 QRegExp needQuotes("[^ 0-9A-Za-z\\x0080-\\xFFFF]"); 00561 QString endQuote("\" "); 00562 QString addr, email; 00563 00564 KABC::AddressBook *addressBook = KABC::StdAddressBook::self(); 00565 KABC::AddressBook::Iterator it; 00566 for( it = addressBook->begin(); it != addressBook->end(); ++it ) { 00567 QStringList emails = (*it).emails(); 00568 QString n = (*it).prefix() + space + 00569 (*it).givenName() + space + 00570 (*it).additionalName() + space + 00571 (*it).familyName() + space + 00572 (*it).suffix(); 00573 n = n.simplifyWhiteSpace(); 00574 00575 QStringList::ConstIterator mit; 00576 00577 for ( mit = emails.begin(); mit != emails.end(); ++mit ) { 00578 email = *mit; 00579 if (!email.isEmpty()) { 00580 if (n.isEmpty() || (email.find( '<' ) != -1)) 00581 addr = QString::null; 00582 else { /* do we really need quotes around this name ? */ 00583 if (n.find(needQuotes) != -1) 00584 addr = '"' + n + endQuote; 00585 else 00586 addr = n + space; 00587 } 00588 00589 if (!addr.isEmpty() && (email.find( '<' ) == -1) 00590 && (email.find( '>' ) == -1) 00591 && (email.find( ',' ) == -1)) 00592 addr += '<' + email + '>'; 00593 else 00594 addr += email; 00595 addr = addr.stripWhiteSpace(); 00596 result.append( addr ); 00597 } 00598 } 00599 } 00600 KABC::DistributionListManager manager( addressBook ); 00601 manager.load(); 00602 result += manager.listNames(); 00603 00604 QApplication::restoreOverrideCursor(); 00605 00606 return result; 00607 } 00608 00609 #include "addresslineedit.moc"
KDE Logo
This file is part of the documentation for kabc Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 16 17:23:25 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003