kmail

recipientseditor.cpp

Go to the documentation of this file.
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   topLayout->addWidget( mEdit );
00163   connect( mEdit, SIGNAL( returnPressed() ), SLOT( slotReturnPressed() ) );
00164   connect( mEdit, SIGNAL( deleteMe() ), SLOT( slotPropagateDeletion() ) );
00165   connect( mEdit, SIGNAL( textChanged( const QString & ) ),
00166     SLOT( analyzeLine( const QString & ) ) );
00167   connect( mEdit, SIGNAL( focusUp() ), SLOT( slotFocusUp() ) );
00168   connect( mEdit, SIGNAL( focusDown() ), SLOT( slotFocusDown() ) );
00169   connect( mEdit, SIGNAL( rightPressed() ), SIGNAL( rightPressed() ) );
00170 
00171   connect( mEdit, SIGNAL( leftPressed() ), mCombo, SLOT( setFocus() ) );
00172   connect( mCombo, SIGNAL( rightPressed() ), mEdit, SLOT( setFocus() ) );
00173 
00174   connect( mCombo, SIGNAL( activated ( int ) ),
00175            this, SLOT( slotTypeModified() ) );
00176 
00177   mRemoveButton = new QPushButton( this );
00178   mRemoveButton->setIconSet( KApplication::reverseLayout() ? SmallIconSet("locationbar_erase") : SmallIconSet( "clear_left" ) );
00179   topLayout->addWidget( mRemoveButton );
00180   connect( mRemoveButton, SIGNAL( clicked() ), SLOT( slotPropagateDeletion() ) );
00181   QToolTip::add( mRemoveButton, i18n("Remove recipient line") );
00182 }
00183 
00184 void RecipientLine::slotFocusUp()
00185 {
00186   emit upPressed( this );
00187 }
00188 
00189 void RecipientLine::slotFocusDown()
00190 {
00191   emit downPressed( this );
00192 }
00193 
00194 void RecipientLine::slotTypeModified()
00195 {
00196   mModified = true;
00197 
00198   emit typeModified( this );
00199 }
00200 
00201 void RecipientLine::analyzeLine( const QString &text )
00202 {
00203   QStringList r = KPIM::splitEmailAddrList( text );
00204   if ( int( r.count() ) != mRecipientsCount ) {
00205     mRecipientsCount = r.count();
00206     emit countChanged();
00207   }
00208 }
00209 
00210 int RecipientLine::recipientsCount()
00211 {
00212   return mRecipientsCount;
00213 }
00214 
00215 void RecipientLine::setRecipient( const Recipient &rec )
00216 {
00217   mEdit->setText( rec.email() );
00218   mCombo->setCurrentItem( Recipient::typeToId( rec.type() ) );
00219 }
00220 
00221 void RecipientLine::setRecipient( const QString &email )
00222 {
00223   setRecipient( Recipient( email ) );
00224 }
00225 
00226 Recipient RecipientLine::recipient() const
00227 {
00228   return Recipient( mEdit->text(),
00229     Recipient::idToType( mCombo->currentItem() ) );
00230 }
00231 
00232 void RecipientLine::setRecipientType( Recipient::Type type )
00233 {
00234   mCombo->setCurrentItem( Recipient::typeToId( type ) );
00235 }
00236 
00237 Recipient::Type RecipientLine::recipientType() const
00238 {
00239   return Recipient::idToType( mCombo->currentItem() );
00240 }
00241 
00242 void RecipientLine::activate()
00243 {
00244   mEdit->setFocus();
00245 }
00246 
00247 bool RecipientLine::isActive()
00248 {
00249   return mEdit->hasFocus();
00250 }
00251 
00252 bool RecipientLine::isEmpty()
00253 {
00254   return mEdit->text().isEmpty();
00255 }
00256 
00257 bool RecipientLine::isModified()
00258 {
00259   return mModified || mEdit->isModified();
00260 }
00261 
00262 void RecipientLine::clearModified()
00263 {
00264   mModified = false;
00265   mEdit->clearModified();
00266 }
00267 
00268 void RecipientLine::slotReturnPressed()
00269 {
00270   emit returnPressed( this );
00271 }
00272 
00273 void RecipientLine::slotPropagateDeletion()
00274 {
00275   emit deleteLine( this );
00276 }
00277 
00278 void RecipientLine::keyPressEvent( QKeyEvent *ev )
00279 {
00280   if ( ev->key() == Key_Up ) {
00281     emit upPressed( this );
00282   } else if ( ev->key() == Key_Down ) {
00283     emit downPressed( this );
00284   }
00285 }
00286 
00287 int RecipientLine::setComboWidth( int w )
00288 {
00289   w = QMAX( w, mCombo->sizeHint().width() );
00290   mCombo->setFixedWidth( w );
00291   mCombo->updateGeometry();
00292   parentWidget()->updateGeometry();
00293   return w;
00294 }
00295 
00296 void RecipientLine::fixTabOrder( QWidget *previous )
00297 {
00298   setTabOrder( previous, mCombo );
00299   setTabOrder( mCombo, mEdit );
00300   setTabOrder( mEdit, mRemoveButton );
00301 }
00302 
00303 QWidget *RecipientLine::tabOut() const
00304 {
00305   return mRemoveButton;
00306 }
00307 
00308 void RecipientLine::clear()
00309 {
00310   mEdit->clear();
00311 }
00312 
00313 void RecipientLine::setRemoveLineButtonEnabled( bool b )
00314 {
00315   mRemoveButton->setEnabled( b );
00316 }
00317 
00318 
00319 // ------------ RecipientsView ---------------------
00320 
00321 RecipientsView::RecipientsView( QWidget *parent )
00322   : QScrollView( parent ), mCurDelLine( 0 ),
00323     mLineHeight( 0 ), mFirstColumnWidth( 0 ),
00324     mModified( false )
00325 {
00326   mCompletionMode = KGlobalSettings::completionMode();
00327   setHScrollBarMode( AlwaysOff );
00328   setLineWidth( 0 );
00329 
00330   addLine();
00331   setResizePolicy( QScrollView::Manual );
00332   setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
00333 
00334   viewport()->setPaletteBackgroundColor( paletteBackgroundColor() );
00335 }
00336 
00337 RecipientLine *RecipientsView::activeLine()
00338 {
00339   return mLines.last();
00340 }
00341 
00342 RecipientLine *RecipientsView::emptyLine()
00343 {
00344   RecipientLine *line;
00345   for( line = mLines.first(); line; line = mLines.next() ) {
00346     if ( line->isEmpty() ) return line;
00347   }
00348 
00349   return 0;
00350 }
00351 
00352 RecipientLine *RecipientsView::addLine()
00353 {
00354   RecipientLine *line = new RecipientLine( viewport() );
00355   addChild( line, 0, mLines.count() * mLineHeight );
00356   line->mEdit->setCompletionMode( mCompletionMode );
00357   line->show();
00358   connect( line, SIGNAL( returnPressed( RecipientLine * ) ),
00359     SLOT( slotReturnPressed( RecipientLine * ) ) );
00360   connect( line, SIGNAL( upPressed( RecipientLine * ) ),
00361     SLOT( slotUpPressed( RecipientLine * ) ) );
00362   connect( line, SIGNAL( downPressed( RecipientLine * ) ),
00363     SLOT( slotDownPressed( RecipientLine * ) ) );
00364   connect( line, SIGNAL( rightPressed() ), SIGNAL( focusRight() ) );
00365   connect( line, SIGNAL( deleteLine( RecipientLine * ) ),
00366     SLOT( slotDecideLineDeletion( RecipientLine * ) ) );
00367   connect( line, SIGNAL( countChanged() ), SLOT( calculateTotal() ) );
00368   connect( line, SIGNAL( typeModified( RecipientLine * ) ),
00369     SLOT( slotTypeModified( RecipientLine * ) ) );
00370   connect( line->mEdit, SIGNAL( completionModeChanged( KGlobalSettings::Completion ) ),
00371     SLOT( setCompletionMode( KGlobalSettings::Completion ) ) );
00372 
00373   if ( mLines.last() ) {
00374     if ( mLines.count() == 1 ) {
00375       if ( GlobalSettings::self()->secondRecipientTypeDefault() ==
00376          GlobalSettings::EnumSecondRecipientTypeDefault::To ) {
00377         line->setRecipientType( Recipient::To );
00378       } else {
00379         if ( mLines.last()->recipientType() == Recipient::Bcc ) {
00380           line->setRecipientType( Recipient::To );
00381         } else {
00382           line->setRecipientType( Recipient::Cc );
00383         }
00384       }
00385     } else {
00386       line->setRecipientType( mLines.last()->recipientType() );
00387     }
00388     line->fixTabOrder( mLines.last()->tabOut() );
00389   }
00390 
00391   mLines.append( line );
00392   // If there is only one line, removing it makes no sense
00393   if ( mLines.count() == 1 ) {
00394     mLines.first()->setRemoveLineButtonEnabled( false );
00395   } else {
00396     mLines.first()->setRemoveLineButtonEnabled( true );
00397   }
00398 
00399   mFirstColumnWidth = line->setComboWidth( mFirstColumnWidth );
00400 
00401   mLineHeight = line->minimumSizeHint().height();
00402 
00403   line->resize( viewport()->width(), mLineHeight );
00404 
00405   resizeView();
00406 
00407   calculateTotal();
00408 
00409   ensureVisible( 0, mLines.count() * mLineHeight );
00410 
00411   return line;
00412 }
00413 
00414 void RecipientsView::slotTypeModified( RecipientLine *line )
00415 {
00416   if ( mLines.count() == 2 ||
00417        ( mLines.count() == 3 && mLines.at( 2 )->isEmpty() ) ) {
00418     if ( mLines.at( 1 ) == line ) {
00419       if ( line->recipientType() == Recipient::To ) {
00420         GlobalSettings::self()->setSecondRecipientTypeDefault(
00421           GlobalSettings::EnumSecondRecipientTypeDefault::To );
00422       } else if ( line->recipientType() == Recipient::Cc ) {
00423         GlobalSettings::self()->setSecondRecipientTypeDefault(
00424           GlobalSettings::EnumSecondRecipientTypeDefault::Cc );
00425       }
00426     }
00427   }
00428 }
00429 
00430 void RecipientsView::calculateTotal()
00431 {
00432   int count = 0;
00433   int empty = 0;
00434 
00435   RecipientLine *line;
00436   for( line = mLines.first(); line; line = mLines.next() ) {
00437     if ( line->isEmpty() ) ++empty;
00438     else count += line->recipientsCount();
00439   }
00440 
00441   if ( empty == 0 ) addLine();
00442 
00443   emit totalChanged( count, mLines.count() );
00444 }
00445 
00446 void RecipientsView::slotReturnPressed( RecipientLine *line )
00447 {
00448   if ( !line->recipient().isEmpty() ) {
00449     RecipientLine *empty = emptyLine();
00450     if ( !empty ) empty = addLine();
00451     activateLine( empty );
00452   }
00453 }
00454 
00455 void RecipientsView::slotDownPressed( RecipientLine *line )
00456 {
00457   int pos = mLines.find( line );
00458   if ( pos >= (int)mLines.count() - 1 ) {
00459     emit focusDown();
00460   } else if ( pos >= 0 ) {
00461     activateLine( mLines.at( pos + 1 ) );
00462   }
00463 }
00464 
00465 void RecipientsView::slotUpPressed( RecipientLine *line )
00466 {
00467   int pos = mLines.find( line );
00468   if ( pos > 0 ) {
00469     activateLine( mLines.at( pos - 1 ) );
00470   } else {
00471     emit focusUp();
00472   }
00473 }
00474 
00475 void RecipientsView::slotDecideLineDeletion( RecipientLine *line )
00476 {
00477   if ( !line->isEmpty() )
00478     mModified = true;
00479   if ( mLines.count() == 1 ) {
00480     line->clear();
00481   } else {
00482     mCurDelLine = line;
00483     QTimer::singleShot( 0, this, SLOT( slotDeleteLine( ) ) );
00484   }
00485 }
00486 
00487 void RecipientsView::slotDeleteLine()
00488 {
00489   if ( !mCurDelLine )
00490     return;
00491 
00492   RecipientLine *line = mCurDelLine;
00493   int pos = mLines.find( line );
00494 
00495   int newPos;
00496   if ( pos == 0 ) newPos = pos + 1;
00497   else newPos = pos - 1;
00498 
00499   // if there is something left to activate, do so
00500   if ( mLines.at( newPos ) )
00501     mLines.at( newPos )->activate();
00502 
00503   mLines.remove( line );
00504   removeChild( line );
00505   delete line;
00506 
00507   bool atLeastOneToLine = false;
00508   unsigned int firstCC = 0;
00509   for( uint i = pos; i < mLines.count(); ++i ) {
00510     RecipientLine *line = mLines.at( i );
00511     moveChild( line, childX( line ), childY( line ) - mLineHeight );
00512     if ( line->recipientType() == Recipient::To )
00513       atLeastOneToLine = true;
00514     else if ( ( line->recipientType() == Recipient::Cc ) && ( i == 0 ) )
00515       firstCC = i;
00516   }
00517   // only one left, can't remove that one
00518   if ( mLines.count() == 1 )
00519     mLines.first()->setRemoveLineButtonEnabled( false );
00520 
00521   if ( !atLeastOneToLine )
00522     mLines.at( firstCC )->setRecipientType( Recipient::To );
00523 
00524   calculateTotal();
00525 
00526   resizeView();
00527 }
00528 
00529 void RecipientsView::resizeView()
00530 {
00531   resizeContents( width(), mLines.count() * mLineHeight );
00532 
00533   if ( mLines.count() < 6 ) {
00534 //    setFixedHeight( mLineHeight * mLines.count() );
00535   }
00536 
00537   parentWidget()->layout()->activate();
00538   emit sizeHintChanged();
00539   QTimer::singleShot( 0, this, SLOT(moveCompletionPopup()) );
00540 }
00541 
00542 void RecipientsView::activateLine( RecipientLine *line )
00543 {
00544   line->activate();
00545   ensureVisible( 0, childY( line ) );
00546 }
00547 
00548 void RecipientsView::viewportResizeEvent ( QResizeEvent *ev )
00549 {
00550   for( uint i = 0; i < mLines.count(); ++i ) {
00551     mLines.at( i )->resize( ev->size().width(), mLineHeight );
00552   }
00553   ensureVisible( 0, mLines.count() * mLineHeight );
00554 }
00555 
00556 QSize RecipientsView::sizeHint() const
00557 {
00558   return QSize( 200, mLineHeight * mLines.count() );
00559 }
00560 
00561 QSize RecipientsView::minimumSizeHint() const
00562 {
00563   int height;
00564   uint numLines = 5;
00565   if ( mLines.count() < numLines ) height = mLineHeight * mLines.count();
00566   else height = mLineHeight * numLines;
00567   return QSize( 200, height );
00568 }
00569 
00570 Recipient::List RecipientsView::recipients() const
00571 {
00572   Recipient::List recipients;
00573 
00574   QPtrListIterator<RecipientLine> it( mLines );
00575   RecipientLine *line;
00576   while( ( line = it.current() ) ) {
00577     if ( !line->recipient().isEmpty() ) {
00578       recipients.append( line->recipient() );
00579     }
00580 
00581     ++it;
00582   }
00583 
00584   return recipients;
00585 }
00586 
00587 void RecipientsView::setCompletionMode ( KGlobalSettings::Completion mode )
00588 {
00589   if ( mCompletionMode == mode )
00590     return;
00591   mCompletionMode = mode;
00592 
00593   QPtrListIterator<RecipientLine> it( mLines );
00594   RecipientLine *line;
00595   while( ( line = it.current() ) ) {
00596     line->mEdit->blockSignals( true );
00597     line->mEdit->setCompletionMode( mode );
00598     line->mEdit->blockSignals( false );
00599     ++it;
00600   }
00601   emit completionModeChanged( mode ); //report change to RecipientsEditor
00602 }
00603 
00604 void RecipientsView::removeRecipient( const QString & recipient,
00605                                       Recipient::Type type )
00606 {
00607   // search a line which matches recipient and type
00608   QPtrListIterator<RecipientLine> it( mLines );
00609   RecipientLine *line;
00610   while( ( line = it.current() ) ) {
00611     if ( ( line->recipient().email() == recipient ) &&
00612          ( line->recipientType() == type ) ) {
00613       break;
00614     }
00615     ++it;
00616   }
00617   if ( line )
00618     line->slotPropagateDeletion();
00619 }
00620 
00621 bool RecipientsView::isModified()
00622 {
00623   if ( mModified )
00624     return true;
00625 
00626   QPtrListIterator<RecipientLine> it( mLines );
00627   RecipientLine *line;
00628   while( ( line = it.current() ) ) {
00629     if ( line->isModified() ) {
00630       return true;
00631     }
00632     ++it;
00633   }
00634 
00635   return false;
00636 }
00637 
00638 void RecipientsView::clearModified()
00639 {
00640   mModified = false;
00641 
00642   QPtrListIterator<RecipientLine> it( mLines );
00643   RecipientLine *line;
00644   while( ( line = it.current() ) ) {
00645     line->clearModified();
00646     ++it;
00647   }
00648 }
00649 
00650 void RecipientsView::setFocus()
00651 {
00652   if ( mLines.last()->isActive() ) setFocusBottom();
00653   else setFocusTop();
00654 }
00655 
00656 void RecipientsView::setFocusTop()
00657 {
00658   RecipientLine *line = mLines.first();
00659   if ( line ) line->activate();
00660   else kdWarning() << "No first" << endl;
00661 }
00662 
00663 void RecipientsView::setFocusBottom()
00664 {
00665   RecipientLine *line = mLines.last();
00666   if ( line ) line->activate();
00667   else  kdWarning() << "No last" << endl;
00668 }
00669 
00670 int RecipientsView::setFirstColumnWidth( int w )
00671 {
00672   mFirstColumnWidth = w;
00673 
00674   QPtrListIterator<RecipientLine> it( mLines );
00675   RecipientLine *line;
00676   while( ( line = it.current() ) ) {
00677     mFirstColumnWidth = line->setComboWidth( mFirstColumnWidth );
00678     ++it;
00679   }
00680 
00681   resizeView();
00682   return mFirstColumnWidth;
00683 }
00684 
00685 void RecipientsView::moveCompletionPopup()
00686 {
00687   for( RecipientLine* line = mLines.first(); line; line = mLines.next() ) {
00688     if ( line->lineEdit()->completionBox( false ) ) {
00689       if ( line->lineEdit()->completionBox()->isVisible() ) {
00690         // ### trigger moving, is there a nicer way to do that?
00691         line->lineEdit()->completionBox()->hide();
00692         line->lineEdit()->completionBox()->show();
00693       }
00694     }
00695   }
00696 
00697 }
00698 
00699 RecipientsToolTip::RecipientsToolTip( RecipientsView *view, QWidget *parent )
00700   : QToolTip( parent ), mView( view )
00701 {
00702 }
00703 
00704 QString RecipientsToolTip::line( const Recipient &r )
00705 {
00706   QString txt = r.email();
00707 
00708   return "&nbsp;&nbsp;" + QStyleSheet::escape( txt ) + "<br/>";
00709 }
00710 
00711 void RecipientsToolTip::maybeTip( const QPoint & p )
00712 {
00713   QString text = "<qt>";
00714 
00715   QString to;
00716   QString cc;
00717   QString bcc;
00718 
00719   Recipient::List recipients = mView->recipients();
00720   Recipient::List::ConstIterator it;
00721   for( it = recipients.begin(); it != recipients.end(); ++it ) {
00722     switch( (*it).type() ) {
00723       case Recipient::To:
00724         to += line( *it );
00725         break;
00726       case Recipient::Cc:
00727         cc += line( *it );
00728         break;
00729       case Recipient::Bcc:
00730         bcc += line( *it );
00731         break;
00732       default:
00733         break;
00734     }
00735   }
00736 
00737   text += i18n("<b>To:</b><br/>") + to;
00738   if ( !cc.isEmpty() ) text += i18n("<b>CC:</b><br/>") + cc;
00739   if ( !bcc.isEmpty() ) text += i18n("<b>BCC:</b><br/>") + bcc;
00740 
00741   text.append( "</qt>" );
00742 
00743   QRect geometry( p + QPoint( 2, 2 ), QPoint( 400, 100 ) );
00744 
00745   tip( QRect( p.x() - 20, p.y() - 20, 40, 40 ), text, geometry );
00746 }
00747 
00748 
00749 SideWidget::SideWidget( RecipientsView *view, QWidget *parent )
00750   : QWidget( parent ), mView( view ), mRecipientPicker( 0 )
00751 {
00752   QBoxLayout *topLayout = new QVBoxLayout( this );
00753 
00754   topLayout->setSpacing( KDialog::spacingHint() );
00755   topLayout->addStretch( 1 );
00756 
00757   mTotalLabel = new QLabel( this );
00758   mTotalLabel->setAlignment( AlignCenter );
00759   topLayout->addWidget( mTotalLabel );
00760   mTotalLabel->hide();
00761 
00762   topLayout->addStretch( 1 );
00763 
00764   new RecipientsToolTip( view, mTotalLabel );
00765 
00766   mDistributionListButton = new QPushButton( i18n("Save List..."), this );
00767   topLayout->addWidget( mDistributionListButton );
00768   mDistributionListButton->hide();
00769   connect( mDistributionListButton, SIGNAL( clicked() ),
00770     SIGNAL( saveDistributionList() ) );
00771   QToolTip::add( mDistributionListButton,
00772     i18n("Save recipients as distribution list") );
00773 
00774   mSelectButton = new QPushButton( i18n("Se&lect..."), this );
00775   topLayout->addWidget( mSelectButton );
00776   connect( mSelectButton, SIGNAL( clicked() ), SLOT( pickRecipient() ) );
00777   QToolTip::add( mSelectButton, i18n("Select recipients from address book") );
00778 }
00779 
00780 SideWidget::~SideWidget()
00781 {
00782 }
00783 
00784 RecipientsPicker* SideWidget::picker() const
00785 {
00786   if ( !mRecipientPicker ) {
00787     // hacks to allow picker() to be const in the presence of lazy loading
00788     SideWidget *non_const_this = const_cast<SideWidget*>( this );
00789     mRecipientPicker = new RecipientsPicker( non_const_this );
00790     connect( mRecipientPicker, SIGNAL( pickedRecipient( const Recipient & ) ),
00791              non_const_this, SIGNAL( pickedRecipient( const Recipient & ) ) );
00792     mPickerPositioner = new KWindowPositioner( non_const_this, mRecipientPicker );
00793   }
00794   return mRecipientPicker;
00795 }
00796 
00797 void SideWidget::setFocus()
00798 {
00799   mSelectButton->setFocus();
00800 }
00801 
00802 void SideWidget::setTotal( int recipients, int lines )
00803 {
00804 #if 0
00805   kdDebug() << "SideWidget::setTotal() recipients: " << recipients <<
00806     "  lines: " << lines << endl;
00807 #endif
00808 
00809   QString labelText;
00810   if ( recipients == 0 ) labelText = i18n("No recipients");
00811   else labelText = i18n("1 recipient","%n recipients", recipients );
00812   mTotalLabel->setText( labelText );
00813 
00814   if ( lines > 3 ) mTotalLabel->show();
00815   else mTotalLabel->hide();
00816 
00817   if ( lines > 2 ) mDistributionListButton->show();
00818   else mDistributionListButton->hide();
00819 }
00820 
00821 void SideWidget::pickRecipient()
00822 {
00823 #if 0
00824   QString rec = KInputDialog::getText( "Pick Recipient",
00825     "Email address of recipient" );
00826   if ( !rec.isEmpty() ) emit pickedRecipient( rec );
00827 #else
00828   RecipientsPicker *p = picker();
00829   p->setDefaultType( mView->activeLine()->recipientType() );
00830   p->setRecipients( mView->recipients() );
00831   p->show();
00832   mPickerPositioner->reposition();
00833   p->raise();
00834 #endif
00835 }
00836 
00837 
00838 RecipientsEditor::RecipientsEditor( QWidget *parent )
00839   : QWidget( parent ), mModified( false )
00840 {
00841   QBoxLayout *topLayout = new QHBoxLayout( this );
00842   topLayout->setSpacing( KDialog::spacingHint() );
00843 
00844   mRecipientsView = new RecipientsView( this );
00845   topLayout->addWidget( mRecipientsView );
00846   connect( mRecipientsView, SIGNAL( focusUp() ), SIGNAL( focusUp() ) );
00847   connect( mRecipientsView, SIGNAL( focusDown() ), SIGNAL( focusDown() ) );
00848   connect( mRecipientsView, SIGNAL( completionModeChanged( KGlobalSettings::Completion ) ),
00849     SIGNAL( completionModeChanged( KGlobalSettings::Completion ) ) );
00850 
00851   mSideWidget = new SideWidget( mRecipientsView, this );
00852   topLayout->addWidget( mSideWidget );
00853   connect( mSideWidget, SIGNAL( pickedRecipient( const Recipient & ) ),
00854     SLOT( slotPickedRecipient( const Recipient & ) ) );
00855   connect( mSideWidget, SIGNAL( saveDistributionList() ),
00856     SLOT( saveDistributionList() ) );
00857 
00858   connect( mRecipientsView, SIGNAL( totalChanged( int, int ) ),
00859     mSideWidget, SLOT( setTotal( int, int ) ) );
00860   connect( mRecipientsView, SIGNAL( focusRight() ),
00861     mSideWidget, SLOT( setFocus() ) );
00862 
00863   connect( mRecipientsView, SIGNAL(sizeHintChanged()),
00864            SIGNAL(sizeHintChanged()) );
00865 }
00866 
00867 RecipientsEditor::~RecipientsEditor()
00868 {
00869 }
00870 
00871 RecipientsPicker* RecipientsEditor::picker() const
00872 {
00873   return mSideWidget->picker();
00874 }
00875 
00876 void RecipientsEditor::slotPickedRecipient( const Recipient &rec )
00877 {
00878   RecipientLine *line = mRecipientsView->activeLine();
00879   if ( !line->isEmpty() ) line = mRecipientsView->addLine();
00880 
00881   Recipient r = rec;
00882   if ( r.type() == Recipient::Undefined ) {
00883     r.setType( line->recipientType() );
00884   }
00885 
00886   line->setRecipient( r );
00887   mModified = true;
00888 }
00889 
00890 void RecipientsEditor::saveDistributionList()
00891 {
00892   DistributionListDialog *dlg = new DistributionListDialog( this );
00893   dlg->setRecipients( mRecipientsView->recipients() );
00894   dlg->show();
00895 }
00896 
00897 Recipient::List RecipientsEditor::recipients() const
00898 {
00899   return mRecipientsView->recipients();
00900 }
00901 
00902 void RecipientsEditor::setRecipientString( const QString &str,
00903   Recipient::Type type )
00904 {
00905   clear();
00906 
00907   int count = 1;
00908 
00909   QStringList r = KPIM::splitEmailAddrList( str );
00910   QStringList::ConstIterator it;
00911   for( it = r.begin(); it != r.end(); ++it ) {
00912     if ( count++ > GlobalSettings::self()->maximumRecipients() ) {
00913       KMessageBox::sorry( this,
00914         i18n("Truncating recipients list to %1 of %2 entries.")
00915         .arg( GlobalSettings::self()->maximumRecipients() )
00916         .arg( r.count() ) );
00917       break;
00918     }
00919     addRecipient( *it, type );
00920   }
00921 }
00922 
00923 QString RecipientsEditor::recipientString( Recipient::Type type )
00924 {
00925   QString str;
00926 
00927   Recipient::List recipients = mRecipientsView->recipients();
00928   Recipient::List::ConstIterator it;
00929   for( it = recipients.begin(); it != recipients.end(); ++it ) {
00930     if ( (*it).type() == type ) {
00931       if ( !str.isEmpty() ) str += ", ";
00932       str.append( (*it).email() );
00933     }
00934   }
00935 
00936   return str;
00937 }
00938 
00939 void RecipientsEditor::addRecipient( const QString & recipient,
00940                                      Recipient::Type type )
00941 {
00942   RecipientLine *line = mRecipientsView->emptyLine();
00943   if ( !line ) line = mRecipientsView->addLine();
00944   line->setRecipient( Recipient( recipient, type ) );
00945 }
00946 
00947 void RecipientsEditor::removeRecipient( const QString & recipient,
00948                                         Recipient::Type type )
00949 {
00950   mRecipientsView->removeRecipient( recipient, type );
00951 }
00952 
00953 bool RecipientsEditor::isModified()
00954 {
00955   return mModified || mRecipientsView->isModified();
00956 }
00957 
00958 void RecipientsEditor::clearModified()
00959 {
00960   mModified = false;
00961   mRecipientsView->clearModified();
00962 }
00963 
00964 void RecipientsEditor::clear()
00965 {
00966 }
00967 
00968 void RecipientsEditor::setFocus()
00969 {
00970   mRecipientsView->setFocus();
00971 }
00972 
00973 void RecipientsEditor::setFocusTop()
00974 {
00975   mRecipientsView->setFocusTop();
00976 }
00977 
00978 void RecipientsEditor::setFocusBottom()
00979 {
00980   mRecipientsView->setFocusBottom();
00981 }
00982 
00983 int RecipientsEditor::setFirstColumnWidth( int w )
00984 {
00985   return mRecipientsView->setFirstColumnWidth( w );
00986 }
00987 
00988 void RecipientsEditor::selectRecipients()
00989 {
00990   mSideWidget->pickRecipient();
00991 }
00992 
00993 void RecipientsEditor::setCompletionMode( KGlobalSettings::Completion mode )
00994 {
00995   mRecipientsView->setCompletionMode( mode );
00996 }
00997 
00998 #include "recipientseditor.moc"