kmail

recipientseditor.cpp

00001 /*
00002     This file is part of KMail.
00003 
00004     Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 
00020     As a special exception, permission is given to link this program
00021     with any edition of Qt, and distribute the resulting executable,
00022     without including the source code for Qt in the source distribution.
00023 */
00024 
00025 #include "recipientseditor.h"
00026 
00027 #include "recipientspicker.h"
00028 #include "kwindowpositioner.h"
00029 #include "distributionlistdialog.h"
00030 #include "globalsettings.h"
00031 
00032 #include <libemailfunctions/email.h>
00033 
00034 #include <kapplication.h>
00035 #include <kcompletionbox.h>
00036 #include <kdebug.h>
00037 #include <kinputdialog.h>
00038 #include <klocale.h>
00039 #include <kiconloader.h>
00040 #include <kmessagebox.h>
00041 
00042 #include <qlayout.h>
00043 #include <qlabel.h>
00044 #include <qscrollview.h>
00045 #include <qcombobox.h>
00046 #include <qhbox.h>
00047 #include <qtimer.h>
00048 #include <qpushbutton.h>
00049 #include <qstylesheet.h>
00050 
00051 Recipient::Recipient( const QString &email, Recipient::Type type )
00052   : mEmail( email ), mType( type )
00053 {
00054 }
00055 
00056 void Recipient::setType( Type type )
00057 {
00058   mType = type;
00059 }
00060 
00061 Recipient::Type Recipient::type() const
00062 {
00063   return mType;
00064 }
00065 
00066 void Recipient::setEmail( const QString &email )
00067 {
00068   mEmail = email;
00069 }
00070 
00071 QString Recipient::email() const
00072 {
00073   return mEmail;
00074 }
00075 
00076 bool Recipient::isEmpty() const
00077 {
00078   return mEmail.isEmpty();
00079 }
00080 
00081 int Recipient::typeToId( Recipient::Type type )
00082 {
00083   return static_cast<int>( type );
00084 }
00085 
00086 Recipient::Type Recipient::idToType( int id )
00087 {
00088   return static_cast<Type>( id );
00089 }
00090 
00091 QString Recipient::typeLabel() const
00092 {
00093   return typeLabel( mType );
00094 }
00095 
00096 QString Recipient::typeLabel( Recipient::Type type )
00097 {
00098   switch( type ) {
00099     case To:
00100       return i18n("To");
00101     case Cc:
00102       return i18n("CC");
00103     case Bcc:
00104       return i18n("BCC");
00105     case Undefined:
00106       break;
00107   }
00108 
00109   return i18n("<Undefined RecipientType>");
00110 }
00111 
00112 QStringList Recipient::allTypeLabels()
00113 {
00114   QStringList types;
00115   types.append( typeLabel( To ) );
00116   types.append( typeLabel( Cc ) );
00117   types.append( typeLabel( Bcc ) );
00118   return types;
00119 }
00120 
00121 
00122 RecipientComboBox::RecipientComboBox( QWidget *parent )
00123   : QComboBox( parent )
00124 {
00125 }
00126 
00127 void RecipientComboBox::keyPressEvent( QKeyEvent *ev )
00128 {
00129   if ( ev->key() == Key_Right ) emit rightPressed();
00130   else QComboBox::keyPressEvent( ev );
00131 }
00132 
00133 
00134 void RecipientLineEdit::keyPressEvent( QKeyEvent *ev )
00135 {
00136   if ( ev->key() == Key_Backspace  &&  text().isEmpty() ) {
00137     ev->accept();
00138     emit deleteMe();
00139   } else if ( ev->key() == Key_Left && cursorPosition() == 0 ) {
00140     emit leftPressed();
00141   } else if ( ev->key() == Key_Right && cursorPosition() == (int)text().length() ) {
00142     emit rightPressed();
00143   } else {
00144     KMLineEdit::keyPressEvent( ev );
00145   }
00146 }
00147 
00148 RecipientLine::RecipientLine( QWidget *parent )
00149   : QWidget( parent ), mRecipientsCount( 0 ), mModified( false )
00150 {
00151   QBoxLayout *topLayout = new QHBoxLayout( this );
00152   topLayout->setSpacing( KDialog::spacingHint() );
00153 
00154   QStringList recipientTypes = Recipient::allTypeLabels();
00155 
00156   mCombo = new RecipientComboBox( this );
00157   mCombo->insertStringList( recipientTypes );
00158   topLayout->addWidget( mCombo );
00159   QToolTip::add( mCombo, i18n("Select type of recipient") );
00160 
00161   mEdit = new RecipientLineEdit( this );
00162   QToolTip::add( mEdit,
00163                  i18n( "Set the list of email addresses to receive this message" ) );
00164   topLayout->addWidget( mEdit );
00165   connect( mEdit, SIGNAL( returnPressed() ), SLOT( slotReturnPressed() ) );
00166   connect( mEdit, SIGNAL( deleteMe() ), SLOT( slotPropagateDeletion() ) );
00167   connect( mEdit, SIGNAL( textChanged( const QString & ) ),
00168     SLOT( analyzeLine( const QString & ) ) );
00169   connect( mEdit, SIGNAL( focusUp() ), SLOT( slotFocusUp() ) );
00170   connect( mEdit, SIGNAL( focusDown() ), SLOT( slotFocusDown() ) );
00171   connect( mEdit, SIGNAL( rightPressed() ), SIGNAL( rightPressed() ) );
00172 
00173   connect( mEdit, SIGNAL( leftPressed() ), mCombo, SLOT( setFocus() ) );
00174   connect( mCombo, SIGNAL( rightPressed() ), mEdit, SLOT( setFocus() ) );
00175 
00176   connect( mCombo, SIGNAL( activated ( int ) ),
00177            this, SLOT( slotTypeModified() ) );
00178 
00179   mRemoveButton = new QPushButton( this );
00180   mRemoveButton->setIconSet( KApplication::reverseLayout() ? SmallIconSet("locationbar_erase") : SmallIconSet( "clear_left" ) );
00181   topLayout->addWidget( mRemoveButton );
00182   connect( mRemoveButton, SIGNAL( clicked() ), SLOT( slotPropagateDeletion() ) );
00183   QToolTip::add( mRemoveButton, i18n("Remove recipient line") );
00184 }
00185 
00186 void RecipientLine::slotFocusUp()
00187 {
00188   emit upPressed( this );
00189 }
00190 
00191 void RecipientLine::slotFocusDown()
00192 {
00193   emit downPressed( this );
00194 }
00195 
00196 void RecipientLine::slotTypeModified()
00197 {
00198   mModified = true;
00199 
00200   emit typeModified( this );
00201 }
00202 
00203 void RecipientLine::analyzeLine( const QString &text )
00204 {
00205   QStringList r = KPIM::splitEmailAddrList( text );
00206   if ( int( r.count() ) != mRecipientsCount ) {
00207     mRecipientsCount = r.count();
00208     emit countChanged();
00209   }
00210 }
00211 
00212 int RecipientLine::recipientsCount()
00213 {
00214   return mRecipientsCount;
00215 }
00216 
00217 void RecipientLine::setRecipient( const Recipient &rec )
00218 {
00219   mEdit->setText( rec.email() );
00220   mCombo->setCurrentItem( Recipient::typeToId( rec.type() ) );
00221 }
00222 
00223 void RecipientLine::setRecipient( const QString &email )
00224 {
00225   setRecipient( Recipient( email ) );
00226 }
00227 
00228 Recipient RecipientLine::recipient() const
00229 {
00230   return Recipient( mEdit->text(),
00231     Recipient::idToType( mCombo->currentItem() ) );
00232 }
00233 
00234 void RecipientLine::setRecipientType( Recipient::Type type )
00235 {
00236   mCombo->setCurrentItem( Recipient::typeToId( type ) );
00237 }
00238 
00239 Recipient::Type RecipientLine::recipientType() const
00240 {
00241   return Recipient::idToType( mCombo->currentItem() );
00242 }
00243 
00244 void RecipientLine::activate()
00245 {
00246   mEdit->setFocus();
00247 }
00248 
00249 bool RecipientLine::isActive()
00250 {
00251   return mEdit->hasFocus();
00252 }
00253 
00254 bool RecipientLine::isEmpty()
00255 {
00256   return mEdit->text().isEmpty();
00257 }
00258 
00259 bool RecipientLine::isModified()
00260 {
00261   return mModified || mEdit->isModified();
00262 }
00263 
00264 void RecipientLine::clearModified()
00265 {
00266   mModified = false;
00267   mEdit->clearModified();
00268 }
00269 
00270 void RecipientLine::slotReturnPressed()
00271 {
00272   emit returnPressed( this );
00273 }
00274 
00275 void RecipientLine::slotPropagateDeletion()
00276 {
00277   emit deleteLine( this );
00278 }
00279 
00280 void RecipientLine::keyPressEvent( QKeyEvent *ev )
00281 {
00282   if ( ev->key() == Key_Up ) {
00283     emit upPressed( this );
00284   } else if ( ev->key() == Key_Down ) {
00285     emit downPressed( this );
00286   }
00287 }
00288 
00289 int RecipientLine::setComboWidth( int w )
00290 {
00291   w = QMAX( w, mCombo->sizeHint().width() );
00292   mCombo->setFixedWidth( w );
00293   mCombo->updateGeometry();
00294   parentWidget()->updateGeometry();
00295   return w;
00296 }
00297 
00298 void RecipientLine::fixTabOrder( QWidget *previous )
00299 {
00300   setTabOrder( previous, mCombo );
00301   setTabOrder( mCombo, mEdit );
00302   setTabOrder( mEdit, mRemoveButton );
00303 }
00304 
00305 QWidget *RecipientLine::tabOut() const
00306 {
00307   return mRemoveButton;
00308 }
00309 
00310 void RecipientLine::clear()
00311 {
00312   mEdit->clear();
00313 }
00314 
00315 void RecipientLine::setRemoveLineButtonEnabled( bool b )
00316 {
00317   mRemoveButton->setEnabled( b );
00318 }
00319 
00320 
00321 // ------------ RecipientsView ---------------------
00322 
00323 RecipientsView::RecipientsView( QWidget *parent )
00324   : QScrollView( parent ), mCurDelLine( 0 ),
00325     mLineHeight( 0 ), mFirstColumnWidth( 0 ),
00326     mModified( false )
00327 {
00328   mCompletionMode = KGlobalSettings::completionMode();
00329   setHScrollBarMode( AlwaysOff );
00330   setLineWidth( 0 );
00331 
00332   addLine();
00333   setResizePolicy( QScrollView::Manual );
00334   setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
00335 
00336   viewport()->setPaletteBackgroundColor( paletteBackgroundColor() );
00337 }
00338 
00339 RecipientLine *RecipientsView::activeLine()
00340 {
00341   return mLines.last();
00342 }
00343 
00344 RecipientLine *RecipientsView::emptyLine()
00345 {
00346   RecipientLine *line;
00347   for( line = mLines.first(); line; line = mLines.next() ) {
00348     if ( line->isEmpty() ) return line;
00349   }
00350 
00351   return 0;
00352 }
00353 
00354 RecipientLine *RecipientsView::addLine()
00355 {
00356   RecipientLine *line = new RecipientLine( viewport() );
00357   addChild( line, 0, mLines.count() * mLineHeight );
00358   line->mEdit->setCompletionMode( mCompletionMode );
00359   line->show();
00360   connect( line, SIGNAL( returnPressed( RecipientLine * ) ),
00361     SLOT( slotReturnPressed( RecipientLine * ) ) );
00362   connect( line, SIGNAL( upPressed( RecipientLine * ) ),
00363     SLOT( slotUpPressed( RecipientLine * ) ) );
00364   connect( line, SIGNAL( downPressed( RecipientLine * ) ),
00365     SLOT( slotDownPressed( RecipientLine * ) ) );
00366   connect( line, SIGNAL( rightPressed() ), SIGNAL( focusRight() ) );
00367   connect( line, SIGNAL( deleteLine( RecipientLine * ) ),
00368     SLOT( slotDecideLineDeletion( RecipientLine * ) ) );
00369   connect( line, SIGNAL( countChanged() ), SLOT( calculateTotal() ) );
00370   connect( line, SIGNAL( typeModified( RecipientLine * ) ),
00371     SLOT( slotTypeModified( RecipientLine * ) ) );
00372   connect( line->mEdit, SIGNAL( completionModeChanged( KGlobalSettings::Completion ) ),
00373     SLOT( setCompletionMode( KGlobalSettings::Completion ) ) );
00374 
00375   if ( mLines.last() ) {
00376     if ( mLines.count() == 1 ) {
00377       if ( GlobalSettings::self()->secondRecipientTypeDefault() ==
00378          GlobalSettings::EnumSecondRecipientTypeDefault::To ) {
00379         line->setRecipientType( Recipient::To );
00380       } else {
00381         if ( mLines.last()->recipientType() == Recipient::Bcc ) {
00382           line->setRecipientType( Recipient::To );
00383         } else {
00384           line->setRecipientType( Recipient::Cc );
00385         }
00386       }
00387     } else {
00388       line->setRecipientType( mLines.last()->recipientType() );
00389     }
00390     line->fixTabOrder( mLines.last()->tabOut() );
00391   }
00392 
00393   mLines.append( line );
00394   // If there is only one line, removing it makes no sense
00395   if ( mLines.count() == 1 ) {
00396     mLines.first()->setRemoveLineButtonEnabled( false );
00397   } else {
00398     mLines.first()->setRemoveLineButtonEnabled( true );
00399   }
00400 
00401   mFirstColumnWidth = line->setComboWidth( mFirstColumnWidth );
00402 
00403   mLineHeight = line->minimumSizeHint().height();
00404 
00405   line->resize( viewport()->width(), mLineHeight );
00406 
00407   resizeView();
00408 
00409   calculateTotal();
00410 
00411   ensureVisible( 0, mLines.count() * mLineHeight );
00412 
00413   return line;
00414 }
00415 
00416 void RecipientsView::slotTypeModified( RecipientLine *line )
00417 {
00418   if ( mLines.count() == 2 ||
00419        ( mLines.count() == 3 && mLines.at( 2 )->isEmpty() ) ) {
00420     if ( mLines.at( 1 ) == line ) {
00421       if ( line->recipientType() == Recipient::To ) {
00422         GlobalSettings::self()->setSecondRecipientTypeDefault(
00423           GlobalSettings::EnumSecondRecipientTypeDefault::To );
00424       } else if ( line->recipientType() == Recipient::Cc ) {
00425         GlobalSettings::self()->setSecondRecipientTypeDefault(
00426           GlobalSettings::EnumSecondRecipientTypeDefault::Cc );
00427       }
00428     }
00429   }
00430 }
00431 
00432 void RecipientsView::calculateTotal()
00433 {
00434   int count = 0;
00435   int empty = 0;
00436 
00437   RecipientLine *line;
00438   for( line = mLines.first(); line; line = mLines.next() ) {
00439     if ( line->isEmpty() ) ++empty;
00440     else count += line->recipientsCount();
00441   }
00442 
00443   if ( empty == 0 ) addLine();
00444 
00445   emit totalChanged( count, mLines.count() );
00446 }
00447 
00448 void RecipientsView::slotReturnPressed( RecipientLine *line )
00449 {
00450   if ( !line->recipient().isEmpty() ) {
00451     RecipientLine *empty = emptyLine();
00452     if ( !empty ) empty = addLine();
00453     activateLine( empty );
00454   }
00455 }
00456 
00457 void RecipientsView::slotDownPressed( RecipientLine *line )
00458 {
00459   int pos = mLines.find( line );
00460   if ( pos >= (int)mLines.count() - 1 ) {
00461     emit focusDown();
00462   } else if ( pos >= 0 ) {
00463     activateLine( mLines.at( pos + 1 ) );
00464   }
00465 }
00466 
00467 void RecipientsView::slotUpPressed( RecipientLine *line )
00468 {
00469   int pos = mLines.find( line );
00470   if ( pos > 0 ) {
00471     activateLine( mLines.at( pos - 1 ) );
00472   } else {
00473     emit focusUp();
00474   }
00475 }
00476 
00477 void RecipientsView::slotDecideLineDeletion( RecipientLine *line )
00478 {
00479   if ( !line->isEmpty() )
00480     mModified = true;
00481   if ( mLines.count() == 1 ) {
00482     line->clear();
00483   } else {
00484     mCurDelLine = line;
00485     QTimer::singleShot( 0, this, SLOT( slotDeleteLine( ) ) );
00486   }
00487 }
00488 
00489 void RecipientsView::slotDeleteLine()
00490 {
00491   if ( !mCurDelLine )
00492     return;
00493 
00494   RecipientLine *line = mCurDelLine;
00495   int pos = mLines.find( line );
00496 
00497   int newPos;
00498   if ( pos == 0 ) newPos = pos + 1;
00499   else newPos = pos - 1;
00500 
00501   // if there is something left to activate, do so
00502   if ( mLines.at( newPos ) )
00503     mLines.at( newPos )->activate();
00504 
00505   mLines.remove( line );
00506   removeChild( line );
00507   delete line;
00508 
00509   bool atLeastOneToLine = false;
00510   unsigned int firstCC = 0;
00511   for( uint i = pos; i < mLines.count(); ++i ) {
00512     RecipientLine *line = mLines.at( i );
00513     moveChild( line, childX( line ), childY( line ) - mLineHeight );
00514     if ( line->recipientType() == Recipient::To )
00515       atLeastOneToLine = true;
00516     else if ( ( line->recipientType() == Recipient::Cc ) && ( i == 0 ) )
00517       firstCC = i;
00518   }
00519   // only one left, can't remove that one
00520   if ( mLines.count() == 1 )
00521     mLines.first()->setRemoveLineButtonEnabled( false );
00522 
00523   if ( !atLeastOneToLine )
00524     mLines.at( firstCC )->setRecipientType( Recipient::To );
00525 
00526   calculateTotal();
00527 
00528   resizeView();
00529 }
00530 
00531 void RecipientsView::resizeView()
00532 {
00533   resizeContents( width(), mLines.count() * mLineHeight );
00534 
00535   if ( mLines.count() < 6 ) {
00536 //    setFixedHeight( mLineHeight * mLines.count() );
00537   }
00538 
00539   parentWidget()->layout()->activate();
00540   emit sizeHintChanged();
00541   QTimer::singleShot( 0, this, SLOT(moveCompletionPopup()) );
00542 }
00543 
00544 void RecipientsView::activateLine( RecipientLine *line )
00545 {
00546   line->activate();
00547   ensureVisible( 0, childY( line ) );
00548 }
00549 
00550 void RecipientsView::viewportResizeEvent ( QResizeEvent *ev )
00551 {
00552   for( uint i = 0; i < mLines.count(); ++i ) {
00553     mLines.at( i )->resize( ev->size().width(), mLineHeight );
00554   }
00555   ensureVisible( 0, mLines.count() * mLineHeight );
00556 }
00557 
00558 QSize RecipientsView::sizeHint() const
00559 {
00560   return QSize( 200, mLineHeight * mLines.count() );
00561 }
00562 
00563 QSize RecipientsView::minimumSizeHint() const
00564 {
00565   int height;
00566   uint numLines = 5;
00567   if ( mLines.count() < numLines ) height = mLineHeight * mLines.count();
00568   else height = mLineHeight * numLines;
00569   return QSize( 200, height );
00570 }
00571 
00572 Recipient::List RecipientsView::recipients() const
00573 {
00574   Recipient::List recipients;
00575 
00576   QPtrListIterator<RecipientLine> it( mLines );
00577   RecipientLine *line;
00578   while( ( line = it.current() ) ) {
00579     if ( !line->recipient().isEmpty() ) {
00580       recipients.append( line->recipient() );
00581     }
00582 
00583     ++it;
00584   }
00585 
00586   return recipients;
00587 }
00588 
00589 void RecipientsView::setCompletionMode ( KGlobalSettings::Completion mode )
00590 {
00591   if ( mCompletionMode == mode )
00592     return;
00593   mCompletionMode = mode;
00594 
00595   QPtrListIterator<RecipientLine> it( mLines );
00596   RecipientLine *line;
00597   while( ( line = it.current() ) ) {
00598     line->mEdit->blockSignals( true );
00599     line->mEdit->setCompletionMode( mode );
00600     line->mEdit->blockSignals( false );
00601     ++it;
00602   }
00603   emit completionModeChanged( mode ); //report change to RecipientsEditor
00604 }
00605 
00606 void RecipientsView::removeRecipient( const QString & recipient,
00607                                       Recipient::Type type )
00608 {
00609   // search a line which matches recipient and type
00610   QPtrListIterator<RecipientLine> it( mLines );
00611   RecipientLine *line;
00612   while( ( line = it.current() ) ) {
00613     if ( ( line->recipient().email() == recipient ) &&
00614          ( line->recipientType() == type ) ) {
00615       break;
00616     }
00617     ++it;
00618   }
00619   if ( line )
00620     line->slotPropagateDeletion();
00621 }
00622 
00623 bool RecipientsView::isModified()
00624 {
00625   if ( mModified )
00626     return true;
00627 
00628   QPtrListIterator<RecipientLine> it( mLines );
00629   RecipientLine *line;
00630   while( ( line = it.current() ) ) {
00631     if ( line->isModified() ) {
00632       return true;
00633     }
00634     ++it;
00635   }
00636 
00637   return false;
00638 }
00639 
00640 void RecipientsView::clearModified()
00641 {
00642   mModified = false;
00643 
00644   QPtrListIterator<RecipientLine> it( mLines );
00645   RecipientLine *line;
00646   while( ( line = it.current() ) ) {
00647     line->clearModified();
00648     ++it;
00649   }
00650 }
00651 
00652 void RecipientsView::setFocus()
00653 {
00654   if ( mLines.last()->isActive() ) setFocusBottom();
00655   else setFocusTop();
00656 }
00657 
00658 void RecipientsView::setFocusTop()
00659 {
00660   RecipientLine *line = mLines.first();
00661   if ( line ) line->activate();
00662   else kdWarning() << "No first" << endl;
00663 }
00664 
00665 void RecipientsView::setFocusBottom()
00666 {
00667   RecipientLine *line = mLines.last();
00668   if ( line ) line->activate();
00669   else  kdWarning() << "No last" << endl;
00670 }
00671 
00672 int RecipientsView::setFirstColumnWidth( int w )
00673 {
00674   mFirstColumnWidth = w;
00675 
00676   QPtrListIterator<RecipientLine> it( mLines );
00677   RecipientLine *line;
00678   while( ( line = it.current() ) ) {
00679     mFirstColumnWidth = line->setComboWidth( mFirstColumnWidth );
00680     ++it;
00681   }
00682 
00683   resizeView();
00684   return mFirstColumnWidth;
00685 }
00686 
00687 void RecipientsView::moveCompletionPopup()
00688 {
00689   for( RecipientLine* line = mLines.first(); line; line = mLines.next() ) {
00690     if ( line->lineEdit()->completionBox( false ) ) {
00691       if ( line->lineEdit()->completionBox()->isVisible() ) {
00692         // ### trigger moving, is there a nicer way to do that?
00693         line->lineEdit()->completionBox()->hide();
00694         line->lineEdit()->completionBox()->show();
00695       }
00696     }
00697   }
00698 
00699 }
00700 
00701 RecipientsToolTip::RecipientsToolTip( RecipientsView *view, QWidget *parent )
00702   : QToolTip( parent ), mView( view )
00703 {
00704 }
00705 
00706 QString RecipientsToolTip::line( const Recipient &r )
00707 {
00708   QString txt = r.email();
00709 
00710   return "&nbsp;&nbsp;" + QStyleSheet::escape( txt ) + "<br/>";
00711 }
00712 
00713 void RecipientsToolTip::maybeTip( const QPoint & p )
00714 {
00715   QString text = "<qt>";
00716 
00717   QString to;
00718   QString cc;
00719   QString bcc;
00720 
00721   Recipient::List recipients = mView->recipients();
00722   Recipient::List::ConstIterator it;
00723   for( it = recipients.begin(); it != recipients.end(); ++it ) {
00724     switch( (*it).type() ) {
00725       case Recipient::To:
00726         to += line( *it );
00727         break;
00728       case Recipient::Cc:
00729         cc += line( *it );
00730         break;
00731       case Recipient::Bcc:
00732         bcc += line( *it );
00733         break;
00734       default:
00735         break;
00736     }
00737   }
00738 
00739   text += i18n("<b>To:</b><br/>") + to;
00740   if ( !cc.isEmpty() ) text += i18n("<b>CC:</b><br/>") + cc;
00741   if ( !bcc.isEmpty() ) text += i18n("<b>BCC:</b><br/>") + bcc;
00742 
00743   text.append( "</qt>" );
00744 
00745   QRect geometry( p + QPoint( 2, 2 ), QPoint( 400, 100 ) );
00746 
00747   tip( QRect( p.x() - 20, p.y() - 20, 40, 40 ), text, geometry );
00748 }
00749 
00750 
00751 SideWidget::SideWidget( RecipientsView *view, QWidget *parent )
00752   : QWidget( parent ), mView( view ), mRecipientPicker( 0 )
00753 {
00754   QBoxLayout *topLayout = new QVBoxLayout( this );
00755 
00756   topLayout->setSpacing( KDialog::spacingHint() );
00757   topLayout->addStretch( 1 );
00758 
00759   mTotalLabel = new QLabel( this );
00760   mTotalLabel->setAlignment( AlignCenter );
00761   topLayout->addWidget( mTotalLabel );
00762   mTotalLabel->hide();
00763 
00764   topLayout->addStretch( 1 );
00765 
00766   new RecipientsToolTip( view, mTotalLabel );
00767 
00768   mDistributionListButton = new QPushButton( i18n("Save List..."), this );
00769   topLayout->addWidget( mDistributionListButton );
00770   mDistributionListButton->hide();
00771   connect( mDistributionListButton, SIGNAL( clicked() ),
00772     SIGNAL( saveDistributionList() ) );
00773   QToolTip::add( mDistributionListButton,
00774     i18n("Save recipients as distribution list") );
00775 
00776   mSelectButton = new QPushButton( i18n("Se&lect..."), this );
00777   topLayout->addWidget( mSelectButton );
00778   connect( mSelectButton, SIGNAL( clicked() ), SLOT( pickRecipient() ) );
00779   QToolTip::add( mSelectButton, i18n("Select recipients from address book") );
00780 }
00781 
00782 SideWidget::~SideWidget()
00783 {
00784 }
00785 
00786 RecipientsPicker* SideWidget::picker() const
00787 {
00788   if ( !mRecipientPicker ) {
00789     // hacks to allow picker() to be const in the presence of lazy loading
00790     SideWidget *non_const_this = const_cast<SideWidget*>( this );
00791     mRecipientPicker = new RecipientsPicker( non_const_this );
00792     connect( mRecipientPicker, SIGNAL( pickedRecipient( const Recipient & ) ),
00793              non_const_this, SIGNAL( pickedRecipient( const Recipient & ) ) );
00794     mPickerPositioner = new KWindowPositioner( non_const_this, mRecipientPicker );
00795   }
00796   return mRecipientPicker;
00797 }
00798 
00799 void SideWidget::setFocus()
00800 {
00801   mSelectButton->setFocus();
00802 }
00803 
00804 void SideWidget::setTotal( int recipients, int lines )
00805 {
00806 #if 0
00807   kdDebug() << "SideWidget::setTotal() recipients: " << recipients <<
00808     "  lines: " << lines << endl;
00809 #endif
00810 
00811   QString labelText;
00812   if ( recipients == 0 ) labelText = i18n("No recipients");
00813   else labelText = i18n("1 recipient","%n recipients", recipients );
00814   mTotalLabel->setText( labelText );
00815 
00816   if ( lines > 3 ) mTotalLabel->show();
00817   else mTotalLabel->hide();
00818 
00819   if ( lines > 2 ) mDistributionListButton->show();
00820   else mDistributionListButton->hide();
00821 }
00822 
00823 void SideWidget::pickRecipient()
00824 {
00825 #if 0
00826   QString rec = KInputDialog::getText( "Pick Recipient",
00827     "Email address of recipient" );
00828   if ( !rec.isEmpty() ) emit pickedRecipient( rec );
00829 #else
00830   RecipientsPicker *p = picker();
00831   p->setDefaultType( mView->activeLine()->recipientType() );
00832   p->setRecipients( mView->recipients() );
00833   p->show();
00834   mPickerPositioner->reposition();
00835   p->raise();
00836 #endif
00837 }
00838 
00839 
00840 RecipientsEditor::RecipientsEditor( QWidget *parent )
00841   : QWidget( parent ), mModified( false )
00842 {
00843   QBoxLayout *topLayout = new QHBoxLayout( this );
00844   topLayout->setSpacing( KDialog::spacingHint() );
00845 
00846   mRecipientsView = new RecipientsView( this );
00847   topLayout->addWidget( mRecipientsView );
00848   connect( mRecipientsView, SIGNAL( focusUp() ), SIGNAL( focusUp() ) );
00849   connect( mRecipientsView, SIGNAL( focusDown() ), SIGNAL( focusDown() ) );
00850   connect( mRecipientsView, SIGNAL( completionModeChanged( KGlobalSettings::Completion ) ),
00851     SIGNAL( completionModeChanged( KGlobalSettings::Completion ) ) );
00852 
00853   mSideWidget = new SideWidget( mRecipientsView, this );
00854   topLayout->addWidget( mSideWidget );
00855   connect( mSideWidget, SIGNAL( pickedRecipient( const Recipient & ) ),
00856     SLOT( slotPickedRecipient( const Recipient & ) ) );
00857   connect( mSideWidget, SIGNAL( saveDistributionList() ),
00858     SLOT( saveDistributionList() ) );
00859 
00860   connect( mRecipientsView, SIGNAL( totalChanged( int, int ) ),
00861     mSideWidget, SLOT( setTotal( int, int ) ) );
00862   connect( mRecipientsView, SIGNAL( focusRight() ),
00863     mSideWidget, SLOT( setFocus() ) );
00864 
00865   connect( mRecipientsView, SIGNAL(sizeHintChanged()),
00866            SIGNAL(sizeHintChanged()) );
00867 }
00868 
00869 RecipientsEditor::~RecipientsEditor()
00870 {
00871 }
00872 
00873 RecipientsPicker* RecipientsEditor::picker() const
00874 {
00875   return mSideWidget->picker();
00876 }
00877 
00878 void RecipientsEditor::slotPickedRecipient( const Recipient &rec )
00879 {
00880   RecipientLine *line = mRecipientsView->activeLine();
00881   if ( !line->isEmpty() ) line = mRecipientsView->addLine();
00882 
00883   Recipient r = rec;
00884   if ( r.type() == Recipient::Undefined ) {
00885     r.setType( line->recipientType() );
00886   }
00887 
00888   line->setRecipient( r );
00889   mModified = true;
00890 }
00891 
00892 void RecipientsEditor::saveDistributionList()
00893 {
00894   DistributionListDialog *dlg = new DistributionListDialog( this );
00895   dlg->setRecipients( mRecipientsView->recipients() );
00896   dlg->exec();
00897   delete dlg;
00898 }
00899 
00900 Recipient::List RecipientsEditor::recipients() const
00901 {
00902   return mRecipientsView->recipients();
00903 }
00904 
00905 void RecipientsEditor::setRecipientString( const QString &str,
00906   Recipient::Type type )
00907 {
00908   clear();
00909 
00910   int count = 1;
00911 
00912   QStringList r = KPIM::splitEmailAddrList( str );
00913   QStringList::ConstIterator it;
00914   for( it = r.begin(); it != r.end(); ++it ) {
00915     if ( count++ > GlobalSettings::self()->maximumRecipients() ) {
00916       KMessageBox::sorry( this,
00917         i18n("Truncating recipients list to %1 of %2 entries.")
00918         .arg( GlobalSettings::self()->maximumRecipients() )
00919         .arg( r.count() ) );
00920       break;
00921     }
00922     addRecipient( *it, type );
00923   }
00924 }
00925 
00926 QString RecipientsEditor::recipientString( Recipient::Type type )
00927 {
00928   QString str;
00929 
00930   Recipient::List recipients = mRecipientsView->recipients();
00931   Recipient::List::ConstIterator it;
00932   for( it = recipients.begin(); it != recipients.end(); ++it ) {
00933     if ( (*it).type() == type ) {
00934       if ( !str.isEmpty() ) str += ", ";
00935       str.append( (*it).email() );
00936     }
00937   }
00938 
00939   return str;
00940 }
00941 
00942 void RecipientsEditor::addRecipient( const QString & recipient,
00943                                      Recipient::Type type )
00944 {
00945   RecipientLine *line = mRecipientsView->emptyLine();
00946   if ( !line ) line = mRecipientsView->addLine();
00947   line->setRecipient( Recipient( recipient, type ) );
00948 }
00949 
00950 void RecipientsEditor::removeRecipient( const QString & recipient,
00951                                         Recipient::Type type )
00952 {
00953   mRecipientsView->removeRecipient( recipient, type );
00954 }
00955 
00956 bool RecipientsEditor::isModified()
00957 {
00958   return mModified || mRecipientsView->isModified();
00959 }
00960 
00961 void RecipientsEditor::clearModified()
00962 {
00963   mModified = false;
00964   mRecipientsView->clearModified();
00965 }
00966 
00967 void RecipientsEditor::clear()
00968 {
00969 }
00970 
00971 void RecipientsEditor::setFocus()
00972 {
00973   mRecipientsView->setFocus();
00974 }
00975 
00976 void RecipientsEditor::setFocusTop()
00977 {
00978   mRecipientsView->setFocusTop();
00979 }
00980 
00981 void RecipientsEditor::setFocusBottom()
00982 {
00983   mRecipientsView->setFocusBottom();
00984 }
00985 
00986 int RecipientsEditor::setFirstColumnWidth( int w )
00987 {
00988   return mRecipientsView->setFirstColumnWidth( w );
00989 }
00990 
00991 void RecipientsEditor::selectRecipients()
00992 {
00993   mSideWidget->pickRecipient();
00994 }
00995 
00996 void RecipientsEditor::setCompletionMode( KGlobalSettings::Completion mode )
00997 {
00998   mRecipientsView->setCompletionMode( mode );
00999 }
01000 
01001 #include "recipientseditor.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys