• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.9.4 API Reference
  • KDE Home
  • Contact Us
 

kabc

  • kabc
addresslineedit.cpp
1 /*
2  This file is part of libkabc.
3  Copyright (c) 2002 Helge Deller <deller@gmx.de>
4  2002 Lubos Lunak <llunak@suse.cz>
5  2001,2003 Carsten Pfeiffer <pfeiffer@kde.org>
6  2001 Waldo Bastian <bastian@kde.org>
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22 */
23 
24 #include "addresslineedit.h"
25 
26 #include <QtGui/QApplication>
27 #include <QtGui/QKeyEvent>
28 #include <QtGui/QMouseEvent>
29 #include <QtCore/QObject>
30 #include <QtCore/QRegExp>
31 
32 #include <kcompletionbox.h>
33 #include <kconfig.h>
34 #include <kcursor.h>
35 #include <kdebug.h>
36 #include <kstandarddirs.h>
37 #include <kstandardshortcut.h>
38 
39 #include "stdaddressbook.h"
40 
41 //=============================================================================
42 //
43 // Class AddressLineEdit
44 //
45 //=============================================================================
46 
47 using namespace KABC;
48 
49 class AddressLineEdit::Private
50 {
51  public:
52  Private( AddressLineEdit *parent )
53  : mParent( parent ),
54  mCompletionInitialized( false ),
55  mSmartPaste( false )
56  {
57  init();
58  }
59 
60  void init();
61  QStringList addresses();
62  QStringList removeMailDupes( const QStringList &adrs );
63 
64  void slotCompletion() { mParent->doCompletion( false ); }
65  void slotPopupCompletion( const QString &completion );
66 
67  AddressLineEdit *mParent;
68  QString mPreviousAddresses;
69  bool mUseCompletion;
70  bool mCompletionInitialized;
71  bool mSmartPaste;
72 
73  static bool sAddressesDirty;
74  static bool initialized;
75 };
76 bool AddressLineEdit::Private::initialized = false;
77 K_GLOBAL_STATIC( KCompletion, sCompletion )
78 
79 void AddressLineEdit::Private::init()
80 {
81  if ( !Private::initialized ) {
82  Private::initialized = true;
83  sCompletion->setOrder( KCompletion::Sorted );
84  sCompletion->setIgnoreCase( true );
85  }
86 
87  if ( mUseCompletion && !mCompletionInitialized ) {
88  mParent->setCompletionObject( sCompletion, false ); // we handle it ourself
89  mParent->connect( mParent, SIGNAL(completion(QString)),
90  mParent, SLOT(slotCompletion()) );
91 
92  KCompletionBox *box = mParent->completionBox();
93  mParent->connect( box, SIGNAL(currentTextChanged(QString)),
94  mParent, SLOT(slotPopupCompletion(QString)) );
95  mParent->connect( box, SIGNAL(userCancelled(QString)),
96  SLOT(userCancelled(QString)) );
97 
98  mCompletionInitialized = true; // don't connect muliple times. That's
99  // ugly, tho, better have completionBox()
100  // virtual in KDE 4
101  // Why? This is only called once. Why should this be called more
102  // than once? And why was this protected?
103  }
104 }
105 
106 QStringList AddressLineEdit::Private::addresses()
107 {
108  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) ); // loading might take a while
109 
110  QStringList result;
111  QLatin1String space( " " );
112  QRegExp needQuotes( QLatin1String( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) );
113  QLatin1String endQuote( "\" " );
114  QString addr, email;
115 
116  KABC::AddressBook *addressBook = KABC::StdAddressBook::self();
117  KABC::AddressBook::Iterator it;
118  for ( it = addressBook->begin(); it != addressBook->end(); ++it ) {
119  QStringList emails = (*it).emails();
120 
121  QString n = (*it).prefix() + space +
122  (*it).givenName() + space +
123  (*it).additionalName() + space +
124  (*it).familyName() + space +
125  (*it).suffix();
126 
127  n = n.simplified();
128 
129  QStringList::ConstIterator mit;
130 
131  for ( mit = emails.constBegin(); mit != emails.constEnd(); ++mit ) {
132  email = *mit;
133  if ( !email.isEmpty() ) {
134  if ( n.isEmpty() || ( email.indexOf( QLatin1Char( '<' ) ) != -1 ) ) {
135  addr.clear();
136  } else { /* do we really need quotes around this name ? */
137  if ( n.indexOf( needQuotes ) != -1 ) {
138  addr = QLatin1Char( '"' ) + n + endQuote;
139  } else {
140  addr = n + space;
141  }
142  }
143 
144  if ( !addr.isEmpty() && ( email.indexOf( QLatin1Char( '<' ) ) == -1 ) &&
145  ( email.indexOf( QLatin1Char( '>' ) ) == -1 ) &&
146  ( email.indexOf( QLatin1Char( ',' ) ) == -1 ) ) {
147  addr += QLatin1Char( '<' ) + email + QLatin1Char( '>' );
148  } else {
149  addr += email;
150  }
151  addr = addr.trimmed();
152  result.append( addr );
153  }
154  }
155  }
156 
157  result += addressBook->allDistributionListNames();
158 
159  QApplication::restoreOverrideCursor();
160 
161  return result;
162 }
163 
164 QStringList AddressLineEdit::Private::removeMailDupes( const QStringList &addrs )
165 {
166  QStringList src( addrs );
167  qSort( src );
168 
169  QString last;
170  for ( QStringList::Iterator it = src.begin(); it != src.end(); ) {
171  if ( *it == last ) {
172  it = src.erase( it );
173  continue; // dupe
174  }
175 
176  last = *it;
177  ++it;
178  }
179 
180  return src;
181 }
182 
183 void AddressLineEdit::Private::slotPopupCompletion( const QString &completion )
184 {
185  mParent->setText( mPreviousAddresses + completion );
186  mParent->cursorAtEnd();
187 }
188 
189 bool AddressLineEdit::Private::sAddressesDirty = false;
190 
191 AddressLineEdit::AddressLineEdit( QWidget *parent, bool useCompletion )
192  : KLineEdit( parent ), d( new Private( this ) )
193 {
194  d->mUseCompletion = useCompletion;
195 
196  // Whenever a new AddressLineEdit is created (== a new composer is created),
197  // we set a dirty flag to reload the addresses upon the first completion.
198  // The address completions are shared between all AddressLineEdits.
199  // Is there a signal that tells us about addressbook updates?
200  if ( d->mUseCompletion ) {
201  d->sAddressesDirty = true;
202  }
203 }
204 
205 //-----------------------------------------------------------------------------
206 AddressLineEdit::~AddressLineEdit()
207 {
208  delete d;
209 }
210 
211 //-----------------------------------------------------------------------------
212 
213 void AddressLineEdit::setFont( const QFont &font )
214 {
215  KLineEdit::setFont( font );
216  if ( d->mUseCompletion ) {
217  completionBox()->setFont( font );
218  }
219 }
220 
221 //-----------------------------------------------------------------------------
222 void AddressLineEdit::keyPressEvent( QKeyEvent *event )
223 {
224  bool accept = false;
225 
226  if ( KStandardShortcut::shortcut( KStandardShortcut::SubstringCompletion ).
227  contains( event->key() | event->modifiers() ) ) {
228  doCompletion( true );
229  accept = true;
230  } else if ( KStandardShortcut::shortcut( KStandardShortcut::TextCompletion ).
231  contains( event->key() | event->modifiers() ) ) {
232  int len = text().length();
233 
234  if ( len == cursorPosition() ) { // at End?
235  doCompletion( true );
236  accept = true;
237  }
238  }
239 
240  if ( !accept ) {
241  KLineEdit::keyPressEvent( event );
242  }
243 }
244 
245 void AddressLineEdit::mouseReleaseEvent( QMouseEvent *event )
246 {
247  if ( d->mUseCompletion && ( event->button() == Qt::MidButton ) ) {
248  d->mSmartPaste = true;
249  KLineEdit::mouseReleaseEvent( event );
250  d->mSmartPaste = false;
251  return;
252  }
253 
254  KLineEdit::mouseReleaseEvent( event );
255 }
256 
257 void AddressLineEdit::insert( const QString &oldText )
258 {
259  if ( !d->mSmartPaste ) {
260  KLineEdit::insert( oldText );
261  return;
262  }
263 
264  QString newText = oldText.trimmed();
265  if ( newText.isEmpty() ) {
266  return;
267  }
268 
269  // remove newlines in the to-be-pasted string as well as an eventual
270  // mailto: protocol
271  newText.replace( QRegExp( QLatin1String( "\r?\n" ) ), QLatin1String( ", " ) );
272  if ( newText.startsWith( QLatin1String( "mailto:" ) ) ) {
273  KUrl u( newText );
274  newText = u.path();
275  } else if ( newText.indexOf( QLatin1String( " at " ) ) != -1 ) {
276  // Anti-spam stuff
277  newText.replace( QLatin1String( " at " ), QLatin1String( "@" ) );
278  newText.replace( QLatin1String( " dot " ), QLatin1String( "." ) );
279  } else if ( newText.indexOf( QLatin1String( "(at)" ) ) != -1 ) {
280  newText.replace( QRegExp( QLatin1String( "\\s*\\(at\\)\\s*" ) ), QLatin1String( "@" ) );
281  }
282 
283  QString contents = text();
284  int start_sel = 0;
285  int end_sel = 0;
286  int pos = cursorPosition();
287  if ( !selectedText().isEmpty() ) {
288  // Cut away the selection.
289  if ( pos > end_sel ) {
290  pos -= ( end_sel - start_sel );
291  } else if ( pos > start_sel ) {
292  pos = start_sel;
293  }
294  contents = contents.left( start_sel ) + contents.right( end_sel + 1 );
295  }
296 
297  int eot = contents.length();
298  while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) {
299  eot--;
300  }
301 
302  if ( eot == 0 ) {
303  contents.clear();
304  } else if ( pos >= eot ) {
305  if ( contents[ eot - 1 ] == QLatin1Char( ',' ) ) {
306  eot--;
307  }
308  contents.truncate( eot );
309  contents += QLatin1String( ", " );
310  pos = eot+2;
311  }
312 
313  contents = contents.left( pos ) + newText + contents.mid( pos );
314  setText( contents );
315  setCursorPosition( pos + newText.length() );
316 }
317 
318 void AddressLineEdit::paste()
319 {
320  if ( d->mUseCompletion ) {
321  d->mSmartPaste = true;
322  }
323 
324  KLineEdit::paste();
325  d->mSmartPaste = false;
326 }
327 
328 //-----------------------------------------------------------------------------
329 void AddressLineEdit::cursorAtEnd()
330 {
331  setCursorPosition( text().length() );
332 }
333 
334 //-----------------------------------------------------------------------------
335 void AddressLineEdit::enableCompletion( bool enable )
336 {
337  d->mUseCompletion = enable;
338 }
339 
340 //-----------------------------------------------------------------------------
341 void AddressLineEdit::doCompletion( bool ctrlT )
342 {
343  if ( !d->mUseCompletion ) {
344  return;
345  }
346 
347  QString prevAddr;
348 
349  QString s( text() );
350  int n = s.lastIndexOf( QLatin1Char( ',' ) );
351 
352  if ( n >= 0 ) {
353  n++; // Go past the ","
354 
355  int len = s.length();
356 
357  // Increment past any whitespace...
358  while ( n < len && s[ n ].isSpace() ) {
359  n++;
360  }
361 
362  prevAddr = s.left( n );
363  s = s.mid( n, 255 ).trimmed();
364  }
365 
366  if ( d->sAddressesDirty ) {
367  loadAddresses();
368  }
369 
370  if ( ctrlT ) {
371  QStringList completions = sCompletion->substringCompletion( s );
372  if ( completions.count() > 1 ) {
373  d->mPreviousAddresses = prevAddr;
374  setCompletedItems( completions );
375  } else if ( completions.count() == 1 ) {
376  setText( prevAddr + completions.first() );
377  }
378 
379  cursorAtEnd();
380  return;
381  }
382 
383  KGlobalSettings::Completion mode = completionMode();
384 
385  switch ( mode ) {
386  case KGlobalSettings::CompletionPopupAuto:
387  {
388  if ( s.isEmpty() ) {
389  break;
390  }
391  }
392  case KGlobalSettings::CompletionPopup:
393  {
394  d->mPreviousAddresses = prevAddr;
395  QStringList items = sCompletion->allMatches( s );
396  items += sCompletion->allMatches( QLatin1String( "\"" ) + s );
397  items += sCompletion->substringCompletion( QLatin1Char( '<' ) + s );
398  int beforeDollarCompletionCount = items.count();
399 
400  if ( s.indexOf( QLatin1Char( ' ' ) ) == -1 ) { // one word, possibly given name
401  items += sCompletion->allMatches( QLatin1String( "$$" ) + s );
402  }
403 
404  if ( !items.isEmpty() ) {
405  if ( items.count() > beforeDollarCompletionCount ) {
406  // remove the '$$whatever$' part
407  for ( QStringList::Iterator it = items.begin();
408  it != items.end(); ++it ) {
409  int pos = (*it).indexOf( QLatin1Char( '$' ), 2 );
410  if ( pos < 0 ) { // ???
411  continue;
412  }
413  (*it) = (*it).mid( pos + 1 );
414  }
415  }
416 
417  items = d->removeMailDupes( items );
418 
419  // We do not want KLineEdit::setCompletedItems to perform text
420  // completion (suggestion) since it does not know how to deal
421  // with providing proper completions for different items on the
422  // same line, e.g. comma-separated list of email addresses.
423  bool autoSuggest = ( mode != KGlobalSettings::CompletionPopupAuto );
424  setCompletedItems( items, autoSuggest );
425 
426  if ( !autoSuggest ) {
427  int index = items.first().indexOf( s );
428  QString newText = prevAddr + items.first().mid( index );
429  //kDebug() << "OLD TEXT:" << text();
430  //kDebug() << "NEW TEXT:" << newText;
431  setUserSelection( false );
432  setCompletedText( newText, true );
433  }
434  }
435 
436  break;
437  }
438 
439  case KGlobalSettings::CompletionShell:
440  {
441  QString match = sCompletion->makeCompletion( s );
442  if ( !match.isNull() && match != s ) {
443  setText( prevAddr + match );
444  cursorAtEnd();
445  }
446  break;
447  }
448 
449  case KGlobalSettings::CompletionMan: // Short-Auto in fact
450  case KGlobalSettings::CompletionAuto:
451  {
452  if ( !s.isEmpty() ) {
453  QString match = sCompletion->makeCompletion( s );
454  if ( !match.isNull() && match != s ) {
455  setCompletedText( prevAddr + match );
456  }
457 
458  break;
459  }
460  }
461  case KGlobalSettings::CompletionNone:
462  default: // fall through
463  break;
464  }
465 }
466 
467 //-----------------------------------------------------------------------------
468 void AddressLineEdit::loadAddresses()
469 {
470  sCompletion->clear();
471  d->sAddressesDirty = false;
472 
473  const QStringList addrs = d->addresses();
474  for ( QStringList::ConstIterator it = addrs.begin(); it != addrs.end(); ++it ) {
475  addAddress( *it );
476  }
477 }
478 
479 void AddressLineEdit::addAddress( const QString &addr )
480 {
481  sCompletion->addItem( addr );
482 
483  int pos = addr.indexOf( QLatin1Char( '<' ) );
484  if ( pos >= 0 ) {
485  ++pos;
486  int pos2 = addr.indexOf( QLatin1Char( '>' ), pos );
487  if ( pos2 >= 0 ) {
488  sCompletion->addItem( addr.mid( pos, pos2 - pos ) );
489  }
490  }
491 }
492 
493 //-----------------------------------------------------------------------------
494 void AddressLineEdit::dropEvent( QDropEvent *event )
495 {
496  const KUrl::List uriList = KUrl::List::fromMimeData( event->mimeData() );
497  if ( !uriList.isEmpty() ) {
498  QString ct = text();
499  KUrl::List::ConstIterator it = uriList.begin();
500  for ( ; it != uriList.end(); ++it ) {
501  if ( !ct.isEmpty() ) {
502  ct.append( QLatin1String( ", " ) );
503  }
504 
505  KUrl u( *it );
506  if ( (*it).protocol() == QLatin1String( "mailto" ) ) {
507  ct.append( (*it).path() );
508  } else {
509  ct.append( (*it).url() );
510  }
511  }
512  setText( ct );
513  setModified( true );
514  } else {
515  if ( d->mUseCompletion ) {
516  d->mSmartPaste = true;
517  }
518 
519  KLineEdit::dropEvent( event );
520  d->mSmartPaste = false;
521  }
522 }
523 
524 #include "addresslineedit.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue Dec 11 2012 12:16:18 by doxygen 1.8.1.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kabc

Skip menu "kabc"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.9.4 API Reference

Skip menu "kdepimlibs-4.9.4 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal