kmail

recipientseditor.cpp
1 /*
2  This file is part of KMail.
3 
4  Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20  As a special exception, permission is given to link this program
21  with any edition of TQt, and distribute the resulting executable,
22  without including the source code for TQt in the source distribution.
23 */
24 
25 #include "recipientseditor.h"
26 
27 #include "recipientspicker.h"
28 #include "twindowpositioner.h"
29 #include "distributionlistdialog.h"
30 #include "globalsettings.h"
31 
32 #include <libemailfunctions/email.h>
33 
34 #include <tdeapplication.h>
35 #include <tdecompletionbox.h>
36 #include <kdebug.h>
37 #include <kinputdialog.h>
38 #include <tdelocale.h>
39 #include <kiconloader.h>
40 #include <tdemessagebox.h>
41 
42 #include <tqlayout.h>
43 #include <tqlabel.h>
44 #include <tqscrollview.h>
45 #include <tqcombobox.h>
46 #include <tqhbox.h>
47 #include <tqtimer.h>
48 #include <tqpushbutton.h>
49 #include <tqstylesheet.h>
50 
51 Recipient::Recipient( const TQString &email, Recipient::Type type )
52  : mEmail( email ), mType( type )
53 {
54 }
55 
56 void Recipient::setType( Type type )
57 {
58  mType = type;
59 }
60 
61 Recipient::Type Recipient::type() const
62 {
63  return mType;
64 }
65 
66 void Recipient::setEmail( const TQString &email )
67 {
68  mEmail = email;
69 }
70 
71 TQString Recipient::email() const
72 {
73  return mEmail;
74 }
75 
76 bool Recipient::isEmpty() const
77 {
78  return mEmail.isEmpty();
79 }
80 
81 int Recipient::typeToId( Recipient::Type type )
82 {
83  return static_cast<int>( type );
84 }
85 
86 Recipient::Type Recipient::idToType( int id )
87 {
88  return static_cast<Type>( id );
89 }
90 
91 TQString Recipient::typeLabel() const
92 {
93  return typeLabel( mType );
94 }
95 
96 TQString Recipient::typeLabel( Recipient::Type type )
97 {
98  switch( type ) {
99  case To:
100  return i18n("To");
101  case Cc:
102  return i18n("CC");
103  case Bcc:
104  return i18n("BCC");
105  case Undefined:
106  break;
107  }
108 
109  return i18n("<Undefined RecipientType>");
110 }
111 
112 TQStringList Recipient::allTypeLabels()
113 {
114  TQStringList types;
115  types.append( typeLabel( To ) );
116  types.append( typeLabel( Cc ) );
117  types.append( typeLabel( Bcc ) );
118  return types;
119 }
120 
121 
122 RecipientComboBox::RecipientComboBox( TQWidget *parent )
123  : TQComboBox( parent )
124 {
125 }
126 
127 void RecipientComboBox::keyPressEvent( TQKeyEvent *ev )
128 {
129  if ( ev->key() == Key_Right ) emit rightPressed();
130  else TQComboBox::keyPressEvent( ev );
131 }
132 
133 
134 void RecipientLineEdit::keyPressEvent( TQKeyEvent *ev )
135 {
136  if ( ev->key() == Key_Backspace && text().isEmpty() ) {
137  ev->accept();
138  emit deleteMe();
139  } else if ( ev->key() == Key_Left && cursorPosition() == 0 ) {
140  emit leftPressed();
141  } else if ( ev->key() == Key_Right && cursorPosition() == (int)text().length() ) {
142  emit rightPressed();
143  } else {
144  KMLineEdit::keyPressEvent( ev );
145  }
146 }
147 
148 RecipientLine::RecipientLine( TQWidget *parent )
149  : TQWidget( parent ), mRecipientsCount( 0 ), mModified( false )
150 {
151  TQBoxLayout *topLayout = new TQHBoxLayout( this );
152  topLayout->setSpacing( KDialog::spacingHint() );
153 
154  TQStringList recipientTypes = Recipient::allTypeLabels();
155 
156  mCombo = new RecipientComboBox( this );
157  mCombo->insertStringList( recipientTypes );
158  topLayout->addWidget( mCombo );
159  TQToolTip::add( mCombo, i18n("Select type of recipient") );
160 
161  mEdit = new RecipientLineEdit( this );
162  TQToolTip::add( mEdit,
163  i18n( "Set the list of email addresses to receive this message" ) );
164  topLayout->addWidget( mEdit );
165  connect( mEdit, TQ_SIGNAL( returnPressed() ), TQ_SLOT( slotReturnPressed() ) );
166  connect( mEdit, TQ_SIGNAL( deleteMe() ), TQ_SLOT( slotPropagateDeletion() ) );
167  connect( mEdit, TQ_SIGNAL( textChanged( const TQString & ) ),
168  TQ_SLOT( analyzeLine( const TQString & ) ) );
169  connect( mEdit, TQ_SIGNAL( focusUp() ), TQ_SLOT( slotFocusUp() ) );
170  connect( mEdit, TQ_SIGNAL( focusDown() ), TQ_SLOT( slotFocusDown() ) );
171  connect( mEdit, TQ_SIGNAL( rightPressed() ), TQ_SIGNAL( rightPressed() ) );
172 
173  connect( mEdit, TQ_SIGNAL( leftPressed() ), mCombo, TQ_SLOT( setFocus() ) );
174  connect( mCombo, TQ_SIGNAL( rightPressed() ), mEdit, TQ_SLOT( setFocus() ) );
175 
176  connect( mCombo, TQ_SIGNAL( activated ( int ) ),
177  this, TQ_SLOT( slotTypeModified() ) );
178 
179  mRemoveButton = new TQPushButton( this );
180  mRemoveButton->setIconSet( TDEApplication::reverseLayout() ? SmallIconSet("locationbar_erase") : SmallIconSet( "clear_left" ) );
181  topLayout->addWidget( mRemoveButton );
182  connect( mRemoveButton, TQ_SIGNAL( clicked() ), TQ_SLOT( slotPropagateDeletion() ) );
183  TQToolTip::add( mRemoveButton, i18n("Remove recipient line") );
184 }
185 
186 void RecipientLine::slotFocusUp()
187 {
188  emit upPressed( this );
189 }
190 
191 void RecipientLine::slotFocusDown()
192 {
193  emit downPressed( this );
194 }
195 
196 void RecipientLine::slotTypeModified()
197 {
198  mModified = true;
199 
200  emit typeModified( this );
201 }
202 
203 void RecipientLine::analyzeLine( const TQString &text )
204 {
205  TQStringList r = KPIM::splitEmailAddrList( text );
206  if ( int( r.count() ) != mRecipientsCount ) {
207  mRecipientsCount = r.count();
208  emit countChanged();
209  }
210 }
211 
212 int RecipientLine::recipientsCount()
213 {
214  return mRecipientsCount;
215 }
216 
217 void RecipientLine::setRecipient( const Recipient &rec )
218 {
219  mEdit->setText( rec.email() );
220  mCombo->setCurrentItem( Recipient::typeToId( rec.type() ) );
221 }
222 
223 void RecipientLine::setRecipient( const TQString &email )
224 {
225  setRecipient( Recipient( email ) );
226 }
227 
228 Recipient RecipientLine::recipient() const
229 {
230  return Recipient( mEdit->text(),
231  Recipient::idToType( mCombo->currentItem() ) );
232 }
233 
234 void RecipientLine::setRecipientType( Recipient::Type type )
235 {
236  mCombo->setCurrentItem( Recipient::typeToId( type ) );
237 }
238 
239 Recipient::Type RecipientLine::recipientType() const
240 {
241  return Recipient::idToType( mCombo->currentItem() );
242 }
243 
244 void RecipientLine::activate()
245 {
246  mEdit->setFocus();
247 }
248 
249 bool RecipientLine::isActive()
250 {
251  return mEdit->hasFocus();
252 }
253 
254 bool RecipientLine::isEmpty()
255 {
256  return mEdit->text().isEmpty();
257 }
258 
259 bool RecipientLine::isModified()
260 {
261  return mModified || mEdit->isModified();
262 }
263 
264 void RecipientLine::clearModified()
265 {
266  mModified = false;
267  mEdit->clearModified();
268 }
269 
270 void RecipientLine::slotReturnPressed()
271 {
272  emit returnPressed( this );
273 }
274 
275 void RecipientLine::slotPropagateDeletion()
276 {
277  emit deleteLine( this );
278 }
279 
280 void RecipientLine::keyPressEvent( TQKeyEvent *ev )
281 {
282  if ( ev->key() == Key_Up ) {
283  emit upPressed( this );
284  } else if ( ev->key() == Key_Down ) {
285  emit downPressed( this );
286  }
287 }
288 
289 int RecipientLine::setComboWidth( int w )
290 {
291  w = TQMAX( w, mCombo->sizeHint().width() );
292  mCombo->setFixedWidth( w );
293  mCombo->updateGeometry();
294  parentWidget()->updateGeometry();
295  return w;
296 }
297 
298 void RecipientLine::fixTabOrder( TQWidget *previous )
299 {
300  setTabOrder( previous, mCombo );
301  setTabOrder( mCombo, mEdit );
302  setTabOrder( mEdit, mRemoveButton );
303 }
304 
305 TQWidget *RecipientLine::tabOut() const
306 {
307  return mRemoveButton;
308 }
309 
310 void RecipientLine::clear()
311 {
312  mEdit->clear();
313 }
314 
315 void RecipientLine::setRemoveLineButtonEnabled( bool b )
316 {
317  mRemoveButton->setEnabled( b );
318 }
319 
320 
321 // ------------ RecipientsView ---------------------
322 
323 RecipientsView::RecipientsView( TQWidget *parent )
324  : TQScrollView( parent ), mCurDelLine( 0 ),
325  mLineHeight( 0 ), mFirstColumnWidth( 0 ),
326  mModified( false )
327 {
328  mCompletionMode = TDEGlobalSettings::completionMode();
329  setHScrollBarMode( AlwaysOff );
330  setLineWidth( 0 );
331 
332  addLine();
333  setResizePolicy( TQScrollView::Manual );
334  setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding );
335 
336  viewport()->setPaletteBackgroundColor( paletteBackgroundColor() );
337 }
338 
339 RecipientLine *RecipientsView::activeLine()
340 {
341  return mLines.last();
342 }
343 
344 RecipientLine *RecipientsView::emptyLine()
345 {
346  RecipientLine *line;
347  for( line = mLines.first(); line; line = mLines.next() ) {
348  if ( line->isEmpty() ) return line;
349  }
350 
351  return 0;
352 }
353 
354 RecipientLine *RecipientsView::addLine()
355 {
356  RecipientLine *line = new RecipientLine( viewport() );
357  addChild( line, 0, mLines.count() * mLineHeight );
358  line->mEdit->setCompletionMode( mCompletionMode );
359  line->show();
360  connect( line, TQ_SIGNAL( returnPressed( RecipientLine * ) ),
361  TQ_SLOT( slotReturnPressed( RecipientLine * ) ) );
362  connect( line, TQ_SIGNAL( upPressed( RecipientLine * ) ),
363  TQ_SLOT( slotUpPressed( RecipientLine * ) ) );
364  connect( line, TQ_SIGNAL( downPressed( RecipientLine * ) ),
365  TQ_SLOT( slotDownPressed( RecipientLine * ) ) );
366  connect( line, TQ_SIGNAL( rightPressed() ), TQ_SIGNAL( focusRight() ) );
367  connect( line, TQ_SIGNAL( deleteLine( RecipientLine * ) ),
368  TQ_SLOT( slotDecideLineDeletion( RecipientLine * ) ) );
369  connect( line, TQ_SIGNAL( countChanged() ), TQ_SLOT( calculateTotal() ) );
370  connect( line, TQ_SIGNAL( typeModified( RecipientLine * ) ),
371  TQ_SLOT( slotTypeModified( RecipientLine * ) ) );
372  connect( line->mEdit, TQ_SIGNAL( completionModeChanged( TDEGlobalSettings::Completion ) ),
373  TQ_SLOT( setCompletionMode( TDEGlobalSettings::Completion ) ) );
374 
375  if ( mLines.last() ) {
376  if ( mLines.count() == 1 ) {
377  if ( GlobalSettings::self()->secondRecipientTypeDefault() ==
378  GlobalSettings::EnumSecondRecipientTypeDefault::To ) {
379  line->setRecipientType( Recipient::To );
380  } else {
381  if ( mLines.last()->recipientType() == Recipient::Bcc ) {
382  line->setRecipientType( Recipient::To );
383  } else {
384  line->setRecipientType( Recipient::Cc );
385  }
386  }
387  } else {
388  line->setRecipientType( mLines.last()->recipientType() );
389  }
390  line->fixTabOrder( mLines.last()->tabOut() );
391  }
392 
393  mLines.append( line );
394  // If there is only one line, removing it makes no sense
395  if ( mLines.count() == 1 ) {
396  mLines.first()->setRemoveLineButtonEnabled( false );
397  } else {
398  mLines.first()->setRemoveLineButtonEnabled( true );
399  }
400 
401  mFirstColumnWidth = line->setComboWidth( mFirstColumnWidth );
402 
403  mLineHeight = line->minimumSizeHint().height();
404 
405  line->resize( viewport()->width(), mLineHeight );
406 
407  resizeView();
408 
409  calculateTotal();
410 
411  ensureVisible( 0, mLines.count() * mLineHeight );
412 
413  return line;
414 }
415 
416 void RecipientsView::slotTypeModified( RecipientLine *line )
417 {
418  if ( mLines.count() == 2 ||
419  ( mLines.count() == 3 && mLines.at( 2 )->isEmpty() ) ) {
420  if ( mLines.at( 1 ) == line ) {
421  if ( line->recipientType() == Recipient::To ) {
422  GlobalSettings::self()->setSecondRecipientTypeDefault(
423  GlobalSettings::EnumSecondRecipientTypeDefault::To );
424  } else if ( line->recipientType() == Recipient::Cc ) {
425  GlobalSettings::self()->setSecondRecipientTypeDefault(
426  GlobalSettings::EnumSecondRecipientTypeDefault::Cc );
427  }
428  }
429  }
430 }
431 
432 void RecipientsView::calculateTotal()
433 {
434  int count = 0;
435  int empty = 0;
436 
437  RecipientLine *line;
438  for( line = mLines.first(); line; line = mLines.next() ) {
439  if ( line->isEmpty() ) ++empty;
440  else count += line->recipientsCount();
441  }
442 
443  if ( empty == 0 ) addLine();
444 
445  emit totalChanged( count, mLines.count() );
446 }
447 
448 void RecipientsView::slotReturnPressed( RecipientLine *line )
449 {
450  if ( !line->recipient().isEmpty() ) {
451  RecipientLine *empty = emptyLine();
452  if ( !empty ) empty = addLine();
453  activateLine( empty );
454  }
455 }
456 
457 void RecipientsView::slotDownPressed( RecipientLine *line )
458 {
459  int pos = mLines.find( line );
460  if ( pos >= (int)mLines.count() - 1 ) {
461  emit focusDown();
462  } else if ( pos >= 0 ) {
463  activateLine( mLines.at( pos + 1 ) );
464  }
465 }
466 
467 void RecipientsView::slotUpPressed( RecipientLine *line )
468 {
469  int pos = mLines.find( line );
470  if ( pos > 0 ) {
471  activateLine( mLines.at( pos - 1 ) );
472  } else {
473  emit focusUp();
474  }
475 }
476 
477 void RecipientsView::slotDecideLineDeletion( RecipientLine *line )
478 {
479  if ( !line->isEmpty() )
480  mModified = true;
481  if ( mLines.count() == 1 ) {
482  line->clear();
483  } else {
484  mCurDelLine = line;
485  TQTimer::singleShot( 0, this, TQ_SLOT( slotDeleteLine( ) ) );
486  }
487 }
488 
489 void RecipientsView::slotDeleteLine()
490 {
491  if ( !mCurDelLine )
492  return;
493 
494  RecipientLine *line = mCurDelLine;
495  int pos = mLines.find( line );
496 
497  int newPos;
498  if ( pos == 0 ) newPos = pos + 1;
499  else newPos = pos - 1;
500 
501  // if there is something left to activate, do so
502  if ( mLines.at( newPos ) )
503  mLines.at( newPos )->activate();
504 
505  mLines.remove( line );
506  removeChild( line );
507  delete line;
508 
509  bool atLeastOneToLine = false;
510  unsigned int firstCC = 0;
511  for( uint i = pos; i < mLines.count(); ++i ) {
512  RecipientLine *line = mLines.at( i );
513  moveChild( line, childX( line ), childY( line ) - mLineHeight );
514  if ( line->recipientType() == Recipient::To )
515  atLeastOneToLine = true;
516  else if ( ( line->recipientType() == Recipient::Cc ) && ( i == 0 ) )
517  firstCC = i;
518  }
519  // only one left, can't remove that one
520  if ( mLines.count() == 1 )
521  mLines.first()->setRemoveLineButtonEnabled( false );
522 
523  if ( !atLeastOneToLine )
524  mLines.at( firstCC )->setRecipientType( Recipient::To );
525 
526  calculateTotal();
527 
528  resizeView();
529 }
530 
531 void RecipientsView::resizeView()
532 {
533  resizeContents( width(), mLines.count() * mLineHeight );
534 
535  if ( mLines.count() < 6 ) {
536 // setFixedHeight( mLineHeight * mLines.count() );
537  }
538 
539  parentWidget()->layout()->activate();
540  emit sizeHintChanged();
541  TQTimer::singleShot( 0, this, TQ_SLOT(moveCompletionPopup()) );
542 }
543 
544 void RecipientsView::activateLine( RecipientLine *line )
545 {
546  line->activate();
547  ensureVisible( 0, childY( line ) );
548 }
549 
550 void RecipientsView::viewportResizeEvent ( TQResizeEvent *ev )
551 {
552  for( uint i = 0; i < mLines.count(); ++i ) {
553  mLines.at( i )->resize( ev->size().width(), mLineHeight );
554  }
555  ensureVisible( 0, mLines.count() * mLineHeight );
556 }
557 
558 TQSize RecipientsView::sizeHint() const
559 {
560  return TQSize( 200, mLineHeight * mLines.count() );
561 }
562 
563 TQSize RecipientsView::minimumSizeHint() const
564 {
565  int height;
566  uint numLines = 5;
567  if ( mLines.count() < numLines ) height = mLineHeight * mLines.count();
568  else height = mLineHeight * numLines;
569  return TQSize( 200, height );
570 }
571 
572 Recipient::List RecipientsView::recipients() const
573 {
574  Recipient::List recipients;
575 
576  TQPtrListIterator<RecipientLine> it( mLines );
577  RecipientLine *line;
578  while( ( line = it.current() ) ) {
579  if ( !line->recipient().isEmpty() ) {
580  recipients.append( line->recipient() );
581  }
582 
583  ++it;
584  }
585 
586  return recipients;
587 }
588 
589 void RecipientsView::setCompletionMode ( TDEGlobalSettings::Completion mode )
590 {
591  if ( mCompletionMode == mode )
592  return;
593  mCompletionMode = mode;
594 
595  TQPtrListIterator<RecipientLine> it( mLines );
596  RecipientLine *line;
597  while( ( line = it.current() ) ) {
598  line->mEdit->blockSignals( true );
599  line->mEdit->setCompletionMode( mode );
600  line->mEdit->blockSignals( false );
601  ++it;
602  }
603  emit completionModeChanged( mode ); //report change to RecipientsEditor
604 }
605 
606 void RecipientsView::removeRecipient( const TQString & recipient,
607  Recipient::Type type )
608 {
609  // search a line which matches recipient and type
610  TQPtrListIterator<RecipientLine> it( mLines );
611  RecipientLine *line;
612  while( ( line = it.current() ) ) {
613  if ( ( line->recipient().email() == recipient ) &&
614  ( line->recipientType() == type ) ) {
615  break;
616  }
617  ++it;
618  }
619  if ( line )
620  line->slotPropagateDeletion();
621 }
622 
623 bool RecipientsView::isModified()
624 {
625  if ( mModified )
626  return true;
627 
628  TQPtrListIterator<RecipientLine> it( mLines );
629  RecipientLine *line;
630  while( ( line = it.current() ) ) {
631  if ( line->isModified() ) {
632  return true;
633  }
634  ++it;
635  }
636 
637  return false;
638 }
639 
640 void RecipientsView::clearModified()
641 {
642  mModified = false;
643 
644  TQPtrListIterator<RecipientLine> it( mLines );
645  RecipientLine *line;
646  while( ( line = it.current() ) ) {
647  line->clearModified();
648  ++it;
649  }
650 }
651 
652 void RecipientsView::setFocus()
653 {
654  if ( mLines.last()->isActive() ) setFocusBottom();
655  else setFocusTop();
656 }
657 
658 void RecipientsView::setFocusTop()
659 {
660  RecipientLine *line = mLines.first();
661  if ( line ) line->activate();
662  else kdWarning() << "No first" << endl;
663 }
664 
665 void RecipientsView::setFocusBottom()
666 {
667  RecipientLine *line = mLines.last();
668  if ( line ) line->activate();
669  else kdWarning() << "No last" << endl;
670 }
671 
672 int RecipientsView::setFirstColumnWidth( int w )
673 {
674  mFirstColumnWidth = w;
675 
676  TQPtrListIterator<RecipientLine> it( mLines );
677  RecipientLine *line;
678  while( ( line = it.current() ) ) {
679  mFirstColumnWidth = line->setComboWidth( mFirstColumnWidth );
680  ++it;
681  }
682 
683  resizeView();
684  return mFirstColumnWidth;
685 }
686 
687 void RecipientsView::moveCompletionPopup()
688 {
689  for( RecipientLine* line = mLines.first(); line; line = mLines.next() ) {
690  if ( line->lineEdit()->completionBox( false ) ) {
691  if ( line->lineEdit()->completionBox()->isVisible() ) {
692  // ### trigger moving, is there a nicer way to do that?
693  line->lineEdit()->completionBox()->hide();
694  line->lineEdit()->completionBox()->show();
695  }
696  }
697  }
698 
699 }
700 
701 RecipientsToolTip::RecipientsToolTip( RecipientsView *view, TQWidget *parent )
702  : TQToolTip( parent ), mView( view )
703 {
704 }
705 
706 TQString RecipientsToolTip::line( const Recipient &r )
707 {
708  TQString txt = r.email();
709 
710  return "&nbsp;&nbsp;" + TQStyleSheet::escape( txt ) + "<br/>";
711 }
712 
713 void RecipientsToolTip::maybeTip( const TQPoint & p )
714 {
715  TQString text = "<qt>";
716 
717  TQString to;
718  TQString cc;
719  TQString bcc;
720 
721  Recipient::List recipients = mView->recipients();
722  Recipient::List::ConstIterator it;
723  for( it = recipients.begin(); it != recipients.end(); ++it ) {
724  switch( (*it).type() ) {
725  case Recipient::To:
726  to += line( *it );
727  break;
728  case Recipient::Cc:
729  cc += line( *it );
730  break;
731  case Recipient::Bcc:
732  bcc += line( *it );
733  break;
734  default:
735  break;
736  }
737  }
738 
739  text += i18n("<b>To:</b><br/>") + to;
740  if ( !cc.isEmpty() ) text += i18n("<b>CC:</b><br/>") + cc;
741  if ( !bcc.isEmpty() ) text += i18n("<b>BCC:</b><br/>") + bcc;
742 
743  text.append( "</qt>" );
744 
745  TQRect geometry( p + TQPoint( 2, 2 ), TQPoint( 400, 100 ) );
746 
747  tip( TQRect( p.x() - 20, p.y() - 20, 40, 40 ), text, geometry );
748 }
749 
750 
751 SideWidget::SideWidget( RecipientsView *view, TQWidget *parent )
752  : TQWidget( parent ), mView( view ), mRecipientPicker( 0 )
753 {
754  TQBoxLayout *topLayout = new TQVBoxLayout( this );
755 
756  topLayout->setSpacing( KDialog::spacingHint() );
757  topLayout->addStretch( 1 );
758 
759  mTotalLabel = new TQLabel( this );
760  mTotalLabel->setAlignment( AlignCenter );
761  topLayout->addWidget( mTotalLabel );
762  mTotalLabel->hide();
763 
764  topLayout->addStretch( 1 );
765 
766  new RecipientsToolTip( view, mTotalLabel );
767 
768  mDistributionListButton = new TQPushButton( i18n("Save List..."), this );
769  topLayout->addWidget( mDistributionListButton );
770  mDistributionListButton->hide();
771  connect( mDistributionListButton, TQ_SIGNAL( clicked() ),
772  TQ_SIGNAL( saveDistributionList() ) );
773  TQToolTip::add( mDistributionListButton,
774  i18n("Save recipients as distribution list") );
775 
776  mSelectButton = new TQPushButton( i18n("Se&lect..."), this );
777  topLayout->addWidget( mSelectButton );
778  connect( mSelectButton, TQ_SIGNAL( clicked() ), TQ_SLOT( pickRecipient() ) );
779  TQToolTip::add( mSelectButton, i18n("Select recipients from address book") );
780 }
781 
782 SideWidget::~SideWidget()
783 {
784 }
785 
786 RecipientsPicker* SideWidget::picker() const
787 {
788  if ( !mRecipientPicker ) {
789  // hacks to allow picker() to be const in the presence of lazy loading
790  SideWidget *non_const_this = const_cast<SideWidget*>( this );
791  mRecipientPicker = new RecipientsPicker( non_const_this );
792  connect( mRecipientPicker, TQ_SIGNAL( pickedRecipient( const Recipient & ) ),
793  non_const_this, TQ_SIGNAL( pickedRecipient( const Recipient & ) ) );
794  mPickerPositioner = new KWindowPositioner( non_const_this, mRecipientPicker );
795  }
796  return mRecipientPicker;
797 }
798 
799 void SideWidget::setFocus()
800 {
801  mSelectButton->setFocus();
802 }
803 
804 void SideWidget::setTotal( int recipients, int lines )
805 {
806 #if 0
807  kdDebug() << "SideWidget::setTotal() recipients: " << recipients <<
808  " lines: " << lines << endl;
809 #endif
810 
811  TQString labelText;
812  if ( recipients == 0 ) labelText = i18n("No recipients");
813  else labelText = i18n("1 recipient","%n recipients", recipients );
814  mTotalLabel->setText( labelText );
815 
816  if ( lines > 3 ) mTotalLabel->show();
817  else mTotalLabel->hide();
818 
819  if ( lines > 2 ) mDistributionListButton->show();
820  else mDistributionListButton->hide();
821 }
822 
823 void SideWidget::pickRecipient()
824 {
825 #if 0
826  TQString rec = KInputDialog::getText( "Pick Recipient",
827  "Email address of recipient" );
828  if ( !rec.isEmpty() ) emit pickedRecipient( rec );
829 #else
830  RecipientsPicker *p = picker();
831  p->setDefaultType( mView->activeLine()->recipientType() );
832  p->setRecipients( mView->recipients() );
833  p->show();
834  mPickerPositioner->reposition();
835  p->raise();
836 #endif
837 }
838 
839 
840 RecipientsEditor::RecipientsEditor( TQWidget *parent )
841  : TQWidget( parent ), mModified( false )
842 {
843  TQBoxLayout *topLayout = new TQHBoxLayout( this );
844  topLayout->setSpacing( KDialog::spacingHint() );
845 
846  mRecipientsView = new RecipientsView( this );
847  topLayout->addWidget( mRecipientsView );
848  connect( mRecipientsView, TQ_SIGNAL( focusUp() ), TQ_SIGNAL( focusUp() ) );
849  connect( mRecipientsView, TQ_SIGNAL( focusDown() ), TQ_SIGNAL( focusDown() ) );
850  connect( mRecipientsView, TQ_SIGNAL( completionModeChanged( TDEGlobalSettings::Completion ) ),
851  TQ_SIGNAL( completionModeChanged( TDEGlobalSettings::Completion ) ) );
852 
853  mSideWidget = new SideWidget( mRecipientsView, this );
854  topLayout->addWidget( mSideWidget );
855  connect( mSideWidget, TQ_SIGNAL( pickedRecipient( const Recipient & ) ),
856  TQ_SLOT( slotPickedRecipient( const Recipient & ) ) );
857  connect( mSideWidget, TQ_SIGNAL( saveDistributionList() ),
858  TQ_SLOT( saveDistributionList() ) );
859 
860  connect( mRecipientsView, TQ_SIGNAL( totalChanged( int, int ) ),
861  mSideWidget, TQ_SLOT( setTotal( int, int ) ) );
862  connect( mRecipientsView, TQ_SIGNAL( focusRight() ),
863  mSideWidget, TQ_SLOT( setFocus() ) );
864 
865  connect( mRecipientsView, TQ_SIGNAL(sizeHintChanged()),
866  TQ_SIGNAL(sizeHintChanged()) );
867 }
868 
869 RecipientsEditor::~RecipientsEditor()
870 {
871 }
872 
873 RecipientsPicker* RecipientsEditor::picker() const
874 {
875  return mSideWidget->picker();
876 }
877 
878 void RecipientsEditor::slotPickedRecipient( const Recipient &rec )
879 {
880  RecipientLine *line = mRecipientsView->activeLine();
881  if ( !line->isEmpty() ) line = mRecipientsView->addLine();
882 
883  Recipient r = rec;
884  if ( r.type() == Recipient::Undefined ) {
885  r.setType( line->recipientType() );
886  }
887 
888  line->setRecipient( r );
889  mModified = true;
890 }
891 
892 void RecipientsEditor::saveDistributionList()
893 {
894  DistributionListDialog *dlg = new DistributionListDialog( this );
895  dlg->setRecipients( mRecipientsView->recipients() );
896  dlg->exec();
897  delete dlg;
898 }
899 
900 Recipient::List RecipientsEditor::recipients() const
901 {
902  return mRecipientsView->recipients();
903 }
904 
905 void RecipientsEditor::setRecipientString( const TQString &str,
906  Recipient::Type type )
907 {
908  clear();
909 
910  int count = 1;
911 
912  TQStringList r = KPIM::splitEmailAddrList( str );
913  TQStringList::ConstIterator it;
914  for( it = r.begin(); it != r.end(); ++it ) {
915  if ( count++ > GlobalSettings::self()->maximumRecipients() ) {
916  KMessageBox::sorry( this,
917  i18n("Truncating recipients list to %1 of %2 entries.")
918  .arg( GlobalSettings::self()->maximumRecipients() )
919  .arg( r.count() ) );
920  break;
921  }
922  addRecipient( *it, type );
923  }
924 }
925 
926 TQString RecipientsEditor::recipientString( Recipient::Type type )
927 {
928  TQString str;
929 
930  Recipient::List recipients = mRecipientsView->recipients();
931  Recipient::List::ConstIterator it;
932  for( it = recipients.begin(); it != recipients.end(); ++it ) {
933  if ( (*it).type() == type ) {
934  if ( !str.isEmpty() ) str += ", ";
935  str.append( (*it).email() );
936  }
937  }
938 
939  return str;
940 }
941 
942 void RecipientsEditor::addRecipient( const TQString & recipient,
943  Recipient::Type type )
944 {
945  RecipientLine *line = mRecipientsView->emptyLine();
946  if ( !line ) line = mRecipientsView->addLine();
947  line->setRecipient( Recipient( recipient, type ) );
948 }
949 
950 void RecipientsEditor::removeRecipient( const TQString & recipient,
951  Recipient::Type type )
952 {
953  mRecipientsView->removeRecipient( recipient, type );
954 }
955 
956 bool RecipientsEditor::isModified()
957 {
958  return mModified || mRecipientsView->isModified();
959 }
960 
961 void RecipientsEditor::clearModified()
962 {
963  mModified = false;
964  mRecipientsView->clearModified();
965 }
966 
967 void RecipientsEditor::clear()
968 {
969 }
970 
971 void RecipientsEditor::setFocus()
972 {
973  mRecipientsView->setFocus();
974 }
975 
976 void RecipientsEditor::setFocusTop()
977 {
978  mRecipientsView->setFocusTop();
979 }
980 
981 void RecipientsEditor::setFocusBottom()
982 {
983  mRecipientsView->setFocusBottom();
984 }
985 
986 int RecipientsEditor::setFirstColumnWidth( int w )
987 {
988  return mRecipientsView->setFirstColumnWidth( w );
989 }
990 
991 void RecipientsEditor::selectRecipients()
992 {
993  mSideWidget->pickRecipient();
994 }
995 
996 void RecipientsEditor::setCompletionMode( TDEGlobalSettings::Completion mode )
997 {
998  mRecipientsView->setCompletionMode( mode );
999 }
1000 
1001 #include "recipientseditor.moc"