kmail Library API Documentation

kmcomposewin.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmcomposewin.cpp
00003 // Author: Markus Wuebben <markus.wuebben@kde.org>
00004 // This code is published under the GPL.
00005 
00006 // keep this in sync with the define in configuredialog.h
00007 #define DEFAULT_EDITOR_STR "kate %f"
00008 
00009 #undef GrayScale
00010 #undef Color
00011 #include <config.h>
00012 
00013 #include "kmcomposewin.h"
00014 
00015 #include "globalsettings.h"
00016 #include "kmglobalns.h" //for text completion mode struct
00017 
00018 #include "kmmainwin.h"
00019 #include "kmreaderwin.h"
00020 #include "kmreadermainwin.h"
00021 #include "messagesender.h"
00022 #include "kmmsgpartdlg.h"
00023 #include <kpgpblock.h>
00024 #include <kaddrbook.h>
00025 #include "kmaddrbook.h"
00026 #include "kmmsgdict.h"
00027 #include "kmfolderimap.h"
00028 #include "kmfoldermaildir.h"
00029 #include "kmfoldermgr.h"
00030 #include "kmfoldercombobox.h"
00031 #include "kmtransport.h"
00032 #include "kmcommands.h"
00033 #include "kcursorsaver.h"
00034 #include "kmkernel.h"
00035 #include "partNode.h"
00036 #include "attachmentlistview.h"
00037 using KMail::AttachmentListView;
00038 #include "dictionarycombobox.h"
00039 using KMail::DictionaryComboBox;
00040 #include "addressesdialog.h"
00041 using KPIM::AddressesDialog;
00042 #include <maillistdrag.h>
00043 using KPIM::MailListDrag;
00044 #include "recentaddresses.h"
00045 #include "completionordereditor.h"
00046 #include "ldapclient.h"
00047 
00048 using KRecentAddress::RecentAddresses;
00049 #include "kleo_util.h"
00050 #include "stl_util.h"
00051 
00052 #include "attachmentcollector.h"
00053 #include "objecttreeparser.h"
00054 
00055 #include <libkpimidentities/identitymanager.h>
00056 #include <libkpimidentities/identitycombo.h>
00057 #include <libkpimidentities/identity.h>
00058 #include <libkdepim/kfileio.h>
00059 #include <libkdepim/email.h>
00060 #include <kleo/cryptobackendfactory.h>
00061 #include <kleo/exportjob.h>
00062 #include <kleo/specialjob.h>
00063 #include <ui/progressdialog.h>
00064 #include <ui/keyselectiondialog.h>
00065 
00066 #include <gpgmepp/context.h>
00067 #include <gpgmepp/key.h>
00068 
00069 #include "klistboxdialog.h"
00070 
00071 #include "messagecomposer.h"
00072 
00073 #include <kcharsets.h>
00074 #include <kcompletionbox.h>
00075 #include <kcursor.h>
00076 #include <kcombobox.h>
00077 #include <kstdaccel.h>
00078 #include <kpopupmenu.h>
00079 #include <kedittoolbar.h>
00080 #include <kkeydialog.h>
00081 #include <kdebug.h>
00082 #include <kfiledialog.h>
00083 #include <kwin.h>
00084 #include <kinputdialog.h>
00085 #include <kmessagebox.h>
00086 #include <kurldrag.h>
00087 #include <kio/scheduler.h>
00088 #include <ktempfile.h>
00089 #include <klocale.h>
00090 #include <kapplication.h>
00091 #include <kstatusbar.h>
00092 #include <kaction.h>
00093 #include <kdirwatch.h>
00094 #include <kstdguiitem.h>
00095 #include <kiconloader.h>
00096 #include <kpushbutton.h>
00097 #include <kmenubar.h>
00098 #include <ktoolbarbutton.h>
00099 #include <kuserprofile.h> // for KServiceTypeProfile
00100 #include <krun.h>
00101 //#include <keditlistbox.h>
00102 
00103 #include <kspell.h>
00104 #include <kspelldlg.h>
00105 #include <spellingfilter.h>
00106 #include <ksyntaxhighlighter.h>
00107 #include <kcolordialog.h>
00108 #include <ksavefile.h>
00109 
00110 #include <qtabdialog.h>
00111 #include <qregexp.h>
00112 #include <qbuffer.h>
00113 #include <qtooltip.h>
00114 #include <qtextcodec.h>
00115 #include <qheader.h>
00116 #include <qwhatsthis.h>
00117 #include <qfontdatabase.h>
00118 
00119 #include <mimelib/mimepp.h>
00120 
00121 #include <algorithm>
00122 #include <memory>
00123 
00124 #include <sys/stat.h>
00125 #include <sys/types.h>
00126 #include <stdlib.h>
00127 #include <unistd.h>
00128 #include <errno.h>
00129 #include <fcntl.h>
00130 #include <assert.h>
00131 
00132 #include "kmcomposewin.moc"
00133 
00134 //-----------------------------------------------------------------------------
00135 KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id  )
00136   : MailComposerIface(), KMail::SecondaryWindow( "kmail-composer#" ),
00137     mSpellCheckInProgress( false ),
00138     mDone( false ),
00139     mAtmModified( false ),
00140     mMsg( 0 ),
00141     mAttachMenu( 0 ),
00142     mSigningAndEncryptionExplicitlyDisabled( false ),
00143     mAutoRequestMDN( false ),
00144     mFolder( 0 ),
00145     mUseHTMLEditor( false ),
00146     mId( id ),
00147     mAttachPK( 0 ), mAttachMPK( 0 ),
00148     mAttachRemoveAction( 0 ), mAttachSaveAction( 0 ), mAttachPropertiesAction( 0 ),
00149     mAppendSignatureAction( 0 ), mPrependSignatureAction( 0 ), mInsertSignatureAction( 0 ),
00150     mSignAction( 0 ), mEncryptAction( 0 ), mRequestMDNAction( 0 ),
00151     mUrgentAction( 0 ), mAllFieldsAction( 0 ), mFromAction( 0 ),
00152     mReplyToAction( 0 ), mToAction( 0 ), mCcAction( 0 ), mBccAction( 0 ),
00153     mSubjectAction( 0 ),
00154     mIdentityAction( 0 ), mTransportAction( 0 ), mFccAction( 0 ),
00155     mWordWrapAction( 0 ), mFixedFontAction( 0 ), mAutoSpellCheckingAction( 0 ),
00156     mDictionaryAction( 0 ),
00157     mEncodingAction( 0 ),
00158     mCryptoModuleAction( 0 ),
00159     mTextCompletionAction( 0 ),
00160 #ifdef KLEO_CHIASMUS
00161     mEncryptChiasmusAction( 0 ),
00162     mEncryptBodyChiasmusAction( 0 ),
00163     mEncryptWithChiasmus( false ),
00164     mEncryptBodyWithChiasmus( false ),
00165 #endif
00166     mComposer( 0 ),
00167     mAutoSaveTimer( 0 ), mLastAutoSaveErrno( 0 )
00168 {
00169   // Set this to be the group leader for all subdialogs - this means
00170   // modal subdialogs will only affect this dialog, not the other windows
00171   setWFlags( getWFlags() | WGroupLeader );
00172 
00173   mSubjectTextWasSpellChecked = false;
00174   if (kmkernel->xmlGuiInstance())
00175     setInstance( kmkernel->xmlGuiInstance() );
00176   mMainWidget = new QWidget(this);
00177   mIdentity = new KPIM::IdentityCombo(kmkernel->identityManager(), mMainWidget);
00178   mDictionaryCombo = new DictionaryComboBox( mMainWidget );
00179   mFcc = new KMFolderComboBox(mMainWidget);
00180   mFcc->showOutboxFolder( FALSE );
00181   mTransport = new QComboBox(true, mMainWidget);
00182   mEdtFrom = new KMLineEdit(this,false,mMainWidget);
00183   mEdtReplyTo = new KMLineEdit(this,false,mMainWidget);
00184   mEdtTo = new KMLineEdit(this,true,mMainWidget);
00185   mEdtCc = new KMLineEdit(this,true,mMainWidget);
00186   mEdtBcc = new KMLineEdit(this,true,mMainWidget);
00187   mEdtSubject = new KMLineEditSpell(this,false,mMainWidget, "subjectLine");
00188   mLblIdentity = new QLabel(mMainWidget);
00189   mDictionaryLabel = new QLabel( mMainWidget );
00190   mLblFcc = new QLabel(mMainWidget);
00191   mLblTransport = new QLabel(mMainWidget);
00192   mLblFrom = new QLabel(mMainWidget);
00193   mLblReplyTo = new QLabel(mMainWidget);
00194   mLblTo = new QLabel(mMainWidget);
00195   mLblCc = new QLabel(mMainWidget);
00196   mLblBcc = new QLabel(mMainWidget);
00197   mLblSubject = new QLabel(mMainWidget);
00198   QString sticky = i18n("Sticky");
00199   mBtnIdentity = new QCheckBox(sticky,mMainWidget);
00200   mBtnFcc = new QCheckBox(sticky,mMainWidget);
00201   mBtnTransport = new QCheckBox(sticky,mMainWidget);
00202   mBtnTo = new QPushButton("...",mMainWidget);
00203   mBtnCc = new QPushButton("...",mMainWidget);
00204   mBtnBcc = new QPushButton("...",mMainWidget);
00205   //mBtnFrom = new QPushButton("...",mMainWidget);
00206   mBtnReplyTo = new QPushButton("...",mMainWidget);
00207 
00208   //setWFlags( WType_TopLevel | WStyle_Dialog );
00209   mDone = false;
00210   mGrid = 0;
00211   mAtmListView = 0;
00212   mAtmList.setAutoDelete(TRUE);
00213   mAtmTempList.setAutoDelete(TRUE);
00214   mAtmModified = FALSE;
00215   mAutoDeleteMsg = FALSE;
00216   mFolder = 0;
00217   mAutoCharset = TRUE;
00218   mFixedFontAction = 0;
00219   mEditor = new KMEdit( mMainWidget, this, mDictionaryCombo->spellConfig() );
00220   mEditor->setTextFormat(Qt::PlainText);
00221   mEditor->setAcceptDrops( true );
00222 
00223   QString tip = i18n("Select email address(es)");
00224   QToolTip::add( mBtnTo, tip );
00225   QToolTip::add( mBtnCc, tip );
00226   QToolTip::add( mBtnReplyTo, tip );
00227 
00228   QWhatsThis::add( mBtnIdentity, i18n("Remember this identity, so that it "
00229     "will be used in future composer windows as well."));
00230   QWhatsThis::add( mBtnFcc, i18n("Remember this folder for sent items, so "
00231     "that it will be used in future composer windows as well."));
00232   QWhatsThis::add( mBtnTransport, i18n("Remember this mail transport, so "
00233     "that it will be used in future composer windows as well."));
00234 
00235   mSpellCheckInProgress=FALSE;
00236 
00237   setCaption( i18n("Composer") );
00238   setMinimumSize(200,200);
00239 
00240   mBtnIdentity->setFocusPolicy(QWidget::NoFocus);
00241   mBtnFcc->setFocusPolicy(QWidget::NoFocus);
00242   mBtnTransport->setFocusPolicy(QWidget::NoFocus);
00243   mBtnTo->setFocusPolicy(QWidget::NoFocus);
00244   mBtnCc->setFocusPolicy(QWidget::NoFocus);
00245   mBtnBcc->setFocusPolicy(QWidget::NoFocus);
00246   //mBtnFrom->setFocusPolicy(QWidget::NoFocus);
00247   mBtnReplyTo->setFocusPolicy(QWidget::NoFocus);
00248 
00249   mAtmListView = new AttachmentListView( this, mMainWidget,
00250                                          "attachment list view" );
00251   mAtmListView->setSelectionMode( QListView::Extended );
00252   mAtmListView->setFocusPolicy( QWidget::NoFocus );
00253   mAtmListView->addColumn( i18n("Name"), 200 );
00254   mAtmListView->addColumn( i18n("Size"), 80 );
00255   mAtmListView->addColumn( i18n("Encoding"), 120 );
00256   int atmColType = mAtmListView->addColumn( i18n("Type"), 120 );
00257   // Stretch "Type".
00258   mAtmListView->header()->setStretchEnabled( true, atmColType );
00259   mAtmEncryptColWidth = 80;
00260   mAtmSignColWidth = 80;
00261   mAtmColEncrypt = mAtmListView->addColumn( i18n("Encrypt"),
00262                                             mAtmEncryptColWidth );
00263   mAtmColSign    = mAtmListView->addColumn( i18n("Sign"),
00264                                             mAtmSignColWidth );
00265   mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
00266   mAtmListView->setColumnWidth( mAtmColSign,    0 );
00267   mAtmListView->setAllColumnsShowFocus( true );
00268 
00269   connect( mAtmListView,
00270            SIGNAL( doubleClicked( QListViewItem* ) ),
00271            SLOT( slotAttachOpen() ) );
00272   connect( mAtmListView,
00273            SIGNAL( rightButtonPressed( QListViewItem*, const QPoint&, int ) ),
00274            SLOT( slotAttachPopupMenu( QListViewItem*, const QPoint&, int ) ) );
00275   connect( mAtmListView,
00276            SIGNAL( selectionChanged() ),
00277            SLOT( slotUpdateAttachActions() ) );
00278   mAttachMenu = 0;
00279 
00280   readConfig();
00281   setupStatusBar();
00282   setupEditor();
00283   if( !aMsg || aMsg->headerField("X-KMail-CryptoFormat").isEmpty() )
00284   setupActions();
00285   else
00286     setupActions( aMsg->headerField("X-KMail-CryptoFormat").stripWhiteSpace().toInt() );
00287 
00288   applyMainWindowSettings(KMKernel::config(), "Composer");
00289 
00290   connect( mEdtSubject, SIGNAL( subjectTextSpellChecked() ),
00291            SLOT( slotSubjectTextSpellChecked() ) );
00292   connect(mEdtSubject,SIGNAL(textChanged(const QString&)),
00293           SLOT(slotUpdWinTitle(const QString&)));
00294   connect(mBtnTo,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00295   connect(mBtnCc,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00296   connect(mBtnBcc,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00297   connect(mBtnReplyTo,SIGNAL(clicked()),SLOT(slotAddrBookReplyTo()));
00298   //connect(mBtnFrom,SIGNAL(clicked()),SLOT(slotAddrBookFrom()));
00299   connect(mIdentity,SIGNAL(identityChanged(uint)),
00300           SLOT(slotIdentityChanged(uint)));
00301   connect( kmkernel->identityManager(), SIGNAL(changed(uint)),
00302           SLOT(slotIdentityChanged(uint)));
00303 
00304   connect(mEdtTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00305           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00306   connect(mEdtCc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00307           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00308   connect(mEdtBcc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00309           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00310   connect(mEdtReplyTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00311           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00312   connect(mEdtFrom,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00313           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00314         connect(kmkernel->folderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00315                                         SLOT(slotFolderRemoved(KMFolder*)));
00316         connect(kmkernel->imapFolderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00317                                         SLOT(slotFolderRemoved(KMFolder*)));
00318         connect(kmkernel->dimapFolderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00319                                         SLOT(slotFolderRemoved(KMFolder*)));
00320   connect( kmkernel, SIGNAL( configChanged() ),
00321            this, SLOT( slotConfigChanged() ) );
00322 
00323   connect (mEditor, SIGNAL (spellcheck_done(int)),
00324     this, SLOT (slotSpellcheckDone (int)));
00325 
00326   mMainWidget->resize(480,510);
00327   setCentralWidget(mMainWidget);
00328   rethinkFields();
00329 
00330   if (mUseExtEditor) {
00331     mEditor->setUseExternalEditor(true);
00332     mEditor->setExternalEditorPath(mExtEditor);
00333   }
00334 
00335   initAutoSave();
00336 
00337   slotUpdateSignatureActions();
00338 
00339   mMsg = 0;
00340   if (aMsg)
00341     setMsg(aMsg);
00342 
00343   mEdtTo->setFocus();
00344 
00345   mDone = true;
00346 }
00347 
00348 //-----------------------------------------------------------------------------
00349 KMComposeWin::~KMComposeWin()
00350 {
00351   writeConfig();
00352   if (mFolder && mMsg)
00353   {
00354     mAutoDeleteMsg = FALSE;
00355     mFolder->addMsg(mMsg);
00356     // Ensure that the message is correctly and fully parsed
00357     mFolder->unGetMsg( mFolder->count() - 1 );
00358   }
00359   if (mAutoDeleteMsg) {
00360     delete mMsg;
00361     mMsg = 0;
00362   }
00363   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.begin();
00364   while ( it != mMapAtmLoadData.end() )
00365   {
00366     KIO::Job *job = it.key();
00367     mMapAtmLoadData.remove( it );
00368     job->kill();
00369     it = mMapAtmLoadData.begin();
00370   }
00371   deleteAll( mComposedMessages );
00372 }
00373 
00374 void KMComposeWin::setAutoDeleteWindow( bool f )
00375 {
00376   if ( f )
00377     setWFlags( getWFlags() | WDestructiveClose );
00378   else
00379     setWFlags( getWFlags() & ~WDestructiveClose );
00380 }
00381 
00382 //-----------------------------------------------------------------------------
00383 void KMComposeWin::send(int how)
00384 {
00385   switch (how) {
00386     case 1:
00387       slotSendNow();
00388       break;
00389     default:
00390     case 0:
00391       // TODO: find out, what the default send method is and send it this way
00392     case 2:
00393       slotSendLater();
00394       break;
00395   }
00396 }
00397 
00398 //-----------------------------------------------------------------------------
00399 void KMComposeWin::addAttachment(KURL url,QString /*comment*/)
00400 {
00401   addAttach(url);
00402 }
00403 
00404 //-----------------------------------------------------------------------------
00405 void KMComposeWin::addAttachment(const QString &name,
00406                                  const QCString &/*cte*/,
00407                                  const QByteArray &data,
00408                                  const QCString &type,
00409                                  const QCString &subType,
00410                                  const QCString &paramAttr,
00411                                  const QString &paramValue,
00412                                  const QCString &contDisp)
00413 {
00414   if (!data.isEmpty()) {
00415     KMMessagePart *msgPart = new KMMessagePart;
00416     msgPart->setName(name);
00417     if ( type == "message" && subType == "rfc822" ) {
00418       msgPart->setMessageBody( data );
00419     } else {
00420       QValueList<int> dummy;
00421       msgPart->setBodyAndGuessCte(data, dummy,
00422               kmkernel->msgSender()->sendQuotedPrintable());
00423     }
00424     msgPart->setTypeStr(type);
00425     msgPart->setSubtypeStr(subType);
00426     msgPart->setParameter(paramAttr,paramValue);
00427     msgPart->setContentDisposition(contDisp);
00428     addAttach(msgPart);
00429   }
00430 }
00431 
00432 //-----------------------------------------------------------------------------
00433 void KMComposeWin::setBody(QString body)
00434 {
00435   mEditor->setText(body);
00436 }
00437 
00438 //-----------------------------------------------------------------------------
00439 bool KMComposeWin::event(QEvent *e)
00440 {
00441   if (e->type() == QEvent::ApplicationPaletteChange)
00442   {
00443      readColorConfig();
00444   }
00445   return KMail::SecondaryWindow::event(e);
00446 }
00447 
00448 
00449 //-----------------------------------------------------------------------------
00450 void KMComposeWin::readColorConfig(void)
00451 {
00452   KConfig *config = KMKernel::config();
00453   KConfigGroupSaver saver(config, "Reader");
00454   QColor c1=QColor(kapp->palette().active().text());
00455   QColor c4=QColor(kapp->palette().active().base());
00456 
00457   if (!config->readBoolEntry("defaultColors",TRUE)) {
00458     mForeColor = config->readColorEntry("ForegroundColor",&c1);
00459     mBackColor = config->readColorEntry("BackgroundColor",&c4);
00460   }
00461   else {
00462     mForeColor = c1;
00463     mBackColor = c4;
00464   }
00465 
00466   // Color setup
00467   mPalette = kapp->palette();
00468   QColorGroup cgrp  = mPalette.active();
00469   cgrp.setColor( QColorGroup::Base, mBackColor);
00470   cgrp.setColor( QColorGroup::Text, mForeColor);
00471   mPalette.setDisabled(cgrp);
00472   mPalette.setActive(cgrp);
00473   mPalette.setInactive(cgrp);
00474 
00475   mEdtTo->setPalette(mPalette);
00476   mEdtFrom->setPalette(mPalette);
00477   mEdtCc->setPalette(mPalette);
00478   mEdtSubject->setPalette(mPalette);
00479   mEdtReplyTo->setPalette(mPalette);
00480   mEdtBcc->setPalette(mPalette);
00481   mTransport->setPalette(mPalette);
00482   mEditor->setPalette(mPalette);
00483   mFcc->setPalette(mPalette);
00484 }
00485 
00486 //-----------------------------------------------------------------------------
00487 void KMComposeWin::readConfig(void)
00488 {
00489   KConfig *config = KMKernel::config();
00490   QCString str;
00491   //  int w, h,
00492   int maxTransportItems;
00493 
00494   KConfigGroupSaver saver(config, "Composer");
00495 
00496   mDefCharset = KMMessage::defaultCharset();
00497   mForceReplyCharset = config->readBoolEntry("force-reply-charset", false );
00498   mAutoSign = config->readEntry("signature","auto") == "auto";
00499   mShowHeaders = config->readNumEntry("headers", HDR_STANDARD);
00500   mWordWrap = config->readBoolEntry("word-wrap", true);
00501   mUseFixedFont = config->readBoolEntry("use-fixed-font", false);
00502   mLineBreak = config->readNumEntry("break-at", 78);
00503   mBtnIdentity->setChecked(config->readBoolEntry("sticky-identity", false));
00504   if (mBtnIdentity->isChecked())
00505     mId = config->readUnsignedNumEntry("previous-identity", mId );
00506   mBtnFcc->setChecked(config->readBoolEntry("sticky-fcc", false));
00507   QString previousFcc = kmkernel->sentFolder()->idString();
00508   if (mBtnFcc->isChecked())
00509     previousFcc = config->readEntry("previous-fcc", previousFcc );
00510   mBtnTransport->setChecked(config->readBoolEntry("sticky-transport", false));
00511   mTransportHistory = config->readListEntry("transport-history");
00512   QString currentTransport = config->readEntry("current-transport");
00513   maxTransportItems = config->readNumEntry("max-transport-items",10);
00514 
00515   if ((mLineBreak == 0) || (mLineBreak > 78))
00516     mLineBreak = 78;
00517   if (mLineBreak < 30)
00518     mLineBreak = 30;
00519   mOutlookCompatible = config->readBoolEntry( "outlook-compatible-attachments", false );
00520   mAutoPgpSign = config->readBoolEntry("pgp-auto-sign", false);
00521   mAutoPgpEncrypt = config->readBoolEntry("pgp-auto-encrypt", false);
00522   mNeverEncryptWhenSavingInDrafts = config->readBoolEntry("never-encrypt-drafts", true);
00523   mConfirmSend = config->readBoolEntry("confirm-before-send", false);
00524   mAutoRequestMDN = config->readBoolEntry("request-mdn", false);
00525 #ifdef KLEO_CHIASMUS
00526   mChiasmusKey = config->readEntry( "chiasmus-key" );
00527   mChiasmusOptions = config->readEntry( "chiasmus-options" );
00528 #endif
00529 
00530   int mode = config->readNumEntry("Completion Mode",
00531                                   KGlobalSettings::completionMode() );
00532   mEdtFrom->setCompletionMode( (KGlobalSettings::Completion) mode );
00533   mEdtReplyTo->setCompletionMode( (KGlobalSettings::Completion) mode );
00534   mEdtTo->setCompletionMode( (KGlobalSettings::Completion) mode );
00535   mEdtCc->setCompletionMode( (KGlobalSettings::Completion) mode );
00536   mEdtBcc->setCompletionMode( (KGlobalSettings::Completion) mode );
00537 
00538   readColorConfig();
00539 
00540   { // area for config group "General"
00541     KConfigGroupSaver saver(config, "General");
00542     mExtEditor = config->readPathEntry("external-editor", DEFAULT_EDITOR_STR);
00543     mUseExtEditor = config->readBoolEntry("use-external-editor", FALSE);
00544 
00545     int headerCount = config->readNumEntry("mime-header-count", 0);
00546     mCustHeaders.clear();
00547     mCustHeaders.setAutoDelete(true);
00548     for (int i = 0; i < headerCount; i++) {
00549       QString thisGroup;
00550       _StringPair *thisItem = new _StringPair;
00551       thisGroup.sprintf("Mime #%d", i);
00552       KConfigGroupSaver saver(config, thisGroup);
00553       thisItem->name = config->readEntry("name");
00554       if ((thisItem->name).length() > 0) {
00555         thisItem->value = config->readEntry("value");
00556         mCustHeaders.append(thisItem);
00557       } else {
00558         delete thisItem;
00559         thisItem = 0;
00560       }
00561     }
00562   }
00563 
00564   { // area fo config group "Fonts"
00565     KConfigGroupSaver saver(config, "Fonts");
00566     mBodyFont = KGlobalSettings::generalFont();
00567     mFixedFont = KGlobalSettings::fixedFont();
00568     if (!config->readBoolEntry("defaultFonts",TRUE)) {
00569       mBodyFont = config->readFontEntry("composer-font", &mBodyFont);
00570       mFixedFont = config->readFontEntry("fixed-font", &mFixedFont);
00571     }
00572     slotUpdateFont();
00573     mEdtFrom->setFont(mBodyFont);
00574     mEdtReplyTo->setFont(mBodyFont);
00575     mEdtTo->setFont(mBodyFont);
00576     mEdtCc->setFont(mBodyFont);
00577     mEdtBcc->setFont(mBodyFont);
00578     mEdtSubject->setFont(mBodyFont);
00579   }
00580 
00581   { // area fo config group "Fonts"
00582     KConfigGroupSaver saver(config, "Geometry");
00583     QSize defaultSize(480,510);
00584     QSize siz = config->readSizeEntry("composer", &defaultSize);
00585     if (siz.width() < 200) siz.setWidth(200);
00586     if (siz.height() < 200) siz.setHeight(200);
00587     resize(siz);
00588   }
00589 
00590   mIdentity->setCurrentIdentity( mId );
00591 
00592   kdDebug(5006) << "KMComposeWin::readConfig. " << mIdentity->currentIdentityName() << endl;
00593   const KPIM::Identity & ident =
00594     kmkernel->identityManager()->identityForUoid( mIdentity->currentIdentity() );
00595 
00596   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
00597 
00598   mTransport->clear();
00599   mTransport->insertStringList( KMTransportInfo::availableTransports() );
00600   while (mTransportHistory.count() > (uint)maxTransportItems)
00601     mTransportHistory.remove( mTransportHistory.last() );
00602   mTransport->insertStringList( mTransportHistory );
00603   if (mBtnTransport->isChecked() && !currentTransport.isEmpty())
00604   {
00605     for (int i = 0; i < mTransport->count(); i++)
00606       if (mTransport->text(i) == currentTransport)
00607         mTransport->setCurrentItem(i);
00608     mTransport->setEditText( currentTransport );
00609   }
00610 
00611   if ( !mBtnFcc->isChecked() )
00612   {
00613       kdDebug(5006) << "KMComposeWin::readConfig: identity.fcc()='"
00614                     << ident.fcc() << "'" << endl;
00615       if ( ident.fcc().isEmpty() )
00616         previousFcc = kmkernel->sentFolder()->idString();
00617       else
00618         previousFcc = ident.fcc();
00619       kdDebug(5006) << "KMComposeWin::readConfig: previousFcc="
00620                 << previousFcc <<  endl;
00621   }
00622 
00623   setFcc( previousFcc );
00624 }
00625 
00626 //-----------------------------------------------------------------------------
00627 void KMComposeWin::writeConfig(void)
00628 {
00629   KConfig *config = KMKernel::config();
00630   QString str;
00631 
00632   {
00633     KConfigGroupSaver saver(config, "Composer");
00634     config->writeEntry("signature", mAutoSign?"auto":"manual");
00635     config->writeEntry("headers", mShowHeaders);
00636     config->writeEntry("sticky-transport", mBtnTransport->isChecked());
00637     config->writeEntry("sticky-identity", mBtnIdentity->isChecked());
00638     config->writeEntry("sticky-fcc", mBtnFcc->isChecked());
00639     config->writeEntry("previous-identity", mIdentity->currentIdentity() );
00640     config->writeEntry("current-transport", mTransport->currentText());
00641     config->writeEntry("previous-fcc", mFcc->getFolder()->idString() );
00642     config->writeEntry( "autoSpellChecking",
00643                         mAutoSpellCheckingAction->isChecked() );
00644     mTransportHistory.remove(mTransport->currentText());
00645     if (KMTransportInfo::availableTransports().findIndex(mTransport
00646       ->currentText()) == -1)
00647         mTransportHistory.prepend(mTransport->currentText());
00648     config->writeEntry("transport-history", mTransportHistory );
00649     config->writeEntry("use-fixed-font", mUseFixedFont );
00650 #ifdef KLEO_CHIASMUS
00651     config->writeEntry( "chiasmus-key", mChiasmusKey );
00652     config->writeEntry( "chiasmus-options", mChiasmusOptions );
00653 #endif
00654   }
00655 
00656   {
00657     KConfigGroupSaver saver(config, "Geometry");
00658     config->writeEntry("composer", size());
00659 
00660     saveMainWindowSettings(config, "Composer");
00661     config->sync();
00662   }
00663 }
00664 
00665 
00666 //-----------------------------------------------------------------------------
00667 void KMComposeWin::autoSaveMessage()
00668 {
00669   kdDebug(5006) << k_funcinfo << endl;
00670   if ( !mMsg || mComposer || mAutoSaveFilename.isEmpty() )
00671     return;
00672   kdDebug(5006) << k_funcinfo << "autosaving message" << endl;
00673 
00674   if ( mAutoSaveTimer )
00675     mAutoSaveTimer->stop();
00676   connect( this, SIGNAL( applyChangesDone( bool ) ),
00677            this, SLOT( slotContinueAutoSave( bool ) ) );
00678   // This method is called when KMail crashed, so don't try signing/encryption
00679   // and don't disable controls because it is also called from a timer and
00680   // then the disabling is distracting.
00681   applyChanges( true, true );
00682 
00683   // Don't continue before the applyChanges is done!
00684   qApp->enter_loop();
00685 
00686   // Ok, it's done now - continue dead letter saving
00687   if ( mComposedMessages.isEmpty() ) {
00688     kdDebug(5006) << "Composing the message failed." << endl;
00689     return;
00690   }
00691   KMMessage *msg = mComposedMessages.first();
00692 
00693   kdDebug(5006) << k_funcinfo << "opening autoSaveFile " << mAutoSaveFilename
00694                 << endl;
00695   const QString filename =
00696     KMKernel::localDataPath() + "autosave/cur/" + mAutoSaveFilename;
00697   KSaveFile autoSaveFile( filename, 0600 );
00698   int status = autoSaveFile.status();
00699   kdDebug(5006) << k_funcinfo << "autoSaveFile.status() = " << status << endl;
00700   if ( status == 0 ) { // no error
00701     kdDebug(5006) << "autosaving message in " << filename << endl;
00702     int fd = autoSaveFile.handle();
00703     QCString msgStr = msg->asString();
00704     if ( ::write( fd, msgStr, msgStr.length() ) == -1 )
00705       status = errno;
00706   }
00707   if ( status == 0 ) {
00708     kdDebug(5006) << k_funcinfo << "closing autoSaveFile" << endl;
00709     autoSaveFile.close();
00710     mLastAutoSaveErrno = 0;
00711   }
00712   else {
00713     kdDebug(5006) << k_funcinfo << "autosaving failed" << endl;
00714     autoSaveFile.abort();
00715     if ( status != mLastAutoSaveErrno ) {
00716       // don't show the same error message twice
00717       KMessageBox::queuedMessageBox( 0, KMessageBox::Sorry,
00718                                      i18n("Autosaving the message as %1 "
00719                                           "failed.\n"
00720                                           "Reason: %2" )
00721                                      .arg( filename, strerror( status ) ),
00722                                      i18n("Autosaving Failed") );
00723       mLastAutoSaveErrno = status;
00724     }
00725   }
00726 
00727   if ( autoSaveInterval() > 0 )
00728     mAutoSaveTimer->start( autoSaveInterval() );
00729 }
00730 
00731 void KMComposeWin::slotContinueAutoSave( bool )
00732 {
00733   disconnect( this, SIGNAL( applyChangesDone( bool ) ),
00734               this, SLOT( slotContinueAutoSave( bool ) ) );
00735   qApp->exit_loop();
00736 }
00737 
00738 
00739 //-----------------------------------------------------------------------------
00740 void KMComposeWin::slotView(void)
00741 {
00742   if (!mDone)
00743     return; // otherwise called from rethinkFields during the construction
00744             // which is not the intended behavior
00745   int id;
00746 
00747   //This sucks awfully, but no, I cannot get an activated(int id) from
00748   // actionContainer()
00749   if (!sender()->isA("KToggleAction"))
00750     return;
00751   KToggleAction *act = (KToggleAction *) sender();
00752 
00753   if (act == mAllFieldsAction)
00754     id = 0;
00755   else if (act == mIdentityAction)
00756     id = HDR_IDENTITY;
00757   else if (act == mTransportAction)
00758     id = HDR_TRANSPORT;
00759   else if (act == mFromAction)
00760     id = HDR_FROM;
00761   else if (act == mReplyToAction)
00762     id = HDR_REPLY_TO;
00763   else if (act == mToAction)
00764     id = HDR_TO;
00765   else if (act == mCcAction)
00766     id = HDR_CC;
00767   else  if (act == mBccAction)
00768     id = HDR_BCC;
00769   else if (act == mSubjectAction)
00770     id = HDR_SUBJECT;
00771   else if (act == mFccAction)
00772     id = HDR_FCC;
00773   else if ( act == mDictionaryAction )
00774     id = HDR_DICTIONARY;
00775   else
00776    {
00777      id = 0;
00778      kdDebug(5006) << "Something is wrong (Oh, yeah?)" << endl;
00779      return;
00780    }
00781 
00782   // sanders There's a bug here this logic doesn't work if no
00783   // fields are shown and then show all fields is selected.
00784   // Instead of all fields being shown none are.
00785   if (!act->isChecked())
00786   {
00787     // hide header
00788     if (id > 0) mShowHeaders = mShowHeaders & ~id;
00789     else mShowHeaders = abs(mShowHeaders);
00790   }
00791   else
00792   {
00793     // show header
00794     if (id > 0) mShowHeaders |= id;
00795     else mShowHeaders = -abs(mShowHeaders);
00796   }
00797   rethinkFields(true);
00798 
00799 }
00800 
00801 void KMComposeWin::rethinkFields(bool fromSlot)
00802 {
00803   //This sucks even more but again no ids. sorry (sven)
00804   int mask, row, numRows;
00805   long showHeaders;
00806 
00807   if (mShowHeaders < 0)
00808     showHeaders = HDR_ALL;
00809   else
00810     showHeaders = mShowHeaders;
00811 
00812   for (mask=1,mNumHeaders=0; mask<=showHeaders; mask<<=1)
00813     if ((showHeaders&mask) != 0) mNumHeaders++;
00814 
00815   numRows = mNumHeaders + 2;
00816 
00817   delete mGrid;
00818   mGrid = new QGridLayout(mMainWidget, numRows, 3, 4, 4);
00819   mGrid->setColStretch(0, 1);
00820   mGrid->setColStretch(1, 100);
00821   mGrid->setColStretch(2, 1);
00822   mGrid->setRowStretch(mNumHeaders, 100);
00823 
00824   mEdtList.clear();
00825   row = 0;
00826   kdDebug(5006) << "KMComposeWin::rethinkFields" << endl;
00827   if (!fromSlot) mAllFieldsAction->setChecked(showHeaders==HDR_ALL);
00828 
00829   if (!fromSlot) mIdentityAction->setChecked(abs(mShowHeaders)&HDR_IDENTITY);
00830   rethinkHeaderLine(showHeaders,HDR_IDENTITY, row, i18n("&Identity:"),
00831                     mLblIdentity, mIdentity, mBtnIdentity);
00832   if (!fromSlot) mDictionaryAction->setChecked(abs(mShowHeaders)&HDR_DICTIONARY);
00833   rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row, i18n("&Dictionary:"),
00834                     mDictionaryLabel, mDictionaryCombo, 0 );
00835   if (!fromSlot) mFccAction->setChecked(abs(mShowHeaders)&HDR_FCC);
00836   rethinkHeaderLine(showHeaders,HDR_FCC, row, i18n("Se&nt-Mail folder:"),
00837                     mLblFcc, mFcc, mBtnFcc);
00838   if (!fromSlot) mTransportAction->setChecked(abs(mShowHeaders)&HDR_TRANSPORT);
00839   rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row, i18n("Mai&l transport:"),
00840                     mLblTransport, mTransport, mBtnTransport);
00841   if (!fromSlot) mFromAction->setChecked(abs(mShowHeaders)&HDR_FROM);
00842   rethinkHeaderLine(showHeaders,HDR_FROM, row, i18n("&From:"),
00843                     mLblFrom, mEdtFrom /*, mBtnFrom */ );
00844   if (!fromSlot) mReplyToAction->setChecked(abs(mShowHeaders)&HDR_REPLY_TO);
00845   rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,i18n("&Reply to:"),
00846                     mLblReplyTo, mEdtReplyTo, mBtnReplyTo);
00847   if (!fromSlot) mToAction->setChecked(abs(mShowHeaders)&HDR_TO);
00848   rethinkHeaderLine(showHeaders, HDR_TO, row, i18n("To:"),
00849                     mLblTo, mEdtTo, mBtnTo,
00850                     i18n("Primary Recipients"),
00851                     i18n("<qt>The email addresses you put "
00852                          "in this field receive a copy of the email.</qt>"));
00853   if (!fromSlot) mCcAction->setChecked(abs(mShowHeaders)&HDR_CC);
00854   rethinkHeaderLine(showHeaders, HDR_CC, row, i18n("&Copy to (CC):"),
00855                     mLblCc, mEdtCc, mBtnCc,
00856                     i18n("Additional Recipients"),
00857                     i18n("<qt>The email addresses you put "
00858                          "in this field receive a copy of the email. "
00859                          "Technically it is the same thing as putting all the "
00860                          "addresses in the <b>To:</b> field but differs in "
00861                          "that it usually symbolises the receiver of the "
00862                          "Carbon Copy (CC) is a listener, not the main "
00863                          "recipient.</qt>"));
00864   if (!fromSlot) mBccAction->setChecked(abs(mShowHeaders)&HDR_BCC);
00865   rethinkHeaderLine(showHeaders,HDR_BCC, row, i18n("&Blind copy to (BCC):"),
00866                     mLblBcc, mEdtBcc, mBtnBcc,
00867                     i18n("Hidden Recipients"),
00868                     i18n("<qt>Essentially the same thing "
00869                          "as the <b>Copy To:</b> field but differs in that "
00870                          "all other recipients do not see who receives a "
00871                          "blind copy.</qt>"));
00872   if (!fromSlot) mSubjectAction->setChecked(abs(mShowHeaders)&HDR_SUBJECT);
00873   rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, i18n("S&ubject:"),
00874                     mLblSubject, mEdtSubject);
00875   assert(row<=mNumHeaders);
00876 
00877   mGrid->addMultiCellWidget(mEditor, row, mNumHeaders, 0, 2);
00878   mGrid->addMultiCellWidget(mAtmListView, mNumHeaders+1, mNumHeaders+1, 0, 2);
00879 
00880   if( !mAtmList.isEmpty() )
00881     mAtmListView->show();
00882   else
00883     mAtmListView->hide();
00884   resize(this->size());
00885   repaint();
00886 
00887   mGrid->activate();
00888 
00889   slotUpdateAttachActions();
00890   mIdentityAction->setEnabled(!mAllFieldsAction->isChecked());
00891   mDictionaryAction->setEnabled( !mAllFieldsAction->isChecked() );
00892   mTransportAction->setEnabled(!mAllFieldsAction->isChecked());
00893   mFromAction->setEnabled(!mAllFieldsAction->isChecked());
00894   mReplyToAction->setEnabled(!mAllFieldsAction->isChecked());
00895   mToAction->setEnabled(!mAllFieldsAction->isChecked());
00896   mCcAction->setEnabled(!mAllFieldsAction->isChecked());
00897   mBccAction->setEnabled(!mAllFieldsAction->isChecked());
00898   mFccAction->setEnabled(!mAllFieldsAction->isChecked());
00899   mSubjectAction->setEnabled(!mAllFieldsAction->isChecked());
00900 }
00901 
00902 
00903 //-----------------------------------------------------------------------------
00904 void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
00905                                      const QString &aLabelStr, QLabel* aLbl,
00906                                      QLineEdit* aEdt, QPushButton* aBtn,
00907                                      const QString &toolTip, const QString &whatsThis )
00908 {
00909   if (aValue & aMask)
00910   {
00911     aLbl->setText(aLabelStr);
00912     if ( !toolTip.isEmpty() )
00913       QToolTip::add( aLbl, toolTip );
00914     if ( !whatsThis.isEmpty() )
00915       QWhatsThis::add( aLbl, whatsThis );
00916     aLbl->adjustSize();
00917     aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
00918     aLbl->setMinimumSize(aLbl->size());
00919     aLbl->show();
00920     aLbl->setBuddy(aEdt);
00921     mGrid->addWidget(aLbl, aRow, 0);
00922 
00923     aEdt->setBackgroundColor( mBackColor );
00924     aEdt->show();
00925     aEdt->setMinimumSize(100, aLbl->height()+2);
00926     mEdtList.append(aEdt);
00927 
00928     mGrid->addWidget(aEdt, aRow, 1);
00929     if (aBtn)
00930     {
00931       mGrid->addWidget(aBtn, aRow, 2);
00932       aBtn->setFixedSize(aBtn->sizeHint().width(), aLbl->height());
00933       aBtn->show();
00934     }
00935     aRow++;
00936   }
00937   else
00938   {
00939     aLbl->hide();
00940     aEdt->hide();
00941     if (aBtn) aBtn->hide();
00942   }
00943 }
00944 
00945 //-----------------------------------------------------------------------------
00946 void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
00947                                      const QString &aLabelStr, QLabel* aLbl,
00948                                      QComboBox* aCbx, QCheckBox* aChk)
00949 {
00950   if (aValue & aMask)
00951   {
00952     aLbl->setText(aLabelStr);
00953     aLbl->adjustSize();
00954     aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
00955     aLbl->setMinimumSize(aLbl->size());
00956     aLbl->show();
00957     aLbl->setBuddy(aCbx);
00958     mGrid->addWidget(aLbl, aRow, 0);
00959 
00960     //    aCbx->setBackgroundColor( mBackColor );
00961     aCbx->show();
00962     aCbx->setMinimumSize(100, aLbl->height()+2);
00963 
00964     mGrid->addWidget(aCbx, aRow, 1);
00965     if ( aChk ) {
00966       mGrid->addWidget(aChk, aRow, 2);
00967       aChk->setFixedSize(aChk->sizeHint().width(), aLbl->height());
00968       aChk->show();
00969     }
00970     aRow++;
00971   }
00972   else
00973   {
00974     aLbl->hide();
00975     aCbx->hide();
00976     if ( aChk )
00977       aChk->hide();
00978   }
00979 }
00980 
00981 namespace {
00982 
00983 #if KDE_IS_VERSION( 3, 3, 0 )
00984   // KDELIBS 3.2 misses setCheckedState(), so we provide our own
00985   typedef KToggleAction K33ToggleAction;
00986 #else
00987   class K33ToggleAction : public KToggleAction {
00988     KGuiItem * m_checkedGuiItem;
00989   public:
00990     K33ToggleAction( const QString & text, const QString & pix, const KShortcut & cut,
00991                      QObject * parent, const char * name=0 )
00992       : KToggleAction( text, pix, cut, parent, name ),
00993         m_checkedGuiItem( 0 )
00994     {
00995 
00996     }
00997     ~K33ToggleAction() {
00998       delete m_checkedGuiItem; m_checkedGuiItem = 0;
00999     }
01000 
01001     // This is inspired from some kdelibs version beyond 3.3..
01002     void updateChecked( int id ) {
01003       if ( !m_checkedGuiItem ) {
01004         KToggleAction::updateChecked( id );
01005         return;
01006       }
01007 
01008       QWidget *w = container( id );
01009 
01010       if ( ::qt_cast<QPopupMenu *>( w ) ) {
01011         QPopupMenu* pm = static_cast<QPopupMenu*>(w);
01012         int itemId_ = itemId( id );
01013         if ( isChecked() ) {
01014           if ( m_checkedGuiItem->hasIcon() )
01015             pm->changeItem( itemId_, m_checkedGuiItem->iconSet( KIcon::Small ), m_checkedGuiItem->text() );
01016           else
01017             pm->changeItem( itemId_, m_checkedGuiItem->text() );
01018           if ( !m_checkedGuiItem->whatsThis().isEmpty() ) // if empty, we keep the initial one
01019             pm->setWhatsThis( itemId_, m_checkedGuiItem->whatsThis() );
01020         } else {
01021           if ( hasIcon() )
01022             pm->changeItem( itemId_, iconSet( KIcon::Small ), text() );
01023           else
01024             pm->changeItem( itemId_, text() );
01025           pm->setWhatsThis( itemId_, whatsThis() );
01026         }
01027         pm->setItemChecked( itemId_, isChecked() );
01028         updateShortcut( pm, itemId_ );
01029       }
01030       else if ( ::qt_cast<QMenuBar *>( w ) ) // not handled in plug...
01031         static_cast<QMenuBar*>(w)->setItemChecked( itemId( id ), isChecked() );
01032       else if ( ::qt_cast<KToolBar *>( w ) ) {
01033         QWidget* r = static_cast<KToolBar*>( w )->getButton( itemId( id ) );
01034         if ( r && ::qt_cast<KToolBarButton *>( r ) ) {
01035           static_cast<KToolBar*>( w )->setButton( itemId( id ), isChecked() );
01036           if ( m_checkedGuiItem->hasIcon() ) {
01037             const QIconSet iconSet = isChecked()
01038               ? m_checkedGuiItem->iconSet( KIcon::Toolbar )
01039               : this->iconSet( KIcon::Toolbar );
01040             static_cast<KToolBar*>( w )->setButtonIconSet( itemId( id ), iconSet );
01041           }
01042         }
01043       }
01044     }
01045 
01046     void setCheckedState( const KGuiItem & item ) {
01047       delete m_checkedGuiItem;
01048       m_checkedGuiItem = new KGuiItem( item );
01049     }
01050 
01051     QString toolTip() const {
01052       if ( m_checkedGuiItem && isChecked() )
01053         return m_checkedGuiItem->toolTip();
01054       else
01055         return KToggleAction::toolTip();
01056     }
01057   };
01058 
01059 #endif
01060 
01061 }
01062 
01063 //-----------------------------------------------------------------------------
01064 void KMComposeWin::setupActions(int aCryptoMessageFormat)
01065 {
01066   if (kmkernel->msgSender()->sendImmediate()) //default == send now?
01067   {
01068     //default = send now, alternative = queue
01069     (void) new KAction (i18n("&Send"), "mail_send", CTRL+Key_Return,
01070                         this, SLOT(slotSendNow()), actionCollection(),
01071                         "send_default");
01072     (void) new KAction (i18n("&Queue"), "queue", 0,
01073                         this, SLOT(slotSendLater()),
01074                         actionCollection(), "send_alternative");
01075   }
01076   else //no, default = send later
01077   {
01078     //default = queue, alternative = send now
01079     (void) new KAction (i18n("&Queue"), "queue",
01080                         CTRL+Key_Return,
01081                         this, SLOT(slotSendLater()), actionCollection(),
01082                         "send_default");
01083     (void) new KAction (i18n("&Send Now"), "mail_send", 0,
01084                         this, SLOT(slotSendNow()),
01085                         actionCollection(), "send_alternative");
01086   }
01087 
01088   (void) new KAction (i18n("Save in &Drafts Folder"), "filesave", 0,
01089                       this, SLOT(slotSaveDraft()),
01090                       actionCollection(), "save_in_drafts");
01091   (void) new KAction (i18n("&Insert File..."), "fileopen", 0,
01092                       this,  SLOT(slotInsertFile()),
01093                       actionCollection(), "insert_file");
01094   (void) new KAction (i18n("&Address Book"), "contents",0,
01095                       this, SLOT(slotAddrBook()),
01096                       actionCollection(), "addressbook");
01097   (void) new KAction (i18n("&New Composer"), "mail_new",
01098                       KStdAccel::shortcut(KStdAccel::New),
01099                       this, SLOT(slotNewComposer()),
01100                       actionCollection(), "new_composer");
01101   (void) new KAction (i18n("New Main &Window"), "window_new", 0,
01102                       this, SLOT(slotNewMailReader()),
01103                       actionCollection(), "open_mailreader");
01104 
01105 
01106   //KStdAction::save(this, SLOT(), actionCollection(), "save_message");
01107   KStdAction::print (this, SLOT(slotPrint()), actionCollection());
01108   KStdAction::close (this, SLOT(slotClose()), actionCollection());
01109 
01110   KStdAction::undo (this, SLOT(slotUndo()), actionCollection());
01111   KStdAction::redo (this, SLOT(slotRedo()), actionCollection());
01112   KStdAction::cut (this, SLOT(slotCut()), actionCollection());
01113   KStdAction::copy (this, SLOT(slotCopy()), actionCollection());
01114   KStdAction::pasteText (this, SLOT(slotPaste()), actionCollection());
01115   KStdAction::selectAll (this, SLOT(slotMarkAll()), actionCollection());
01116 
01117   KStdAction::find (this, SLOT(slotFind()), actionCollection());
01118   KStdAction::findNext(this, SLOT(slotSearchAgain()), actionCollection());
01119 
01120   KStdAction::replace (this, SLOT(slotReplace()), actionCollection());
01121   KStdAction::spelling (this, SLOT(slotSpellcheck()), actionCollection(), "spellcheck");
01122 
01123   (void) new KAction (i18n("Pa&ste as Quotation"),0,this,SLOT( slotPasteAsQuotation()),
01124                       actionCollection(), "paste_quoted");
01125 
01126   (void) new KAction(i18n("Add &Quote Characters"), 0, this,
01127               SLOT(slotAddQuotes()), actionCollection(), "tools_quote");
01128 
01129   (void) new KAction(i18n("Re&move Quote Characters"), 0, this,
01130               SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote");
01131 
01132 
01133   (void) new KAction (i18n("Cl&ean Spaces"), 0, this, SLOT(slotCleanSpace()),
01134                       actionCollection(), "clean_spaces");
01135 
01136   mFixedFontAction = new KToggleAction( i18n("Use Fi&xed Font"), 0, this,
01137                       SLOT(slotUpdateFont()), actionCollection(), "toggle_fixedfont" );
01138   mFixedFontAction->setChecked(mUseFixedFont);
01139 
01140   //these are checkable!!!
01141   mUrgentAction = new KToggleAction (i18n("&Urgent"), 0,
01142                                     actionCollection(),
01143                                     "urgent");
01144   mRequestMDNAction = new KToggleAction ( i18n("&Request Disposition Notification"), 0,
01145                                          actionCollection(),
01146                                          "options_request_mdn");
01147   mRequestMDNAction->setChecked(mAutoRequestMDN);
01148   //----- Message-Encoding Submenu
01149   mEncodingAction = new KSelectAction( i18n( "Se&t Encoding" ), "charset",
01150                                       0, this, SLOT(slotSetCharset() ),
01151                                       actionCollection(), "charsets" );
01152   mWordWrapAction = new KToggleAction (i18n("&Wordwrap"), 0,
01153                       actionCollection(), "wordwrap");
01154   mWordWrapAction->setChecked(mWordWrap);
01155   connect(mWordWrapAction, SIGNAL(toggled(bool)), SLOT(slotWordWrapToggled(bool)));
01156 
01157   mAutoSpellCheckingAction =
01158     new KToggleAction( i18n( "&Automatic Spellchecking" ), "spellcheck", 0,
01159                        actionCollection(), "options_auto_spellchecking" );
01160   KConfigGroup composerConfig( KMKernel::config(), "Composer" );
01161   const bool spellChecking =
01162     composerConfig.readBoolEntry( "autoSpellChecking", true );
01163   mAutoSpellCheckingAction->setEnabled( !mUseExtEditor );
01164   mAutoSpellCheckingAction->setChecked( !mUseExtEditor && spellChecking );
01165   slotAutoSpellCheckingToggled( !mUseExtEditor && spellChecking );
01166   connect( mAutoSpellCheckingAction, SIGNAL( toggled( bool ) ),
01167            this, SLOT( slotAutoSpellCheckingToggled( bool ) ) );
01168 
01169   QStringList encodings = KMMsgBase::supportedEncodings(TRUE);
01170   encodings.prepend( i18n("Auto-Detect"));
01171   mEncodingAction->setItems( encodings );
01172   mEncodingAction->setCurrentItem( -1 );
01173 
01174   //these are checkable!!!
01175   markupAction = new KToggleAction (i18n("Formatting (HTML)"), 0, this,
01176                                     SLOT(slotToggleMarkup()),
01177                       actionCollection(), "html");
01178   markupAction->setChecked(mUseHTMLEditor);
01179 
01180   mAllFieldsAction = new KToggleAction (i18n("&All Fields"), 0, this,
01181                                        SLOT(slotView()),
01182                                        actionCollection(), "show_all_fields");
01183   mIdentityAction = new KToggleAction (i18n("&Identity"), 0, this,
01184                                       SLOT(slotView()),
01185                                       actionCollection(), "show_identity");
01186   mDictionaryAction = new KToggleAction (i18n("&Dictionary"), 0, this,
01187                                          SLOT(slotView()),
01188                                          actionCollection(), "show_dictionary");
01189   mFccAction = new KToggleAction (i18n("Sent-Mail F&older"), 0, this,
01190                                  SLOT(slotView()),
01191                                  actionCollection(), "show_fcc");
01192   mTransportAction = new KToggleAction (i18n("&Mail Transport"), 0, this,
01193                                       SLOT(slotView()),
01194                                       actionCollection(), "show_transport");
01195   mFromAction = new KToggleAction (i18n("&From"), 0, this,
01196                                   SLOT(slotView()),
01197                                   actionCollection(), "show_from");
01198   mReplyToAction = new KToggleAction (i18n("&Reply To"), 0, this,
01199                                      SLOT(slotView()),
01200                                      actionCollection(), "show_reply_to");
01201   mToAction = new KToggleAction (i18n("&To"), 0, this,
01202                                 SLOT(slotView()),
01203                                 actionCollection(), "show_to");
01204   mCcAction = new KToggleAction (i18n("&CC"), 0, this,
01205                                 SLOT(slotView()),
01206                                 actionCollection(), "show_cc");
01207   mBccAction = new KToggleAction (i18n("&BCC"), 0, this,
01208                                  SLOT(slotView()),
01209                                  actionCollection(), "show_bcc");
01210   mSubjectAction = new KToggleAction (i18n("&Subject"), 0, this,
01211                                      SLOT(slotView()),
01212                                      actionCollection(), "show_subject");
01213   //end of checkable
01214 
01215   mAppendSignatureAction =  new KAction (i18n("Append S&ignature"), 0, this,
01216                       SLOT(slotAppendSignature()),
01217                       actionCollection(), "append_signature");
01218 
01219   mPrependSignatureAction =  new KAction (i18n("Prepend S&ignature"), 0, this,
01220                       SLOT(slotPrependSignature()),
01221                       actionCollection(), "prepend_signature");
01222 
01223   mInsertSignatureAction =  new KAction (i18n("Insert Signature At C&ursor Position"), "edit", 0, this,
01224                       SLOT(slotInsertSignatureAtCursor()),
01225                       actionCollection(), "insert_signature_at_cursor_position");
01226 
01227   mAttachPK  = new KAction (i18n("Attach &Public Key..."), 0, this,
01228                            SLOT(slotInsertPublicKey()),
01229                            actionCollection(), "attach_public_key");
01230   mAttachMPK = new KAction (i18n("Attach &My Public Key"), 0, this,
01231                            SLOT(slotInsertMyPublicKey()),
01232                            actionCollection(), "attach_my_public_key");
01233   (void) new KAction (i18n("&Attach File..."), "attach",
01234                       0, this, SLOT(slotAttachFile()),
01235                       actionCollection(), "attach");
01236   mAttachRemoveAction = new KAction (i18n("&Remove Attachment"), 0, this,
01237                       SLOT(slotAttachRemove()),
01238                       actionCollection(), "remove");
01239   mAttachSaveAction = new KAction (i18n("&Save Attachment As..."), "filesave",0,
01240                       this, SLOT(slotAttachSave()),
01241                       actionCollection(), "attach_save");
01242   mAttachPropertiesAction = new KAction (i18n("Attachment Pr&operties"), 0, this,
01243                       SLOT(slotAttachProperties()),
01244                       actionCollection(), "attach_properties");
01245 
01246   setStandardToolBarMenuEnabled(true);
01247 
01248   KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());
01249   KStdAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection());
01250   KStdAction::preferences(kmkernel, SLOT(slotShowConfigurationDialog()), actionCollection());
01251 
01252   (void) new KAction (i18n("Configure &Spellchecker..."), 0, this, SLOT(slotSpellcheckConfig()),
01253                       actionCollection(), "setup_spellchecker");
01254 
01255   mTextCompletionAction =  new   KSelectAction (i18n("&Text Completion"), 0,
01256                       this, SLOT(slotMenuTextCompletion()),
01257                       actionCollection(), "setup_text_completion");
01258 
01259 
01260   //Adding the Menu Items to the "Text Completion" submenu (Settings)
01261   QStringList menuItems;
01262   for ( int i=0; i < KMGlobalNS::numCompletionModes; ++i )
01263     menuItems <<  i18n( KMGlobalNS::completionModes[i].displayName );
01264 
01265   mTextCompletionAction->setItems( menuItems );
01266 
01267   //Setting the menu to last selected option.
01268   KConfigGroup composer( KMKernel::config(), "Composer" );
01269   const int mode = composer.readNumEntry("Completion Mode", KGlobalSettings::completionMode() );
01270   for ( int i=0; i < KMGlobalNS::numCompletionModes; ++i ) {
01271     if ( KMGlobalNS::completionModes[i].mode == mode )
01272       mTextCompletionAction->setCurrentItem( i );
01273   }
01274 
01275   (void) new KAction (i18n("Configure Completion &Order..."), 0,
01276                       this, SLOT(slotConfigureAddressCompletion()),
01277                       actionCollection(), "setup_completion_order");
01278 
01279   (void) new KAction ( i18n("Configure &Recent Addresses..." ), 0,
01280                        mEdtTo, SLOT( editRecentAddresses() ),
01281                        actionCollection(), "setup_recent_addresses" );
01282 
01283 #ifdef KLEO_CHIASMUS
01284   if ( Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ) ) {
01285     K33ToggleAction * a = new K33ToggleAction( i18n( "Encrypt Message with Chiasmus..." ),
01286                                                "chidecrypted", 0, actionCollection(),
01287                                                "encrypt_message_chiasmus" );
01288     a->setCheckedState( KGuiItem( i18n( "Encrypt Message with Chiasmus..." ), "chiencrypted" ) );
01289     mEncryptChiasmusAction = a;
01290     a = new K33ToggleAction( i18n( "Encrypt Message Body with Chiasmus..." ),
01291                                                "chidecrypted", 0, actionCollection(),
01292                                                "encrypt_message_body_chiasmus" );
01293     a->setCheckedState( KGuiItem( i18n( "Encrypt Message Body with Chiasmus..." ), "chiencrypted" ) );
01294     mEncryptBodyChiasmusAction = a;
01295     mEncryptBodyChiasmusAction->setEnabled( false );
01296 
01297     connect( mEncryptChiasmusAction, SIGNAL(toggled(bool)),
01298              this, SLOT(slotEncryptChiasmusToggled(bool)) );
01299     connect( mEncryptBodyChiasmusAction, SIGNAL(toggled(bool)),
01300              this, SLOT(slotEncryptBodyChiasmusToggled(bool)) );
01301 
01302   } else {
01303     mEncryptChiasmusAction = 0;
01304     mEncryptBodyChiasmusAction = 0;
01305   }
01306 #endif // KLEO_CHIASMUS
01307 
01308   mEncryptAction = new KToggleAction (i18n("&Encrypt Message"),
01309                                      "decrypted", 0,
01310                                      actionCollection(), "encrypt_message");
01311   mSignAction = new KToggleAction (i18n("&Sign Message"),
01312                                   "signature", 0,
01313                                   actionCollection(), "sign_message");
01314   // get PGP user id for the chosen identity
01315   const KPIM::Identity & ident =
01316     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
01317   // PENDING(marc): check the uses of this member and split it into
01318   // smime/openpgp and or enc/sign, if necessary:
01319   mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
01320   mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();
01321 
01322   mLastEncryptActionState = false;
01323   mLastSignActionState = mAutoPgpSign;
01324 
01325   // "Attach public key" is only possible if OpenPGP support is available:
01326   mAttachPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() );
01327 
01328   // "Attach my public key" is only possible if OpenPGP support is
01329   // available and the user specified his key for the current identity:
01330   mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
01331               !ident.pgpEncryptionKey().isEmpty() );
01332 
01333   if ( !Kleo::CryptoBackendFactory::instance()->openpgp() && !Kleo::CryptoBackendFactory::instance()->smime() ) {
01334     // no crypto whatsoever
01335     mEncryptAction->setEnabled( false );
01336     setEncryption( false );
01337     mSignAction->setEnabled( false );
01338     setSigning( false );
01339   } else {
01340     const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp()
01341       && !ident.pgpSigningKey().isEmpty();
01342     const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime()
01343       && !ident.smimeSigningKey().isEmpty();
01344 
01345     setEncryption( false );
01346     setSigning( ( canOpenPGPSign || canSMIMESign ) && mAutoPgpSign );
01347   }
01348 
01349   connect(mEncryptAction, SIGNAL(toggled(bool)),
01350                          SLOT(slotEncryptToggled( bool )));
01351   connect(mSignAction,    SIGNAL(toggled(bool)),
01352                          SLOT(slotSignToggled(    bool )));
01353 
01354   QStringList l;
01355   for ( int i = 0 ; i < numCryptoMessageFormats ; ++i )
01356     l.push_back( Kleo::cryptoMessageFormatToLabel( cryptoMessageFormats[i] ) );
01357 
01358   mCryptoModuleAction = new KSelectAction( i18n( "&Cryptographic Message Format" ), 0,
01359                        this, SLOT(slotSelectCryptoModule()),
01360                        actionCollection(), "options_select_crypto" );
01361   mCryptoModuleAction->setItems( l );
01362   mCryptoModuleAction->setCurrentItem(
01363     format2cb(   (0 <= aCryptoMessageFormat)
01364                ? (Kleo::CryptoMessageFormat)aCryptoMessageFormat
01365                : ident.preferredCryptoMessageFormat() ) );
01366 
01367   slotSelectCryptoModule();
01368 
01369   QStringList styleItems;
01370   styleItems << i18n( "Standard" );
01371   styleItems << i18n( "Bulleted List (Disc)" );
01372   styleItems << i18n( "Bulleted List (Circle)" );
01373   styleItems << i18n( "Bulleted List (Square)" );
01374   styleItems << i18n( "Ordered List (Decimal)" );
01375   styleItems << i18n( "Ordered List (Alpha lower)" );
01376   styleItems << i18n( "Ordered List (Alpha upper)" );
01377 
01378   listAction = new KSelectAction( i18n( "Select Style" ), 0, actionCollection(),
01379                                  "text_list" );
01380   listAction->setItems( styleItems );
01381   connect( listAction, SIGNAL( activated( const QString& ) ),
01382            SLOT( slotListAction( const QString& ) ) );
01383   fontAction = new KFontAction( "Select Font", 0, actionCollection(),
01384                                "text_font" );
01385   connect( fontAction, SIGNAL( activated( const QString& ) ),
01386            SLOT( slotFontAction( const QString& ) ) );
01387   fontSizeAction = new KFontSizeAction( "Select Size", 0, actionCollection(),
01388                                        "text_size" );
01389   connect( fontSizeAction, SIGNAL( fontSizeChanged( int ) ),
01390            SLOT( slotSizeAction( int ) ) );
01391 
01392   alignLeftAction = new KToggleAction (i18n("Align Left"), "text_left", 0,
01393                       this, SLOT(slotAlignLeft()), actionCollection(),
01394                       "align_left");
01395   alignLeftAction->setChecked( TRUE );
01396   alignRightAction = new KToggleAction (i18n("Align Right"), "text_right", 0,
01397                       this, SLOT(slotAlignRight()), actionCollection(),
01398                       "align_right");
01399   alignCenterAction = new KToggleAction (i18n("Align Center"), "text_center", 0,
01400                        this, SLOT(slotAlignCenter()), actionCollection(),
01401                        "align_center");
01402   textBoldAction = new KToggleAction (i18n("&Bold"), "text_bold", 0,
01403                                      this, SLOT(slotTextBold()),
01404                                      actionCollection(), "text_bold");
01405   textItalicAction = new KToggleAction (i18n("&Italic"), "text_italic", 0,
01406                                        this, SLOT(slotTextItalic()),
01407                                        actionCollection(), "text_italic");
01408   textUnderAction = new KToggleAction (i18n("&Underline"), "text_under", 0,
01409                                      this, SLOT(slotTextUnder()),
01410                                      actionCollection(), "text_under");
01411   actionFormatReset = new KAction( i18n( "Reset Font Settings" ), "eraser", 0,
01412                                      this, SLOT( slotFormatReset() ),
01413                                      actionCollection(), "format_reset");
01414   actionFormatColor = new KAction( i18n( "Text Color..." ), "colorize", 0,
01415                                      this, SLOT( slotTextColor() ),
01416                                      actionCollection(), "format_color");
01417 
01418 
01419   createGUI("kmcomposerui.rc");
01420   // In Kontact, this entry would read "Configure Kontact", but bring
01421   // up KMail's config dialog. That's sensible, though, so fix the label.
01422   KAction* configureAction = actionCollection()->action("options_configure" );
01423   if ( configureAction )
01424     configureAction->setText( i18n("Configure KMail" ) );
01425 }
01426 
01427 //-----------------------------------------------------------------------------
01428 void KMComposeWin::setupStatusBar(void)
01429 {
01430   statusBar()->insertItem("", 0, 1);
01431   statusBar()->setItemAlignment(0, AlignLeft | AlignVCenter);
01432 
01433   statusBar()->insertItem(i18n(" Column: %1 ").arg("     "),2,0,true);
01434   statusBar()->insertItem(i18n(" Line: %1 ").arg("     "),1,0,true);
01435 }
01436 
01437 
01438 //-----------------------------------------------------------------------------
01439 void KMComposeWin::updateCursorPosition()
01440 {
01441   int col,line;
01442   QString temp;
01443   line = mEditor->currentLine();
01444   col = mEditor->currentColumn();
01445   temp = i18n(" Line: %1 ").arg(line+1);
01446   statusBar()->changeItem(temp,1);
01447   temp = i18n(" Column: %1 ").arg(col+1);
01448   statusBar()->changeItem(temp,2);
01449 }
01450 
01451 
01452 //-----------------------------------------------------------------------------
01453 void KMComposeWin::setupEditor(void)
01454 {
01455   //QPopupMenu* menu;
01456   mEditor->setModified(FALSE);
01457   QFontMetrics fm(mBodyFont);
01458   mEditor->setTabStopWidth(fm.width(QChar(' ')) * 8);
01459   //mEditor->setFocusPolicy(QWidget::ClickFocus);
01460 
01461   if (mWordWrap)
01462   {
01463     mEditor->setWordWrap( QMultiLineEdit::FixedColumnWidth );
01464     mEditor->setWrapColumnOrWidth(mLineBreak);
01465   }
01466   else
01467   {
01468     mEditor->setWordWrap( QMultiLineEdit::NoWrap );
01469   }
01470 
01471   // Font setup
01472   slotUpdateFont();
01473 
01474   /* installRBPopup() is broken in kdelibs, we should wait for
01475           the new klibtextedit (dnaber, 2002-01-01)
01476   menu = new QPopupMenu(this);
01477   //#ifdef BROKEN
01478   menu->insertItem(i18n("Undo"),mEditor,
01479                    SLOT(undo()), KStdAccel::shortcut(KStdAccel::Undo));
01480   menu->insertItem(i18n("Redo"),mEditor,
01481                    SLOT(redo()), KStdAccel::shortcut(KStdAccel::Redo));
01482   menu->insertSeparator();
01483   //#endif //BROKEN
01484   menu->insertItem(i18n("Cut"), this, SLOT(slotCut()));
01485   menu->insertItem(i18n("Copy"), this, SLOT(slotCopy()));
01486   menu->insertItem(i18n("Paste"), this, SLOT(slotPaste()));
01487   menu->insertItem(i18n("Mark All"),this, SLOT(slotMarkAll()));
01488   menu->insertSeparator();
01489   menu->insertItem(i18n("Find..."), this, SLOT(slotFind()));
01490   menu->insertItem(i18n("Replace..."), this, SLOT(slotReplace()));
01491   menu->insertSeparator();
01492   menu->insertItem(i18n("Fixed Font Widths"), this, SLOT(slotUpdateFont()));
01493   mEditor->installRBPopup(menu);
01494   */
01495   updateCursorPosition();
01496   connect(mEditor,SIGNAL(CursorPositionChanged()),SLOT(updateCursorPosition()));
01497   connect( mEditor, SIGNAL( currentFontChanged( const QFont & ) ),
01498           this, SLOT( fontChanged( const QFont & ) ) );
01499   connect( mEditor, SIGNAL( currentAlignmentChanged( int ) ),
01500           this, SLOT( alignmentChanged( int ) ) );
01501 
01502 }
01503 
01504 
01505 //-----------------------------------------------------------------------------
01506 static QString cleanedUpHeaderString( const QString & s )
01507 {
01508   // remove invalid characters from the header strings
01509   QString res( s );
01510   res.replace( '\r', "" );
01511   res.replace( '\n', " " );
01512   return res.stripWhiteSpace();
01513 }
01514 
01515 static QString sanitizedSeparators( const QString& s )
01516 {
01517     const bool allowSemicolon = GlobalSettings::allowSemicolonAsAddressSeparator();
01518     return KPIM::splitEmailAddrList( s, allowSemicolon ).join( "," );
01519 }
01520 
01521 //-----------------------------------------------------------------------------
01522 QString KMComposeWin::subject() const
01523 {
01524   return cleanedUpHeaderString( mEdtSubject->text() );
01525 }
01526 
01527 //-----------------------------------------------------------------------------
01528 QString KMComposeWin::to() const
01529 {
01530     kdDebug() << "TO: " << kdBacktrace() << endl;
01531   return sanitizedSeparators( cleanedUpHeaderString( mEdtTo->text() ) );
01532 }
01533 
01534 //-----------------------------------------------------------------------------
01535 QString KMComposeWin::cc() const
01536 {
01537   if ( mEdtCc->isHidden() )
01538     return QString::null;
01539   else
01540     return sanitizedSeparators( cleanedUpHeaderString( mEdtCc->text() ) );
01541 }
01542 
01543 //-----------------------------------------------------------------------------
01544 QString KMComposeWin::bcc() const
01545 {
01546   if ( mEdtBcc->isHidden() )
01547     return QString::null;
01548   else
01549     return sanitizedSeparators( cleanedUpHeaderString( mEdtBcc->text() ) );
01550 }
01551 
01552 //-----------------------------------------------------------------------------
01553 QString KMComposeWin::from() const
01554 {
01555   return cleanedUpHeaderString( mEdtFrom->text() );
01556 }
01557 
01558 //-----------------------------------------------------------------------------
01559 QString KMComposeWin::replyTo() const
01560 {
01561   return cleanedUpHeaderString( mEdtReplyTo->text() );
01562 }
01563 
01564 //-----------------------------------------------------------------------------
01565 void KMComposeWin::verifyWordWrapLengthIsAdequate(const QString &body)
01566 {
01567   int maxLineLength = 0;
01568   int curPos;
01569   int oldPos = 0;
01570   if (mEditor->QMultiLineEdit::wordWrap() == QMultiLineEdit::FixedColumnWidth) {
01571     for (curPos = 0; curPos < (int)body.length(); ++curPos)
01572         if (body[curPos] == '\n') {
01573           if ((curPos - oldPos) > maxLineLength)
01574             maxLineLength = curPos - oldPos;
01575           oldPos = curPos;
01576         }
01577     if ((curPos - oldPos) > maxLineLength)
01578       maxLineLength = curPos - oldPos;
01579     if (mEditor->wrapColumnOrWidth() < maxLineLength) // column
01580       mEditor->setWrapColumnOrWidth(maxLineLength);
01581   }
01582 }
01583 
01584 //-----------------------------------------------------------------------------
01585 void KMComposeWin::decryptOrStripOffCleartextSignature( QCString& body )
01586 {
01587   QPtrList<Kpgp::Block> pgpBlocks;
01588   QStrList nonPgpBlocks;
01589   if( Kpgp::Module::prepareMessageForDecryption( body,
01590                                                  pgpBlocks, nonPgpBlocks ) )
01591   {
01592     // Only decrypt/strip off the signature if there is only one OpenPGP
01593     // block in the message
01594     if( pgpBlocks.count() == 1 )
01595     {
01596       Kpgp::Block* block = pgpBlocks.first();
01597       if( ( block->type() == Kpgp::PgpMessageBlock ) ||
01598           ( block->type() == Kpgp::ClearsignedBlock ) )
01599       {
01600         if( block->type() == Kpgp::PgpMessageBlock )
01601           // try to decrypt this OpenPGP block
01602           block->decrypt();
01603         else
01604           // strip off the signature
01605           block->verify();
01606 
01607         body = nonPgpBlocks.first()
01608              + block->text()
01609              + nonPgpBlocks.last();
01610       }
01611     }
01612   }
01613 }
01614 
01615 //-----------------------------------------------------------------------------
01616 void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
01617                           bool allowDecryption, bool isModified)
01618 {
01619   //assert(newMsg!=0);
01620   if(!newMsg)
01621     {
01622       kdDebug(5006) << "KMComposeWin::setMsg() : newMsg == 0!" << endl;
01623       return;
01624     }
01625   mMsg = newMsg;
01626 
01627   mEdtTo->setText(mMsg->to());
01628   mEdtFrom->setText(mMsg->from());
01629   mEdtCc->setText(mMsg->cc());
01630   mEdtSubject->setText(mMsg->subject());
01631   mEdtReplyTo->setText(mMsg->replyTo());
01632   mEdtBcc->setText(mMsg->bcc());
01633 
01634   if (!mBtnIdentity->isChecked() && !newMsg->headerField("X-KMail-Identity").isEmpty())
01635     mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
01636 
01637   // don't overwrite the header values with identity specific values
01638   // unless the identity is sticky
01639   if ( !mBtnIdentity->isChecked() ) {
01640     disconnect(mIdentity,SIGNAL(identityChanged(uint)),
01641                this, SLOT(slotIdentityChanged(uint)));
01642   }
01643    mIdentity->setCurrentIdentity( mId );
01644   if ( !mBtnIdentity->isChecked() ) {
01645     connect(mIdentity,SIGNAL(identityChanged(uint)),
01646             this, SLOT(slotIdentityChanged(uint)));
01647   }
01648   else {
01649     // make sure the header values are overwritten with the values of the
01650     // sticky identity (the slot isn't called by the signal for new messages
01651     // since the identity has already been set before the signal was connected)
01652     slotIdentityChanged( mId );
01653   }
01654 
01655   KPIM::IdentityManager * im = kmkernel->identityManager();
01656 
01657   const KPIM::Identity & ident = im->identityForUoid( mIdentity->currentIdentity() );
01658 
01659   mOldSigText = GlobalSettings::prependSignatures()? ident.signature().rawText() : ident.signatureText();
01660 
01661   // check for the presence of a DNT header, indicating that MDN's were
01662   // requested
01663   QString mdnAddr = newMsg->headerField("Disposition-Notification-To");
01664   mRequestMDNAction->setChecked( ( !mdnAddr.isEmpty() &&
01665                                   im->thatIsMe( mdnAddr ) ) || mAutoRequestMDN );
01666 
01667   // check for presence of a priority header, indicating urgent mail:
01668   mUrgentAction->setChecked( newMsg->isUrgent() );
01669 
01670   // enable/disable encryption if the message was/wasn't encrypted
01671   switch ( mMsg->encryptionState() ) {
01672     case KMMsgFullyEncrypted: // fall through
01673     case KMMsgPartiallyEncrypted:
01674       mLastEncryptActionState = true;
01675       break;
01676     case KMMsgNotEncrypted:
01677       mLastEncryptActionState = false;
01678       break;
01679     default: // nothing
01680       break;
01681   }
01682 
01683   // enable/disable signing if the message was/wasn't signed
01684   switch ( mMsg->signatureState() ) {
01685     case KMMsgFullySigned: // fall through
01686     case KMMsgPartiallySigned:
01687       mLastSignActionState = true;
01688       break;
01689     case KMMsgNotSigned:
01690       mLastSignActionState = false;
01691       break;
01692     default: // nothing
01693       break;
01694   }
01695 
01696   mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
01697   mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();
01698 
01699   if( !mMsg->headerField("X-KMail-CryptoFormat").isEmpty() ){
01700     const int format = mMsg->headerField("X-KMail-CryptoFormat").stripWhiteSpace().toInt();
01701     if( 0 <= format ){
01702       mCryptoModuleAction->setCurrentItem(
01703         format2cb( (Kleo::CryptoMessageFormat)format ) );
01704       slotSelectCryptoModule();
01705     }
01706   }
01707 
01708   if ( Kleo::CryptoBackendFactory::instance()->openpgp() || Kleo::CryptoBackendFactory::instance()->smime() ) {
01709     const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp()
01710       && !ident.pgpSigningKey().isEmpty();
01711     const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime()
01712       && !ident.smimeSigningKey().isEmpty();
01713 
01714     setEncryption( mLastEncryptActionState );
01715     setSigning( ( canOpenPGPSign || canSMIMESign ) && mLastSignActionState );
01716   }
01717 
01718   // "Attach my public key" is only possible if the user uses OpenPGP
01719   // support and he specified his key:
01720   mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
01721               !ident.pgpEncryptionKey().isEmpty() );
01722 
01723   QString transport = newMsg->headerField("X-KMail-Transport");
01724   if (!mBtnTransport->isChecked() && !transport.isEmpty())
01725   {
01726     for (int i = 0; i < mTransport->count(); i++)
01727       if (mTransport->text(i) == transport)
01728         mTransport->setCurrentItem(i);
01729     mTransport->setEditText( transport );
01730   }
01731 
01732   if (!mBtnFcc->isChecked())
01733   {
01734     if (!mMsg->fcc().isEmpty())
01735       setFcc(mMsg->fcc());
01736     else
01737       setFcc(ident.fcc());
01738   }
01739 
01740   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
01741 
01742   partNode * root = partNode::fromMessage( mMsg );
01743 
01744   KMail::ObjectTreeParser otp; // all defaults are ok
01745   otp.parseObjectTree( root );
01746 
01747   KMail::AttachmentCollector ac;
01748   ac.setDiveIntoEncryptions( true );
01749   ac.setDiveIntoSignatures( true );
01750   ac.setDiveIntoMessages( false );
01751 
01752   ac.collectAttachmentsFrom( root );
01753 
01754   for ( std::vector<partNode*>::const_iterator it = ac.attachments().begin() ; it != ac.attachments().end() ; ++it )
01755     addAttach( new KMMessagePart( (*it)->msgPart() ) );
01756 
01757   mEditor->setText( otp.textualContent() );
01758   mCharset = otp.textualContentCharset();
01759   if ( mCharset.isEmpty() )
01760     mCharset = mMsg->charset();
01761   if ( mCharset.isEmpty() )
01762     mCharset = mDefCharset;
01763   setCharset( mCharset );
01764 
01765   if ( partNode * n = root->findType( DwMime::kTypeText, DwMime::kSubtypeHtml ) )
01766     if ( partNode * p = n->parentNode() )
01767       if ( p->hasType( DwMime::kTypeMultipart ) &&
01768            p->hasSubType( DwMime::kSubtypeAlternative ) )
01769         if ( mMsg->headerField( "X-KMail-Markup" ) == "true" )
01770           toggleMarkup( true );
01771 
01772   /* Handle the special case of non-mime mails */
01773   if ( mMsg->numBodyParts() == 0 && otp.textualContent().isEmpty() ) {
01774     mCharset=mMsg->charset();
01775     if ( mCharset.isEmpty() ||  mCharset == "default" )
01776       mCharset = mDefCharset;
01777 
01778     QCString bodyDecoded = mMsg->bodyDecoded();
01779 
01780     if( allowDecryption )
01781       decryptOrStripOffCleartextSignature( bodyDecoded );
01782 
01783     const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
01784     if (codec) {
01785       mEditor->setText(codec->toUnicode(bodyDecoded));
01786     } else
01787       mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
01788   }
01789 
01790 
01791 #ifdef BROKEN_FOR_OPAQUE_SIGNED_OR_ENCRYPTED_MAILS
01792   const int num = mMsg->numBodyParts();
01793   kdDebug(5006) << "KMComposeWin::setMsg() mMsg->numBodyParts="
01794                 << mMsg->numBodyParts() << endl;
01795 
01796   if ( num > 0 ) {
01797     KMMessagePart bodyPart;
01798     int firstAttachment = 0;
01799 
01800     mMsg->bodyPart(1, &bodyPart);
01801     if ( bodyPart.typeStr().lower() == "text" &&
01802          bodyPart.subtypeStr().lower() == "html" ) {
01803       // check whether we are inside a mp/al body part
01804       partNode *root = partNode::fromMessage( mMsg );
01805       partNode *node = root->findType( DwMime::kTypeText,
01806                                        DwMime::kSubtypeHtml );
01807       if ( node && node->parentNode() &&
01808            node->parentNode()->hasType( DwMime::kTypeMultipart ) &&
01809            node->parentNode()->hasSubType( DwMime::kSubtypeAlternative ) ) {
01810         // we have a mp/al body part with a text and an html body
01811       kdDebug(5006) << "KMComposeWin::setMsg() : text/html found" << endl;
01812       firstAttachment = 2;
01813         if ( mMsg->headerField( "X-KMail-Markup" ) == "true" )
01814           toggleMarkup( true );
01815       }
01816       delete root; root = 0;
01817     }
01818     if ( firstAttachment == 0 ) {
01819         mMsg->bodyPart(0, &bodyPart);
01820         if ( bodyPart.typeStr().lower() == "text" ) {
01821           // we have a mp/mx body with a text body
01822         kdDebug(5006) << "KMComposeWin::setMsg() : text/* found" << endl;
01823           firstAttachment = 1;
01824         }
01825       }
01826 
01827     if ( firstAttachment != 0 ) // there's text to show
01828     {
01829       mCharset = bodyPart.charset();
01830       if ( mCharset.isEmpty() || mCharset == "default" )
01831         mCharset = mDefCharset;
01832 
01833       QCString bodyDecoded = bodyPart.bodyDecoded();
01834 
01835       if( allowDecryption )
01836         decryptOrStripOffCleartextSignature( bodyDecoded );
01837 
01838       // As nobody seems to know the purpose of the following line and
01839       // as it breaks word wrapping of long lines if drafts with attachments
01840       // are opened for editting in the composer (cf. Bug#41102) I comment it
01841       // out. Ingo, 2002-04-21
01842       //verifyWordWrapLengthIsAdequate(bodyDecoded);
01843 
01844       const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
01845       if (codec)
01846         mEditor->setText(codec->toUnicode(bodyDecoded));
01847       else
01848         mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
01849       //mEditor->insertLine("\n", -1); <-- why ?
01850     } else mEditor->setText("");
01851     for( int i = firstAttachment; i < num; ++i )
01852     {
01853       KMMessagePart *msgPart = new KMMessagePart;
01854       mMsg->bodyPart(i, msgPart);
01855       QCString mimeType = msgPart->typeStr().lower() + '/'
01856                         + msgPart->subtypeStr().lower();
01857       // don't add the detached signature as attachment when editting a
01858       // PGP/MIME signed message
01859       if( mimeType != "application/pgp-signature" ) {
01860         addAttach(msgPart);
01861       }
01862     }
01863   } else{
01864     mCharset=mMsg->charset();
01865     if ( mCharset.isEmpty() ||  mCharset == "default" )
01866       mCharset = mDefCharset;
01867 
01868     QCString bodyDecoded = mMsg->bodyDecoded();
01869 
01870     if( allowDecryption )
01871       decryptOrStripOffCleartextSignature( bodyDecoded );
01872 
01873     const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
01874     if (codec) {
01875       mEditor->setText(codec->toUnicode(bodyDecoded));
01876     } else
01877       mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
01878   }
01879 
01880   setCharset(mCharset);
01881 #endif // BROKEN_FOR_OPAQUE_SIGNED_OR_ENCRYPTED_MAILS
01882 
01883   if( mAutoSign && mayAutoSign ) {
01884     //
01885     // Espen 2000-05-16
01886     // Delay the signature appending. It may start a fileseletor.
01887     // Not user friendy if this modal fileseletor opens before the
01888     // composer.
01889     //
01890       if ( GlobalSettings::prependSignatures() ) {
01891         QTimer::singleShot( 0, this, SLOT(slotPrependSignature()) );
01892       } else {
01893         QTimer::singleShot( 0, this, SLOT(slotAppendSignature()) );
01894       }
01895 
01896   }
01897   mEditor->setModified(isModified);
01898 }
01899 
01900 
01901 //-----------------------------------------------------------------------------
01902 void KMComposeWin::setFcc( const QString &idString )
01903 {
01904   // check if the sent-mail folder still exists
01905   KMFolder *folder = kmkernel->findFolderById( idString );
01906   if ( folder )
01907     mFcc->setFolder( idString );
01908   else
01909     mFcc->setFolder( kmkernel->sentFolder() );
01910 }
01911 
01912 
01913 //-----------------------------------------------------------------------------
01914 bool KMComposeWin::queryClose ()
01915 {
01916   if ( !mEditor->checkExternalEditorFinished() )
01917     return false;
01918   if (kmkernel->shuttingDown() || kapp->sessionSaving())
01919     return true;
01920 
01921   if(mEditor->isModified() || mEdtFrom->edited() || mEdtReplyTo->edited() ||
01922      mEdtTo->edited() || mEdtCc->edited() || mEdtBcc->edited() ||
01923      mEdtSubject->edited() || mAtmModified ||
01924      (mTransport->lineEdit() && mTransport->lineEdit()->edited()))
01925   {
01926     const int rc = KMessageBox::warningYesNoCancel(this,
01927            i18n("Do you want to save the message for later or discard it?"),
01928            i18n("Close Composer"),
01929            i18n("&Save as Draft"),
01930            KStdGuiItem::discard() );
01931     if (rc == KMessageBox::Cancel)
01932       return false;
01933     else if (rc == KMessageBox::Yes) {
01934       // doSend will close the window. Just return false from this method
01935       slotSaveDraft();
01936       return false;
01937     }
01938   }
01939   cleanupAutoSave();
01940   return true;
01941 }
01942 
01943 //-----------------------------------------------------------------------------
01944 bool KMComposeWin::userForgotAttachment()
01945 {
01946   KConfigGroup composer( KMKernel::config(), "Composer" );
01947   bool checkForForgottenAttachments =
01948     composer.readBoolEntry( "showForgottenAttachmentWarning", true );
01949 
01950   if ( !checkForForgottenAttachments || ( mAtmList.count() > 0 ) )
01951     return false;
01952 
01953 
01954   QStringList attachWordsList =
01955     composer.readListEntry( "attachment-keywords" );
01956 
01957   if ( attachWordsList.isEmpty() ) {
01958     // default value (FIXME: this is duplicated in configuredialog.cpp)
01959     attachWordsList << QString::fromLatin1("attachment")
01960                     << QString::fromLatin1("attached");
01961     if ( QString::fromLatin1("attachment") != i18n("attachment") )
01962       attachWordsList << i18n("attachment");
01963     if ( QString::fromLatin1("attached") != i18n("attached") )
01964       attachWordsList << i18n("attached");
01965   }
01966 
01967   QRegExp rx ( QString::fromLatin1("\\b") +
01968                attachWordsList.join("\\b|\\b") +
01969                QString::fromLatin1("\\b") );
01970   rx.setCaseSensitive( false );
01971 
01972   bool gotMatch = false;
01973 
01974   // check whether the subject contains one of the attachment key words
01975   // unless the message is a reply or a forwarded message
01976   QString subj = subject();
01977   gotMatch =    ( KMMessage::stripOffPrefixes( subj ) == subj )
01978              && ( rx.search( subj ) >= 0 );
01979 
01980   if ( !gotMatch ) {
01981     // check whether the non-quoted text contains one of the attachment key
01982     // words
01983     QRegExp quotationRx ("^([ \\t]*([|>:}#]|[A-Za-z]+>))+");
01984     for ( int i = 0; i < mEditor->numLines(); ++i ) {
01985       QString line = mEditor->textLine( i );
01986       gotMatch =    ( quotationRx.search( line ) < 0 )
01987                  && ( rx.search( line ) >= 0 );
01988       if ( gotMatch )
01989         break;
01990     }
01991   }
01992 
01993   if ( !gotMatch )
01994     return false;
01995 
01996   int rc = KMessageBox::warningYesNoCancel( this,
01997              i18n("The message you have composed seems to refer to an "
01998                   "attached file but you have not attached anything.\n"
01999                   "Do you want to attach a file to your message?"),
02000              i18n("File Attachment Reminder"),
02001              i18n("&Attach File..."),
02002              i18n("&Send as Is") );
02003   if ( rc == KMessageBox::Cancel )
02004     return true;
02005   if ( rc == KMessageBox::Yes ) {
02006     slotAttachFile();
02007     //preceed with editing
02008     return true;
02009   }
02010   return false;
02011 }
02012 
02013 //-----------------------------------------------------------------------------
02014 void KMComposeWin::applyChanges( bool dontSignNorEncrypt, bool dontDisable )
02015 {
02016 #ifdef DEBUG
02017   kdDebug(5006) << "entering KMComposeWin::applyChanges" << endl;
02018 #endif
02019 
02020   if(!mMsg) {
02021     kdDebug(5006) << "KMComposeWin::applyChanges() : mMsg == 0!\n" << endl;
02022     emit applyChangesDone( false );
02023     return;
02024   }
02025 
02026   if( mComposer ) {
02027     kdDebug(5006) << "KMComposeWin::applyChanges() : applyChanges called twice"
02028                   << endl;
02029     return;
02030   }
02031 
02032   // Make new job and execute it
02033   mComposer = new MessageComposer( this );
02034   connect( mComposer, SIGNAL( done( bool ) ),
02035            this, SLOT( slotComposerDone( bool ) ) );
02036 
02037   // TODO: Add a cancel button for this operation
02038   if ( !dontDisable )
02039     setEnabled( false );
02040 
02041   mComposer->setDisableBreaking( mDisableBreaking );
02042   mComposer->applyChanges( dontSignNorEncrypt );
02043 }
02044 
02045 void KMComposeWin::slotComposerDone( bool rc )
02046 {
02047   deleteAll( mComposedMessages );
02048   mComposedMessages = mComposer->composedMessageList();
02049   emit applyChangesDone( rc );
02050   delete mComposer;
02051   mComposer = 0;
02052 }
02053 
02054 const KPIM::Identity & KMComposeWin::identity() const {
02055   return kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
02056 }
02057 
02058 Kleo::CryptoMessageFormat KMComposeWin::cryptoMessageFormat() const {
02059   if ( !mCryptoModuleAction )
02060     return Kleo::AutoFormat;
02061   return cb2format( mCryptoModuleAction->currentItem() );
02062 }
02063 
02064 bool KMComposeWin::encryptToSelf() const {
02065   return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
02066 }
02067 
02068 bool KMComposeWin::queryExit ()
02069 {
02070   return true;
02071 }
02072 
02073 //-----------------------------------------------------------------------------
02074 void KMComposeWin::addAttach(const KURL aUrl)
02075 {
02076   if ( !aUrl.isValid() ) {
02077     KMessageBox::sorry( this, i18n( "<qt><p>KMail could not recognize the location of the attachment (%1);</p>"
02078                                  "<p>you have to specify the full path if you wish to attach a file.</p></qt>" )
02079                         .arg( aUrl.prettyURL() ) );
02080     return;
02081   }
02082   KIO::TransferJob *job = KIO::get(aUrl);
02083   KIO::Scheduler::scheduleJob( job );
02084   atmLoadData ld;
02085   ld.url = aUrl;
02086   ld.data = QByteArray();
02087   ld.insert = false;
02088   if( !aUrl.fileEncoding().isEmpty() )
02089     ld.encoding = aUrl.fileEncoding().latin1();
02090 
02091   mMapAtmLoadData.insert(job, ld);
02092   connect(job, SIGNAL(result(KIO::Job *)),
02093           this, SLOT(slotAttachFileResult(KIO::Job *)));
02094   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
02095           this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
02096 }
02097 
02098 
02099 //-----------------------------------------------------------------------------
02100 void KMComposeWin::addAttach(const KMMessagePart* msgPart)
02101 {
02102   mAtmList.append(msgPart);
02103 
02104   // show the attachment listbox if it does not up to now
02105   if (mAtmList.count()==1)
02106   {
02107     mGrid->setRowStretch(mNumHeaders+1, 50);
02108     mAtmListView->setMinimumSize(100, 80);
02109     mAtmListView->setMaximumHeight( 100 );
02110     mAtmListView->show();
02111     resize(size());
02112   }
02113 
02114   // add a line in the attachment listbox
02115   KMAtmListViewItem *lvi = new KMAtmListViewItem( mAtmListView );
02116   msgPartToItem(msgPart, lvi);
02117   mAtmItemList.append(lvi);
02118 
02119   slotUpdateAttachActions();
02120 }
02121 
02122 
02123 //-----------------------------------------------------------------------------
02124 void KMComposeWin::slotUpdateAttachActions()
02125 {
02126   int selectedCount = 0;
02127   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it ) {
02128     if ( (*it)->isSelected() ) {
02129       ++selectedCount;
02130     }
02131   }
02132 
02133   mAttachRemoveAction->setEnabled( selectedCount >= 1 );
02134   mAttachSaveAction->setEnabled( selectedCount == 1 );
02135   mAttachPropertiesAction->setEnabled( selectedCount == 1 );
02136 }
02137 
02138 
02139 //-----------------------------------------------------------------------------
02140 
02141 QString KMComposeWin::prettyMimeType( const QString& type )
02142 {
02143   QString t = type.lower();
02144   KServiceType::Ptr st = KServiceType::serviceType( t );
02145   return st ? st->comment() : t;
02146 }
02147 
02148 void KMComposeWin::msgPartToItem(const KMMessagePart* msgPart,
02149                                  KMAtmListViewItem *lvi)
02150 {
02151   assert(msgPart != 0);
02152 
02153   if (!msgPart->fileName().isEmpty())
02154     lvi->setText(0, msgPart->fileName());
02155   else
02156     lvi->setText(0, msgPart->name());
02157   lvi->setText(1, KIO::convertSize( msgPart->decodedSize()));
02158   lvi->setText(2, msgPart->contentTransferEncodingStr());
02159   lvi->setText(3, prettyMimeType(msgPart->typeStr() + "/" + msgPart->subtypeStr()));
02160   if( canSignEncryptAttachments() ) {
02161     lvi->enableCryptoCBs( true );
02162     lvi->setEncrypt( mEncryptAction->isChecked() );
02163     lvi->setSign(    mSignAction->isChecked() );
02164   } else {
02165     lvi->enableCryptoCBs( false );
02166   }
02167 }
02168 
02169 
02170 //-----------------------------------------------------------------------------
02171 void KMComposeWin::removeAttach(const QString &aUrl)
02172 {
02173   int idx;
02174   KMMessagePart* msgPart;
02175   for(idx=0,msgPart=mAtmList.first(); msgPart;
02176       msgPart=mAtmList.next(),idx++) {
02177     if (msgPart->name() == aUrl) {
02178       removeAttach(idx);
02179       return;
02180     }
02181   }
02182 }
02183 
02184 
02185 //-----------------------------------------------------------------------------
02186 void KMComposeWin::removeAttach(int idx)
02187 {
02188   mAtmModified = TRUE;
02189   mAtmList.remove(idx);
02190   delete mAtmItemList.take(idx);
02191 
02192   if( mAtmList.isEmpty() )
02193   {
02194     mAtmListView->hide();
02195     mGrid->setRowStretch(mNumHeaders+1, 0);
02196     mAtmListView->setMinimumSize(0, 0);
02197     resize(size());
02198   }
02199 }
02200 
02201 
02202 //-----------------------------------------------------------------------------
02203 bool KMComposeWin::encryptFlagOfAttachment(int idx)
02204 {
02205   return (int)(mAtmItemList.count()) > idx
02206     ? ((KMAtmListViewItem*)(mAtmItemList.at( idx )))->isEncrypt()
02207     : false;
02208 }
02209 
02210 
02211 //-----------------------------------------------------------------------------
02212 bool KMComposeWin::signFlagOfAttachment(int idx)
02213 {
02214   return (int)(mAtmItemList.count()) > idx
02215     ? ((KMAtmListViewItem*)(mAtmItemList.at( idx )))->isSign()
02216     : false;
02217 }
02218 
02219 
02220 //-----------------------------------------------------------------------------
02221 void KMComposeWin::addrBookSelInto()
02222 {
02223   AddressesDialog dlg( this );
02224   QString txt;
02225   QStringList lst;
02226 
02227   txt = to();
02228   if ( !txt.isEmpty() ) {
02229       lst = KPIM::splitEmailAddrList( txt );
02230       dlg.setSelectedTo( lst );
02231   }
02232 
02233   txt = cc();
02234   if ( !txt.isEmpty() ) {
02235       lst = KPIM::splitEmailAddrList( txt );
02236       dlg.setSelectedCC( lst );
02237   }
02238 
02239   txt = bcc();
02240   if ( !txt.isEmpty() ) {
02241       lst = KPIM::splitEmailAddrList( txt );
02242       dlg.setSelectedBCC( lst );
02243   }
02244 
02245   dlg.setRecentAddresses( RecentAddresses::self( KMKernel::config() )->kabcAddresses() );
02246 
02247   if (dlg.exec()==QDialog::Rejected) return;
02248 
02249   mEdtTo->setText( dlg.to().join(", ") );
02250   mEdtTo->setEdited( true );
02251 
02252   mEdtCc->setText( dlg.cc().join(", ") );
02253   mEdtCc->setEdited( true );
02254 
02255   mEdtBcc->setText( dlg.bcc().join(", ") );
02256   mEdtBcc->setEdited( true );
02257 }
02258 
02259 
02260 //-----------------------------------------------------------------------------
02261 void KMComposeWin::setCharset(const QCString& aCharset, bool forceDefault)
02262 {
02263   if ((forceDefault && mForceReplyCharset) || aCharset.isEmpty())
02264     mCharset = mDefCharset;
02265   else
02266     mCharset = aCharset.lower();
02267 
02268   if ( mCharset.isEmpty() || mCharset == "default" )
02269      mCharset = mDefCharset;
02270 
02271   if (mAutoCharset)
02272   {
02273     mEncodingAction->setCurrentItem( 0 );
02274     return;
02275   }
02276 
02277   QStringList encodings = mEncodingAction->items();
02278   int i = 0;
02279   bool charsetFound = FALSE;
02280   for ( QStringList::Iterator it = encodings.begin(); it != encodings.end();
02281      ++it, i++ )
02282   {
02283     if (i > 0 && ((mCharset == "us-ascii" && i == 1) ||
02284      (i != 1 && KGlobal::charsets()->codecForName(
02285       KGlobal::charsets()->encodingForName(*it))
02286       == KGlobal::charsets()->codecForName(mCharset))))
02287     {
02288       mEncodingAction->setCurrentItem( i );
02289       slotSetCharset();
02290       charsetFound = TRUE;
02291       break;
02292     }
02293   }
02294   if (!aCharset.isEmpty() && !charsetFound) setCharset("", TRUE);
02295 }
02296 
02297 
02298 //-----------------------------------------------------------------------------
02299 void KMComposeWin::slotAddrBook()
02300 {
02301   KAddrBookExternal::openAddressBook(this);
02302 }
02303 
02304 
02305 //-----------------------------------------------------------------------------
02306 void KMComposeWin::slotAddrBookFrom()
02307 {
02308   addrBookSelInto();
02309 }
02310 
02311 
02312 //-----------------------------------------------------------------------------
02313 void KMComposeWin::slotAddrBookReplyTo()
02314 {
02315   addrBookSelInto();
02316 }
02317 
02318 
02319 //-----------------------------------------------------------------------------
02320 void KMComposeWin::slotAddrBookTo()
02321 {
02322   addrBookSelInto();
02323 }
02324 
02325 //-----------------------------------------------------------------------------
02326 void KMComposeWin::slotAttachFile()
02327 {
02328   // Create File Dialog and return selected file(s)
02329   // We will not care about any permissions, existence or whatsoever in
02330   // this function.
02331 
02332   KFileDialog fdlg(QString::null, QString::null, this, 0, TRUE);
02333   fdlg.setOperationMode( KFileDialog::Other );
02334   fdlg.setCaption(i18n("Attach File"));
02335   fdlg.okButton()->setGuiItem(KGuiItem(i18n("&Attach"),"fileopen"));
02336   fdlg.setMode(KFile::Files);
02337   fdlg.exec();
02338   KURL::List files = fdlg.selectedURLs();
02339 
02340   for (KURL::List::Iterator it = files.begin(); it != files.end(); ++it)
02341     addAttach(*it);
02342 }
02343 
02344 
02345 //-----------------------------------------------------------------------------
02346 void KMComposeWin::slotAttachFileData(KIO::Job *job, const QByteArray &data)
02347 {
02348   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
02349   assert(it != mMapAtmLoadData.end());
02350   QBuffer buff((*it).data);
02351   buff.open(IO_WriteOnly | IO_Append);
02352   buff.writeBlock(data.data(), data.size());
02353   buff.close();
02354 }
02355 
02356 
02357 //-----------------------------------------------------------------------------
02358 void KMComposeWin::slotAttachFileResult(KIO::Job *job)
02359 {
02360   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
02361   assert(it != mMapAtmLoadData.end());
02362   if (job->error())
02363   {
02364     mMapAtmLoadData.remove(it);
02365     job->showErrorDialog();
02366     return;
02367   }
02368   if ((*it).insert)
02369   {
02370     (*it).data.resize((*it).data.size() + 1);
02371     (*it).data[(*it).data.size() - 1] = '\0';
02372     if ( const QTextCodec * codec = KGlobal::charsets()->codecForName((*it).encoding) )
02373       mEditor->insert( codec->toUnicode( (*it).data ) );
02374     else
02375       mEditor->insert( QString::fromLocal8Bit( (*it).data ) );
02376     mMapAtmLoadData.remove(it);
02377     return;
02378   }
02379   const QCString partCharset = (*it).url.fileEncoding().isEmpty()
02380                              ? mCharset
02381                              : QCString((*it).url.fileEncoding().latin1());
02382 
02383   KMMessagePart* msgPart;
02384 
02385   KCursorSaver busy(KBusyPtr::busy());
02386   QString name( (*it).url.fileName() );
02387   // ask the job for the mime type of the file
02388   QString mimeType = static_cast<KIO::MimetypeJob*>(job)->mimetype();
02389 
02390   if ( name.isEmpty() ) {
02391     // URL ends with '/' (e.g. http://www.kde.org/)
02392     // guess a reasonable filename
02393     if( mimeType == "text/html" )
02394       name = "index.html";
02395     else {
02396       // try to determine a reasonable extension
02397       QStringList patterns( KMimeType::mimeType( mimeType )->patterns() );
02398       QString ext;
02399       if( !patterns.isEmpty() ) {
02400         ext = patterns[0];
02401         int i = ext.findRev( '.' );
02402         if( i == -1 )
02403           ext.prepend( '.' );
02404         else if( i > 0 )
02405           ext = ext.mid( i );
02406       }
02407       name = QString("unknown") += ext;
02408     }
02409   }
02410 
02411   name.truncate( 256 ); // is this needed?
02412 
02413   QCString encoding = KMMsgBase::autoDetectCharset(partCharset,
02414     KMMessage::preferredCharsets(), name);
02415   if (encoding.isEmpty()) encoding = "utf-8";
02416 
02417   QCString encName;
02418   if ( mOutlookCompatible )
02419     encName = KMMsgBase::encodeRFC2047String( name, encoding );
02420   else
02421     encName = KMMsgBase::encodeRFC2231String( name, encoding );
02422   bool RFC2231encoded = false;
02423   if ( !mOutlookCompatible )
02424     RFC2231encoded = name != QString( encName );
02425 
02426   // create message part
02427   msgPart = new KMMessagePart;
02428   msgPart->setName(name);
02429   QValueList<int> allowedCTEs;
02430   if ( mimeType == "message/rfc822" ) {
02431     msgPart->setMessageBody( (*it).data );
02432     allowedCTEs << DwMime::kCte7bit;
02433     allowedCTEs << DwMime::kCte8bit;
02434   } else {
02435     msgPart->setBodyAndGuessCte((*it).data, allowedCTEs,
02436                               !kmkernel->msgSender()->sendQuotedPrintable());
02437     kdDebug(5006) << "autodetected cte: " << msgPart->cteStr() << endl;
02438   }
02439   int slash = mimeType.find( '/' );
02440   if( slash == -1 )
02441     slash = mimeType.length();
02442   msgPart->setTypeStr( mimeType.left( slash ).latin1() );
02443   msgPart->setSubtypeStr( mimeType.mid( slash + 1 ).latin1() );
02444   msgPart->setContentDisposition(QCString("attachment;\n\tfilename")
02445     + ( RFC2231encoded ? "*=" + encName : "=\"" + encName + '"' ));
02446 
02447   mMapAtmLoadData.remove(it);
02448 
02449   msgPart->setCharset(partCharset);
02450 
02451   // show message part dialog, if not configured away (default):
02452   KConfigGroup composer(KMKernel::config(), "Composer");
02453   if (!composer.hasKey("showMessagePartDialogOnAttach"))
02454     // make it visible in the config file:
02455     composer.writeEntry("showMessagePartDialogOnAttach", false);
02456   if (composer.readBoolEntry("showMessagePartDialogOnAttach", false)) {
02457     KMMsgPartDialogCompat dlg;
02458     int encodings = 0;
02459     for ( QValueListConstIterator<int> it = allowedCTEs.begin() ;
02460           it != allowedCTEs.end() ; ++it )
02461       switch ( *it ) {
02462       case DwMime::kCteBase64: encodings |= KMMsgPartDialog::Base64; break;
02463       case DwMime::kCteQp: encodings |= KMMsgPartDialog::QuotedPrintable; break;
02464       case DwMime::kCte7bit: encodings |= KMMsgPartDialog::SevenBit; break;
02465       case DwMime::kCte8bit: encodings |= KMMsgPartDialog::EightBit; break;
02466       default: ;
02467       }
02468     dlg.setShownEncodings( encodings );
02469     dlg.setMsgPart(msgPart);
02470     if (!dlg.exec()) {
02471       delete msgPart;
02472       msgPart = 0;
02473       return;
02474     }
02475   }
02476   mAtmModified = TRUE;
02477   if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
02478 
02479   // add the new attachment to the list
02480   addAttach(msgPart);
02481 }
02482 
02483 
02484 //-----------------------------------------------------------------------------
02485 void KMComposeWin::slotInsertFile()
02486 {
02487   KFileDialog fdlg(QString::null, QString::null, this, 0, TRUE);
02488   fdlg.setOperationMode( KFileDialog::Opening );
02489   fdlg.okButton()->setText(i18n("&Insert"));
02490   fdlg.setCaption(i18n("Insert File"));
02491   fdlg.toolBar()->insertCombo(KMMsgBase::supportedEncodings(FALSE), 4711,
02492     false, 0, 0, 0);
02493   KComboBox *combo = fdlg.toolBar()->getCombo(4711);
02494   for (int i = 0; i < combo->count(); i++)
02495     if (KGlobal::charsets()->codecForName(KGlobal::charsets()->
02496       encodingForName(combo->text(i)))
02497       == QTextCodec::codecForLocale()) combo->setCurrentItem(i);
02498   if (!fdlg.exec()) return;
02499 
02500   KURL u = fdlg.selectedURL();
02501 
02502   if (u.fileName().isEmpty()) return;
02503 
02504   KIO::Job *job = KIO::get(u);
02505   atmLoadData ld;
02506   ld.url = u;
02507   ld.data = QByteArray();
02508   ld.insert = true;
02509   ld.encoding = KGlobal::charsets()->encodingForName(
02510     combo->currentText()).latin1();
02511   mMapAtmLoadData.insert(job, ld);
02512   connect(job, SIGNAL(result(KIO::Job *)),
02513           this, SLOT(slotAttachFileResult(KIO::Job *)));
02514   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
02515           this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
02516 }
02517 
02518 
02519 //-----------------------------------------------------------------------------
02520 void KMComposeWin::slotSetCharset()
02521 {
02522   if (mEncodingAction->currentItem() == 0)
02523   {
02524     mAutoCharset = true;
02525     return;
02526   }
02527   mAutoCharset = false;
02528 
02529   mCharset = KGlobal::charsets()->encodingForName( mEncodingAction->
02530     currentText() ).latin1();
02531 }
02532 
02533 
02534 //-----------------------------------------------------------------------------
02535 void KMComposeWin::slotSelectCryptoModule()
02536 {
02537   if( canSignEncryptAttachments() ) {
02538     // if the encrypt/sign columns are hidden then show them
02539     if( 0 == mAtmListView->columnWidth( mAtmColEncrypt ) ) {
02540       // set/unset signing/encryption for all attachments according to the
02541       // state of the global sign/encrypt action
02542       if( !mAtmList.isEmpty() ) {
02543         for( KMAtmListViewItem* lvi = (KMAtmListViewItem*)mAtmItemList.first();
02544              lvi;
02545              lvi = (KMAtmListViewItem*)mAtmItemList.next() ) {
02546           lvi->setSign( mSignAction->isChecked() );
02547           lvi->setEncrypt( mEncryptAction->isChecked() );
02548         }
02549       }
02550       int totalWidth = 0;
02551       // determine the total width of the columns
02552       for( int col=0; col < mAtmColEncrypt; col++ )
02553         totalWidth += mAtmListView->columnWidth( col );
02554       int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
02555                                          - mAtmSignColWidth;
02556       // reduce the width of all columns so that the encrypt and sign column
02557       // fit
02558       int usedWidth = 0;
02559       for( int col=0; col < mAtmColEncrypt-1; col++ ) {
02560         int newWidth = mAtmListView->columnWidth( col ) * reducedTotalWidth
02561                                                        / totalWidth;
02562         mAtmListView->setColumnWidth( col, newWidth );
02563         usedWidth += newWidth;
02564       }
02565       // the last column before the encrypt column gets the remaining space
02566       // (because of rounding errors the width of this column isn't calculated
02567       // the same way as the width of the other columns)
02568       mAtmListView->setColumnWidth( mAtmColEncrypt-1,
02569                                     reducedTotalWidth - usedWidth );
02570       mAtmListView->setColumnWidth( mAtmColEncrypt, mAtmEncryptColWidth );
02571       mAtmListView->setColumnWidth( mAtmColSign,    mAtmSignColWidth );
02572       for( KMAtmListViewItem* lvi = (KMAtmListViewItem*)mAtmItemList.first();
02573            lvi;
02574            lvi = (KMAtmListViewItem*)mAtmItemList.next() ) {
02575         lvi->enableCryptoCBs( true );
02576       }
02577     }
02578   } else {
02579     // if the encrypt/sign columns are visible then hide them
02580     if( 0 != mAtmListView->columnWidth( mAtmColEncrypt ) ) {
02581       mAtmEncryptColWidth = mAtmListView->columnWidth( mAtmColEncrypt );
02582       mAtmSignColWidth = mAtmListView->columnWidth( mAtmColSign );
02583       int totalWidth = 0;
02584       // determine the total width of the columns
02585       for( int col=0; col < mAtmListView->columns(); col++ )
02586         totalWidth += mAtmListView->columnWidth( col );
02587       int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
02588                                          - mAtmSignColWidth;
02589       // increase the width of all columns so that the visible columns take
02590       // up the whole space
02591       int usedWidth = 0;
02592       for( int col=0; col < mAtmColEncrypt-1; col++ ) {
02593         int newWidth = mAtmListView->columnWidth( col ) * totalWidth
02594                                                        / reducedTotalWidth;
02595         mAtmListView->setColumnWidth( col, newWidth );
02596         usedWidth += newWidth;
02597       }
02598       // the last column before the encrypt column gets the remaining space
02599       // (because of rounding errors the width of this column isn't calculated
02600       // the same way as the width of the other columns)
02601       mAtmListView->setColumnWidth( mAtmColEncrypt-1, totalWidth - usedWidth );
02602       mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
02603       mAtmListView->setColumnWidth( mAtmColSign,    0 );
02604       for( KMAtmListViewItem* lvi = (KMAtmListViewItem*)mAtmItemList.first();
02605            lvi;
02606            lvi = (KMAtmListViewItem*)mAtmItemList.next() ) {
02607         lvi->enableCryptoCBs( false );
02608       }
02609     }
02610   }
02611 }
02612 
02613 static void showExportError( QWidget * w, const GpgME::Error & err ) {
02614   assert( err );
02615   const QString msg = i18n("<qt><p>An error occurred while trying to export "
02616                "the key from the backend:</p>"
02617                "<p><b>%1</b></p></qt>")
02618     .arg( QString::fromLocal8Bit( err.asString() ) );
02619   KMessageBox::error( w, msg, i18n("Key Export Failed") );
02620 }
02621 
02622 
02623 //-----------------------------------------------------------------------------
02624 void KMComposeWin::slotInsertMyPublicKey()
02625 {
02626   // get PGP user id for the chosen identity
02627   mFingerprint =
02628     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() ).pgpEncryptionKey();
02629   if ( !mFingerprint.isEmpty() )
02630     startPublicKeyExport();
02631 }
02632 
02633 void KMComposeWin::startPublicKeyExport() {
02634   if ( mFingerprint.isEmpty() )
02635     return;
02636   Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->openpgp()->publicKeyExportJob( true );
02637   assert( job );
02638 
02639   connect( job, SIGNAL(result(const GpgME::Error&,const QByteArray&)),
02640        this, SLOT(slotPublicKeyExportResult(const GpgME::Error&,const QByteArray&)) );
02641 
02642   const GpgME::Error err = job->start( mFingerprint );
02643   if ( err )
02644     showExportError( this, err );
02645   else
02646     (void)new Kleo::ProgressDialog( job, i18n("Exporting key..."), this );
02647 }
02648 
02649 void KMComposeWin::slotPublicKeyExportResult( const GpgME::Error & err, const QByteArray & keydata ) {
02650   if ( err ) {
02651     showExportError( this, err );
02652     return;
02653   }
02654 
02655   // create message part
02656   KMMessagePart * msgPart = new KMMessagePart();
02657   msgPart->setName( i18n("OpenPGP key 0x%1").arg( mFingerprint ) );
02658   msgPart->setTypeStr("application");
02659   msgPart->setSubtypeStr("pgp-keys");
02660   QValueList<int> dummy;
02661   msgPart->setBodyAndGuessCte(keydata, dummy, false);
02662   msgPart->setContentDisposition( "attachment;\n\tfilename=0x" + QCString( mFingerprint.latin1() ) + ".asc" );
02663 
02664   // add the new attachment to the list
02665   addAttach(msgPart);
02666   rethinkFields(); //work around initial-size bug in Qt-1.32
02667 }
02668 
02669 //-----------------------------------------------------------------------------
02670 void KMComposeWin::slotInsertPublicKey()
02671 {
02672   Kleo::KeySelectionDialog dlg( i18n("Attach Public OpenPGP Key"),
02673                                 i18n("Select the public key which should "
02674                                      "be attached."),
02675                 std::vector<GpgME::Key>(),
02676                 Kleo::KeySelectionDialog::PublicKeys|Kleo::KeySelectionDialog::OpenPGPKeys,
02677                 false /* no multi selection */,
02678                 false /* no remember choice box */,
02679                 this, "attach public key selection dialog" );
02680   if ( dlg.exec() != QDialog::Accepted )
02681     return;
02682 
02683   mFingerprint = dlg.fingerprint();
02684   startPublicKeyExport();
02685 }
02686 
02687 
02688 //-----------------------------------------------------------------------------
02689 void KMComposeWin::slotAttachPopupMenu(QListViewItem *, const QPoint &, int)
02690 {
02691   if (!mAttachMenu)
02692   {
02693      mAttachMenu = new QPopupMenu(this);
02694 
02695      mOpenId = mAttachMenu->insertItem(SmallIcon("fileopen"), i18n("to open", "Open"), this,
02696                              SLOT(slotAttachOpen()));
02697      mOpenWithId = mAttachMenu->insertItem(i18n("to open with", "Open with..."), this,
02698                              SLOT(slotAttachOpenWith()));
02699      mViewId = mAttachMenu->insertItem(i18n("to view", "View"), this,
02700                              SLOT(slotAttachView()));
02701      mRemoveId = mAttachMenu->insertItem(i18n("Remove"), this, SLOT(slotAttachRemove()));
02702      mSaveAsId = mAttachMenu->insertItem( SmallIcon("filesaveas"), i18n("Save As..."), this,
02703                                           SLOT( slotAttachSave() ) );
02704      mPropertiesId = mAttachMenu->insertItem( i18n("Properties"), this,
02705                                               SLOT( slotAttachProperties() ) );
02706      mAttachMenu->insertSeparator();
02707      mAttachMenu->insertItem(i18n("Add Attachment..."), this, SLOT(slotAttachFile()));
02708   }
02709 
02710   int selectedCount = 0;
02711   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it ) {
02712     if ( (*it)->isSelected() ) {
02713       ++selectedCount;
02714     }
02715   }
02716 
02717   mAttachMenu->setItemEnabled( mOpenId, selectedCount > 0 );
02718   mAttachMenu->setItemEnabled( mOpenWithId, selectedCount > 0 );
02719   mAttachMenu->setItemEnabled( mViewId, selectedCount > 0 );
02720   mAttachMenu->setItemEnabled( mRemoveId, selectedCount > 0 );
02721   mAttachMenu->setItemEnabled( mSaveAsId, selectedCount == 1 );
02722   mAttachMenu->setItemEnabled( mPropertiesId, selectedCount == 1 );
02723 
02724   mAttachMenu->popup(QCursor::pos());
02725 }
02726 
02727 //-----------------------------------------------------------------------------
02728 int KMComposeWin::currentAttachmentNum()
02729 {
02730   int i = 0;
02731   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i )
02732     if ( *it == mAtmListView->currentItem() )
02733       return i;
02734   return -1;
02735 }
02736 
02737 //-----------------------------------------------------------------------------
02738 void KMComposeWin::slotAttachProperties()
02739 {
02740   int idx = currentAttachmentNum();
02741 
02742   if (idx < 0) return;
02743 
02744   KMMessagePart* msgPart = mAtmList.at(idx);
02745   msgPart->setCharset(mCharset);
02746 
02747   KMMsgPartDialogCompat dlg;
02748   dlg.setMsgPart(msgPart);
02749   KMAtmListViewItem* listItem = (KMAtmListViewItem*)(mAtmItemList.at(idx));
02750   if( canSignEncryptAttachments() && listItem ) {
02751     dlg.setCanSign(    true );
02752     dlg.setCanEncrypt( true );
02753     dlg.setSigned(    listItem->isSign()    );
02754     dlg.setEncrypted( listItem->isEncrypt() );
02755   } else {
02756     dlg.setCanSign(    false );
02757     dlg.setCanEncrypt( false );
02758   }
02759   if (dlg.exec())
02760   {
02761     mAtmModified = TRUE;
02762     // values may have changed, so recreate the listbox line
02763     if( listItem ) {
02764       msgPartToItem(msgPart, listItem);
02765       if( canSignEncryptAttachments() ) {
02766         listItem->setSign(    dlg.isSigned()    );
02767         listItem->setEncrypt( dlg.isEncrypted() );
02768       }
02769     }
02770   }
02771   if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
02772 }
02773 
02774 
02775 //-----------------------------------------------------------------------------
02776 void KMComposeWin::slotAttachView()
02777 {
02778   int i = 0;
02779   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
02780     if ( (*it)->isSelected() ) {
02781       viewAttach( i );
02782     }
02783   }
02784 }
02785 
02786 //-----------------------------------------------------------------------------
02787 void KMComposeWin::slotAttachOpen()
02788 {
02789   int i = 0;
02790   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
02791     if ( (*it)->isSelected() ) {
02792       openAttach( i, false );
02793     }
02794   }
02795 }
02796 
02797 //-----------------------------------------------------------------------------
02798 void KMComposeWin::slotAttachOpenWith()
02799 {
02800   int i = 0;
02801   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
02802     if ( (*it)->isSelected() ) {
02803       openAttach( i, true );
02804     }
02805   }
02806 }
02807 
02808 //-----------------------------------------------------------------------------
02809 bool KMComposeWin::inlineSigningEncryptionSelected() {
02810   if ( !mSignAction->isChecked() && !mEncryptAction->isChecked() )
02811     return false;
02812   return cryptoMessageFormat() == Kleo::InlineOpenPGPFormat;
02813 }
02814 
02815 //-----------------------------------------------------------------------------
02816 void KMComposeWin::viewAttach( int index )
02817 {
02818   QString str, pname;
02819   KMMessagePart* msgPart;
02820   msgPart = mAtmList.at(index);
02821   pname = msgPart->name().stripWhiteSpace();
02822   if (pname.isEmpty()) pname=msgPart->contentDescription();
02823   if (pname.isEmpty()) pname="unnamed";
02824 
02825   KTempFile* atmTempFile = new KTempFile();
02826   mAtmTempList.append( atmTempFile );
02827   atmTempFile->setAutoDelete( true );
02828   KPIM::kByteArrayToFile(msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
02829     false);
02830   KMReaderMainWin *win = new KMReaderMainWin(msgPart, false,
02831     atmTempFile->name(), pname, mCharset );
02832   win->show();
02833 }
02834 
02835 //-----------------------------------------------------------------------------
02836 void KMComposeWin::openAttach( int index, bool with )
02837 {
02838   KMMessagePart* msgPart = mAtmList.at(index);
02839   const QString contentTypeStr =
02840     ( msgPart->typeStr() + '/' + msgPart->subtypeStr() ).lower();
02841 
02842   KMimeType::Ptr mimetype;
02843   mimetype = KMimeType::mimeType( contentTypeStr );
02844 
02845   KTempFile* atmTempFile = new KTempFile();
02846   mAtmTempList.append( atmTempFile );
02847   const bool autoDelete = true;
02848   atmTempFile->setAutoDelete( autoDelete );
02849 
02850   KURL url;
02851   url.setPath( atmTempFile->name() );
02852 
02853   KPIM::kByteArrayToFile( msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
02854     false );
02855   if ( ::chmod( QFile::encodeName( atmTempFile->name() ), S_IRUSR ) != 0) {
02856     QFile::remove(url.path());
02857     return;
02858   }
02859 
02860   KService::Ptr offer =
02861     KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
02862 
02863   if ( with || !offer || mimetype->name() == "application/octet-stream" ) {
02864     if ( ( !KRun::displayOpenWithDialog( url, autoDelete ) ) && autoDelete ) {
02865       QFile::remove(url.path());
02866     }
02867   }
02868   else {
02869     if ( ( !KRun::run( *offer, url, autoDelete ) ) && autoDelete ) {
02870         QFile::remove( url.path() );
02871     }
02872   }
02873 
02874 }
02875 
02876 //-----------------------------------------------------------------------------
02877 void KMComposeWin::slotAttachSave()
02878 {
02879   KMMessagePart* msgPart;
02880   QString fileName, pname;
02881   int idx = currentAttachmentNum();
02882 
02883   if (idx < 0) return;
02884 
02885   msgPart = mAtmList.at(idx);
02886   pname = msgPart->name();
02887   if (pname.isEmpty()) pname="unnamed";
02888 
02889   KURL url = KFileDialog::getSaveURL(QString::null, QString::null, 0, i18n("Save Attachment As"));
02890 
02891   if( url.isEmpty() )
02892     return;
02893 
02894   kmkernel->byteArrayToRemoteFile(msgPart->bodyDecodedBinary(), url);
02895 }
02896 
02897 
02898 //-----------------------------------------------------------------------------
02899 void KMComposeWin::slotAttachRemove()
02900 {
02901   bool attachmentRemoved = false;
02902   int i = 0;
02903   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ) {
02904     if ( (*it)->isSelected() ) {
02905       removeAttach( i );
02906       attachmentRemoved = true;
02907     }
02908     else {
02909       ++it;
02910       ++i;
02911     }
02912   }
02913 
02914   if ( attachmentRemoved ) {
02915     mEditor->setModified( true );
02916     slotUpdateAttachActions();
02917   }
02918 }
02919 
02920 //-----------------------------------------------------------------------------
02921 void KMComposeWin::slotFind()
02922 {
02923   mEditor->search();
02924 }
02925 
02926 void KMComposeWin::slotSearchAgain()
02927 {
02928   mEditor->repeatSearch();
02929 }
02930 
02931 //-----------------------------------------------------------------------------
02932 void KMComposeWin::slotReplace()
02933 {
02934   mEditor->replace();
02935 }
02936 
02937 //-----------------------------------------------------------------------------
02938 void KMComposeWin::slotUpdateFont()
02939 {
02940   if ( mFixedFontAction ) {
02941     mUseFixedFont = mFixedFontAction->isChecked();
02942   }
02943   mEditor->setFont( mUseFixedFont ? mFixedFont : mBodyFont );
02944 }
02945 
02946 QString KMComposeWin::quotePrefixName() const
02947 {
02948     if ( !msg() )
02949         return QString::null;
02950 
02951     KConfig *config=KMKernel::config();
02952     KConfigGroupSaver saver(config, "General");
02953 
02954     int languageNr = config->readNumEntry("reply-current-language",0);
02955     config->setGroup( QString("KMMessage #%1").arg(languageNr) );
02956 
02957     QString quotePrefix = config->readEntry("indent-prefix", ">%_");
02958     quotePrefix = msg()->formatString(quotePrefix);
02959     return quotePrefix;
02960 }
02961 
02962 void KMComposeWin::slotPasteAsQuotation()
02963 {
02964     if( mEditor->hasFocus() && msg() )
02965     {
02966         QString quotePrefix = quotePrefixName();
02967         QString s = QApplication::clipboard()->text();
02968         if (!s.isEmpty()) {
02969             for (int i=0; (uint)i<s.length(); i++) {
02970                 if ( s[i] < ' ' && s[i] != '\n' && s[i] != '\t' )
02971                     s[i] = ' ';
02972             }
02973             s.prepend(quotePrefix);
02974             s.replace("\n","\n"+quotePrefix);
02975             mEditor->insert(s);
02976         }
02977     }
02978 }
02979 
02980 
02981 void KMComposeWin::slotAddQuotes()
02982 {
02983     if( mEditor->hasFocus() && msg() )
02984     {
02985         if ( mEditor->hasMarkedText()) {
02986             QString s =  mEditor->markedText();
02987             QString quotePrefix = quotePrefixName();
02988             s.prepend(quotePrefix);
02989             s.replace("\n", "\n"+quotePrefix);
02990             mEditor->insert(s);
02991         } else {
02992             int l =  mEditor->currentLine();
02993             int c =  mEditor->currentColumn();
02994             QString s =  mEditor->textLine(l);
02995             s.prepend("> ");
02996             mEditor->insertLine(s,l);
02997             mEditor->removeLine(l+1);
02998             mEditor->setCursorPosition(l,c+2);
02999         }
03000     }
03001 }
03002 
03003 
03004 void KMComposeWin::slotRemoveQuotes()
03005 {
03006     if( mEditor->hasFocus() && msg() )
03007     {
03008         QString quotePrefix = quotePrefixName();
03009         if (mEditor->hasMarkedText()) {
03010             QString s = mEditor->markedText();
03011             QString quotePrefix = quotePrefixName();
03012             if (s.left(2) == quotePrefix )
03013                 s.remove(0,2);
03014             s.replace("\n"+quotePrefix,"\n");
03015             mEditor->insert(s);
03016         } else {
03017             int l = mEditor->currentLine();
03018             int c = mEditor->currentColumn();
03019             QString s = mEditor->textLine(l);
03020             if (s.left(2) == quotePrefix) {
03021                 s.remove(0,2);
03022                 mEditor->insertLine(s,l);
03023                 mEditor->removeLine(l+1);
03024                 mEditor->setCursorPosition(l,c-2);
03025             }
03026         }
03027     }
03028 }
03029 
03030 
03031 //-----------------------------------------------------------------------------
03032 void KMComposeWin::slotUndo()
03033 {
03034   QWidget* fw = focusWidget();
03035   if (!fw) return;
03036 
03037   if ( ::qt_cast<KEdit*>(fw) )
03038       static_cast<QMultiLineEdit*>(fw)->undo();
03039   else if (::qt_cast<QLineEdit*>(fw))
03040       static_cast<QLineEdit*>(fw)->undo();
03041 }
03042 
03043 void KMComposeWin::slotRedo()
03044 {
03045   QWidget* fw = focusWidget();
03046   if (!fw) return;
03047 
03048   if (::qt_cast<KEdit*>(fw))
03049       static_cast<KEdit*>(fw)->redo();
03050   else if (::qt_cast<QLineEdit*>(fw))
03051       static_cast<QLineEdit*>(fw)->redo();
03052 }
03053 
03054 //-----------------------------------------------------------------------------
03055 void KMComposeWin::slotCut()
03056 {
03057   QWidget* fw = focusWidget();
03058   if (!fw) return;
03059 
03060   if (::qt_cast<KEdit*>(fw))
03061       static_cast<KEdit*>(fw)->cut();
03062   else if (::qt_cast<QLineEdit*>(fw))
03063       static_cast<QLineEdit*>(fw)->cut();
03064 }
03065 
03066 
03067 //-----------------------------------------------------------------------------
03068 void KMComposeWin::slotCopy()
03069 {
03070   QWidget* fw = focusWidget();
03071   if (!fw) return;
03072 
03073 #ifdef KeyPress
03074 #undef KeyPress
03075 #endif
03076 
03077   QKeyEvent k(QEvent::KeyPress, Key_C , 0 , ControlButton);
03078   kapp->notify(fw, &k);
03079 }
03080 
03081 
03082 //-----------------------------------------------------------------------------
03083 void KMComposeWin::slotPaste()
03084 {
03085   QWidget* fw = focusWidget();
03086   if (!fw) return;
03087 
03088 #ifdef KeyPress
03089 #undef KeyPress
03090 #endif
03091 
03092   QKeyEvent k(QEvent::KeyPress, Key_V , 0 , ControlButton);
03093   kapp->notify(fw, &k);
03094 }
03095 
03096 
03097 //-----------------------------------------------------------------------------
03098 void KMComposeWin::slotMarkAll()
03099 {
03100   QWidget* fw = focusWidget();
03101   if (!fw) return;
03102 
03103   if (::qt_cast<QLineEdit*>(fw))
03104       static_cast<QLineEdit*>(fw)->selectAll();
03105   else if (::qt_cast<KEdit*>(fw))
03106       static_cast<KEdit*>(fw)->selectAll();
03107 }
03108 
03109 
03110 //-----------------------------------------------------------------------------
03111 void KMComposeWin::slotClose()
03112 {
03113   close(FALSE);
03114 }
03115 
03116 
03117 //-----------------------------------------------------------------------------
03118 void KMComposeWin::slotNewComposer()
03119 {
03120   KMComposeWin* win;
03121   KMMessage* msg = new KMMessage;
03122 
03123   msg->initHeader();
03124   win = new KMComposeWin(msg);
03125   win->show();
03126 }
03127 
03128 
03129 //-----------------------------------------------------------------------------
03130 void KMComposeWin::slotNewMailReader()
03131 {
03132   KMMainWin *kmmwin = new KMMainWin(0);
03133   kmmwin->show();
03134   //d->resize(d->size());
03135 }
03136 
03137 
03138 //-----------------------------------------------------------------------------
03139 void KMComposeWin::slotUpdWinTitle(const QString& text)
03140 {
03141   if (text.isEmpty())
03142        setCaption("("+i18n("unnamed")+")");
03143   else setCaption(text);
03144 }
03145 
03146 
03147 //-----------------------------------------------------------------------------
03148 void KMComposeWin::slotEncryptToggled(bool on)
03149 {
03150 #ifdef KLEO_CHIASMUS
03151   if ( on && mEncryptBodyWithChiasmus ) {
03152     int ret = KMessageBox::warningContinueCancel( this,
03153         i18n("You have chosen to encrypt the body of your message using "
03154              "chiasmus. Additionally encrypting such messages by other means "
03155              "is currently not supported. Do you want to continue, which "
03156              "will disable encryption of the body (but not of attachments ) "
03157              "using chiasmus, or cancel, which will disable additional "
03158              "(non-chiasmus) encryption?" ),
03159         i18n("Chiasmus Body Encryption Conflict") );
03160     if ( ret == KMessageBox::Cancel ) {
03161       mEncryptAction->setChecked( false );
03162       return;
03163     }
03164     slotEncryptBodyChiasmusToggled( false );
03165     mEncryptBodyChiasmusAction->setChecked( false );
03166   }
03167 #endif
03168   setEncryption( on, true /* set by the user */ );
03169 }
03170 
03171 
03172 //-----------------------------------------------------------------------------
03173 void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
03174 {
03175   if ( !mEncryptAction->isEnabled() )
03176     encrypt = false;
03177   // check if the user wants to encrypt messages to himself and if he defined
03178   // an encryption key for the current identity
03179   else if ( encrypt && encryptToSelf() && !mLastIdentityHasEncryptionKey ) {
03180     if ( setByUser )
03181       KMessageBox::sorry( this,
03182                           i18n("<qt><p>You have requested that messages be "
03183                    "encrypted to yourself, but the currently selected "
03184                    "identity does not define an (OpenPGP or S/MIME) "
03185                    "encryption key to use for this.</p>"
03186                                "<p>Please select the key(s) to use "
03187                                "in the identity configuration.</p>"
03188                                "</qt>"),
03189                           i18n("Undefined Encryption Key") );
03190     encrypt = false;
03191   }
03192 
03193   // make sure the mEncryptAction is in the right state
03194   mEncryptAction->setChecked( encrypt );
03195 
03196   // show the appropriate icon
03197   if ( encrypt )
03198     mEncryptAction->setIcon("encrypted");
03199   else
03200     mEncryptAction->setIcon("decrypted");
03201 
03202   // mark the attachments for (no) encryption
03203   if ( canSignEncryptAttachments() ) {
03204     for ( KMAtmListViewItem* entry =
03205             static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
03206           entry;
03207           entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
03208       entry->setEncrypt( encrypt );
03209   }
03210 }
03211 
03212 
03213 //-----------------------------------------------------------------------------
03214 void KMComposeWin::slotSignToggled(bool on)
03215 {
03216 #ifdef KLEO_CHIASMUS
03217   if ( on && mEncryptBodyWithChiasmus ) {
03218     int ret = KMessageBox::warningContinueCancel( this,
03219         i18n("You have chosen to encrypt the body of your message using "
03220              "chiasmus. Signing such messages is currently not supported. Do you "
03221              "want to continue, which will disable encryption of the body (but not "
03222              "of attachments ) using chiasmus, or cancel, which will disable signing?"),
03223           i18n("Chiasmus Body Encryption Conflict" ) );
03224     if ( ret == KMessageBox::Cancel ) {
03225       mSignAction->setChecked( false );
03226       return;
03227     }
03228     slotEncryptBodyChiasmusToggled( false );
03229     mEncryptBodyChiasmusAction->setChecked( false );
03230   }
03231 #endif
03232   setSigning( on, true /* set by the user */ );
03233 }
03234 
03235 
03236 //-----------------------------------------------------------------------------
03237 void KMComposeWin::setSigning( bool sign, bool setByUser )
03238 {
03239   if ( !mSignAction->isEnabled() )
03240     sign = false;
03241 
03242   // check if the user defined a signing key for the current identity
03243   if ( sign && !mLastIdentityHasSigningKey ) {
03244     if ( setByUser )
03245       KMessageBox::sorry( this,
03246                           i18n("<qt><p>In order to be able to sign "
03247                                "this message you first have to "
03248                                "define the (OpenPGP or S/MIME) signing key "
03249                    "to use.</p>"
03250                                "<p>Please select the key to use "
03251                                "in the identity configuration.</p>"
03252                                "</qt>"),
03253                           i18n("Undefined Signing Key") );
03254     sign = false;
03255   }
03256 
03257   // make sure the mSignAction is in the right state
03258   mSignAction->setChecked( sign );
03259 
03260   // mark the attachments for (no) signing
03261   if ( canSignEncryptAttachments() ) {
03262     for ( KMAtmListViewItem* entry =
03263             static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
03264           entry;
03265           entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
03266       entry->setSign( sign );
03267   }
03268 }
03269 
03270 
03271 //-----------------------------------------------------------------------------
03272 void KMComposeWin::slotWordWrapToggled(bool on)
03273 {
03274   if (on)
03275   {
03276     mEditor->setWordWrap( QMultiLineEdit::FixedColumnWidth );
03277     mEditor->setWrapColumnOrWidth(mLineBreak);
03278   }
03279   else
03280   {
03281     mEditor->setWordWrap( QMultiLineEdit::NoWrap );
03282   }
03283 }
03284 
03285 
03286 //-----------------------------------------------------------------------------
03287 void KMComposeWin::slotPrint()
03288 {
03289   mMessageWasModified = ( mEditor->isModified() || mEdtFrom->edited() ||
03290                           mEdtReplyTo->edited() || mEdtTo->edited() ||
03291                           mEdtCc->edited() || mEdtBcc->edited() ||
03292                           mEdtSubject->edited() || mAtmModified ||
03293                           ( mTransport->lineEdit() &&
03294                             mTransport->lineEdit()->edited() ) );
03295   connect( this, SIGNAL( applyChangesDone( bool ) ),
03296            this, SLOT( slotContinuePrint( bool ) ) );
03297   applyChanges( true );
03298 }
03299 
03300 void KMComposeWin::slotContinuePrint( bool rc )
03301 {
03302   disconnect( this, SIGNAL( applyChangesDone( bool ) ),
03303               this, SLOT( slotContinuePrint( bool ) ) );
03304 
03305   if( rc ) {
03306     if ( mComposedMessages.isEmpty() ) {
03307       kdDebug(5006) << "Composing the message failed." << endl;
03308       return;
03309     }
03310     KMCommand *command = new KMPrintCommand( this, mComposedMessages.first() );
03311     command->start();
03312     mEditor->setModified( mMessageWasModified );
03313   }
03314 }
03315 
03316 
03317 //----------------------------------------------------------------------------
03318 void KMComposeWin::doSend(int aSendNow, bool saveInDrafts)
03319 {
03320   if ( aSendNow && kmkernel->isOffline() ) {
03321     KMessageBox::information( this,
03322                               i18n("KMail is currently in offline mode,"
03323                                    "your messages will be kept in the outbox until you go online."),
03324                               i18n("Online/Offline"), "kmailIsOffline" );
03325     mSendNow = false;
03326   } else {
03327     mSendNow = aSendNow;
03328   }
03329 
03330   mSaveInDrafts = saveInDrafts;
03331 
03332   if (!saveInDrafts)
03333   {
03334     if ( from().isEmpty() ) {
03335       if ( !( mShowHeaders & HDR_FROM ) ) {
03336         mShowHeaders |= HDR_FROM;
03337         rethinkFields( false );
03338       }
03339       mEdtFrom->setFocus();
03340       KMessageBox::sorry( this,
03341                           i18n("You must enter your email address in the "
03342                                "From: field. You should also set your email "
03343                                "address for all identities, so that you do "
03344                                "not have to enter it for each message.") );
03345       return;
03346     }
03347     if ( to().isEmpty() )
03348     {
03349         if (  cc().isEmpty() && bcc().isEmpty()) {
03350           if ( mEdtTo ) mEdtTo->setFocus();
03351           KMessageBox::information( this,
03352                                 i18n("You must specify at least one receiver,"
03353                                      "either in the To: field or as CC or as BCC.") );
03354           return;
03355         }
03356         else {
03357                 if ( mEdtTo ) mEdtTo->setFocus();
03358                 int rc =
03359                             KMessageBox::questionYesNo( this,
03360                                                         i18n("To field is missing."
03361                                                               "Send message anyway?"),
03362                                                         i18n("No To: specified") );
03363                 if ( rc == KMessageBox::No ){
03364                    return;
03365                 }
03366         }
03367     }
03368 
03369     if (subject().isEmpty())
03370     {
03371         mEdtSubject->setFocus();
03372         int rc =
03373           KMessageBox::questionYesNo( this,
03374                                       i18n("You did not specify a subject. "
03375                                            "Send message anyway?"),
03376                                       i18n("No Subject Specified"),
03377                                       i18n("S&end as Is"),
03378                                       i18n("&Specify the Subject"),
03379                                       "no_subject_specified" );
03380         if( rc == KMessageBox::No )
03381         {
03382            return;
03383         }
03384     }
03385 
03386     if ( userForgotAttachment() )
03387       return;
03388   }
03389 
03390   KCursorSaver busy(KBusyPtr::busy());
03391   mMsg->setDateToday();
03392 
03393   // If a user sets up their outgoing messages preferences wrong and then
03394   // sends mail that gets 'stuck' in their outbox, they should be able to
03395   // rectify the problem by editing their outgoing preferences and
03396   // resending.
03397   // Hence this following conditional
03398   QString hf = mMsg->headerField("X-KMail-Transport");
03399   if ((mTransport->currentText() != mTransport->text(0)) ||
03400       (!hf.isEmpty() && (hf != mTransport->text(0))))
03401     mMsg->setHeaderField("X-KMail-Transport", mTransport->currentText());
03402 
03403   if( saveInDrafts )
03404     mMsg->setHeaderField("X-KMail-CryptoFormat", QString::number(cryptoMessageFormat()));
03405 
03406   mDisableBreaking = saveInDrafts;
03407 
03408   const bool neverEncrypt = ( saveInDrafts && mNeverEncryptWhenSavingInDrafts )
03409                            || mSigningAndEncryptionExplicitlyDisabled;
03410   connect( this, SIGNAL( applyChangesDone( bool ) ),
03411            SLOT( slotContinueDoSend( bool ) ) );
03412 
03413   if ( mEditor->textFormat() == Qt::RichText )
03414     mMsg->setHeaderField( "X-KMail-Markup", "true" );
03415   else
03416     mMsg->removeHeaderField( "X-KMail-Markup" );
03417   if ( mEditor->textFormat() == Qt::RichText && inlineSigningEncryptionSelected() ) {
03418     QString keepBtnText = mEncryptAction->isChecked() ?
03419       mSignAction->isChecked() ? i18n( "&Keep markup, do not sign/encrypt" )
03420                                : i18n( "&Keep markup, do not encrypt" )
03421       : i18n( "&Keep markup, do not sign" );
03422     QString yesBtnText = mEncryptAction->isChecked() ?
03423       mSignAction->isChecked() ? i18n("Sign/Encrypt (delete markup)")
03424       : i18n( "Encrypt (delete markup)" )
03425       : i18n( "Sign (delete markup)" );
03426     int ret = KMessageBox::warningYesNoCancel(this,
03427                                       i18n("<qt><p>Inline signing/encrypting of HTML messages is not possible;</p>"
03428                                            "<p>do you want to delete your markup?</p></qt>"),
03429                                            i18n("Sign/Encrypt Message?"),
03430                                            KGuiItem( yesBtnText ),
03431                                            KGuiItem( keepBtnText ) );
03432     if ( KMessageBox::Cancel == ret )
03433       return;
03434     if ( KMessageBox::No == ret ) {
03435       mEncryptAction->setChecked(false);
03436       mSignAction->setChecked(false);
03437     }
03438     else {
03439       toggleMarkup(false);
03440     }
03441   }
03442 
03443   kdDebug(5006) << "KMComposeWin::doSend() - calling applyChanges()"
03444                 << endl;
03445   applyChanges( neverEncrypt );
03446 }
03447 
03448 void KMComposeWin::slotContinueDoSend( bool sentOk )
03449 {
03450   kdDebug(5006) << "KMComposeWin::slotContinueDoSend( " << sentOk << " )"
03451                 << endl;
03452   disconnect( this, SIGNAL( applyChangesDone( bool ) ),
03453               this, SLOT( slotContinueDoSend( bool ) ) );
03454 
03455   if ( !sentOk ) {
03456     mDisableBreaking = false;
03457     return;
03458   }
03459   if (!mAutoDeleteMsg) mEditor->setModified(FALSE);
03460   mEdtFrom->setEdited(FALSE);
03461   mEdtReplyTo->setEdited(FALSE);
03462   mEdtTo->setEdited(FALSE);
03463   mEdtCc->setEdited(FALSE);
03464   mEdtBcc->setEdited(FALSE);
03465   mEdtSubject->setEdited(FALSE);
03466   if (mTransport->lineEdit())
03467     mTransport->lineEdit()->setEdited(FALSE);
03468   mAtmModified = FALSE;
03469 
03470   for ( QValueVector<KMMessage*>::iterator it = mComposedMessages.begin() ; it != mComposedMessages.end() ; ++it ) {
03471 
03472     // remove fields that contain no data (e.g. an empty Cc: or Bcc:)
03473     (*it)->cleanupHeader();
03474 
03475     // needed for imap
03476     (*it)->setComplete( true );
03477 
03478     if (mSaveInDrafts) {
03479       KMFolder* draftsFolder = 0, *imapDraftsFolder = 0;
03480       // get the draftsFolder
03481       if ( !(*it)->drafts().isEmpty() ) {
03482     draftsFolder = kmkernel->folderMgr()->findIdString( (*it)->drafts() );
03483     if ( draftsFolder == 0 )
03484       // This is *NOT* supposed to be "imapDraftsFolder", because a
03485       // dIMAP folder works like a normal folder
03486       draftsFolder = kmkernel->dimapFolderMgr()->findIdString( (*it)->drafts() );
03487     if ( draftsFolder == 0 )
03488       imapDraftsFolder = kmkernel->imapFolderMgr()->findIdString( (*it)->drafts() );
03489     if ( !draftsFolder && !imapDraftsFolder ) {
03490       const KPIM::Identity & id = kmkernel->identityManager()
03491         ->identityForUoidOrDefault( (*it)->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
03492       KMessageBox::information(0, i18n("The custom drafts folder for identity "
03493                        "\"%1\" does not exist (anymore); "
03494                        "therefore, the default drafts folder "
03495                        "will be used.")
03496                    .arg( id.identityName() ) );
03497     }
03498       }
03499       if (imapDraftsFolder && imapDraftsFolder->noContent())
03500     imapDraftsFolder = 0;
03501 
03502       if ( draftsFolder == 0 ) {
03503     draftsFolder = kmkernel->draftsFolder();
03504       } else {
03505     draftsFolder->open();
03506       }
03507       kdDebug(5006) << "saveindrafts: drafts=" << draftsFolder->name() << endl;
03508       if (imapDraftsFolder)
03509     kdDebug(5006) << "saveindrafts: imapdrafts="
03510               << imapDraftsFolder->name() << endl;
03511 
03512       sentOk = !(draftsFolder->addMsg((*it)));
03513 
03514       //Ensure the drafts message is correctly and fully parsed
03515       draftsFolder->unGetMsg(draftsFolder->count() - 1);
03516       (*it) = draftsFolder->getMsg(draftsFolder->count() - 1);
03517 
03518       if (imapDraftsFolder) {
03519     // move the message to the imap-folder and highlight it
03520     imapDraftsFolder->moveMsg((*it));
03521     (static_cast<KMFolderImap*>(imapDraftsFolder->storage()))->getFolder();
03522       }
03523 
03524     } else {
03525       (*it)->setTo( KMMessage::expandAliases( to() ));
03526       (*it)->setCc( KMMessage::expandAliases( cc() ));
03527       if( !mComposer->originalBCC().isEmpty() )
03528     (*it)->setBcc( KMMessage::expandAliases( mComposer->originalBCC() ));
03529       QString recips = (*it)->headerField( "X-KMail-Recipients" );
03530       if( !recips.isEmpty() ) {
03531     (*it)->setHeaderField( "X-KMail-Recipients", KMMessage::expandAliases( recips ) );
03532       }
03533       (*it)->cleanupHeader();
03534       sentOk = kmkernel->msgSender()->send((*it), mSendNow);
03535     }
03536 
03537     if (!sentOk)
03538       return;
03539 
03540     *it = 0; // don't kill it later...
03541   }
03542 
03543   RecentAddresses::self( KMKernel::config() )->add( bcc() );
03544   RecentAddresses::self( KMKernel::config() )->add( cc() );
03545   RecentAddresses::self( KMKernel::config() )->add( to() );
03546 
03547   mAutoDeleteMsg = FALSE;
03548   mFolder = 0;
03549   cleanupAutoSave();
03550   close();
03551   return;
03552 }
03553 
03554 
03555 
03556 //----------------------------------------------------------------------------
03557 void KMComposeWin::slotSendLater()
03558 {
03559   if ( mEditor->checkExternalEditorFinished() )
03560     doSend( false );
03561 }
03562 
03563 
03564 //----------------------------------------------------------------------------
03565 void KMComposeWin::slotSaveDraft() {
03566   if ( mEditor->checkExternalEditorFinished() )
03567     doSend( false, true );
03568 }
03569 
03570 
03571 //----------------------------------------------------------------------------
03572 void KMComposeWin::slotSendNow() {
03573   if ( !mEditor->checkExternalEditorFinished() )
03574     return;
03575   if (mConfirmSend) {
03576     switch(KMessageBox::warningYesNoCancel(mMainWidget,
03577                                     i18n("About to send email..."),
03578                                     i18n("Send Confirmation"),
03579                                     i18n("Send &Now"),
03580                                     i18n("Send &Later"))) {
03581     case KMessageBox::Yes:        // send now
03582         doSend(TRUE);
03583       break;
03584     case KMessageBox::No:        // send later
03585         doSend(FALSE);
03586       break;
03587     case KMessageBox::Cancel:        // cancel
03588       break;
03589     default:
03590       ;    // whoa something weird happened here!
03591     }
03592     return;
03593   }
03594 
03595   doSend(TRUE);
03596 }
03597 
03598 
03599 //----------------------------------------------------------------------------
03600 void KMComposeWin::slotAppendSignature()
03601 {
03602     insertSignature();
03603 }
03604 
03605 //----------------------------------------------------------------------------
03606 void KMComposeWin::slotPrependSignature()
03607 {
03608     insertSignature( false );
03609 }
03610 
03611 //----------------------------------------------------------------------------
03612 void KMComposeWin::slotInsertSignatureAtCursor()
03613 {
03614     insertSignature( false, mEditor->currentLine() );
03615 }
03616 
03617 
03618 //----------------------------------------------------------------------------
03619 void KMComposeWin::insertSignature( bool append, int pos )
03620 {
03621   bool mod = mEditor->isModified();
03622 
03623   const KPIM::Identity & ident =
03624     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
03625   mOldSigText = append? ident.signatureText() : ident.signature().rawText();
03626   if( !mOldSigText.isEmpty() )
03627   {
03628     mEditor->sync();
03629     if ( append ) {
03630        mEditor->append(mOldSigText);
03631     } else {
03632        mEditor->insertAt(mOldSigText, pos, 0);
03633     }
03634     mEditor->update();
03635     mEditor->setModified(mod);
03636     // for append and prepend, move the cursor to 0,0, for insertAt,
03637     // keep it in the same row, but move to first column
03638     mEditor->setCursorPosition( pos, 0 );
03639     if ( !append && pos == 0 )
03640       mEditor->setContentsPos( 0, 0 );
03641   }
03642 }
03643 
03644 
03645 //-----------------------------------------------------------------------------
03646 void KMComposeWin::slotHelp()
03647 {
03648   kapp->invokeHelp();
03649 }
03650 
03651 //-----------------------------------------------------------------------------
03652 void KMComposeWin::slotCleanSpace()
03653 {
03654   mEditor->cleanWhiteSpace();
03655 }
03656 
03657 //-----------------------------------------------------------------------------
03658 void KMComposeWin::slotToggleMarkup()
03659 {
03660  if ( markupAction->isChecked() ) {
03661     toolBar("htmlToolBar")->show();
03662    // markup will be toggled as soon as markup is actually used
03663    fontChanged( mEditor->currentFont().family() ); // set buttons in correct position
03664    fontAction->setFont( mEditor->currentFont().family() );
03665    fontSizeAction->setFontSize( mEditor->currentFont().pointSize() );
03666    mSaveFont = mEditor->currentFont();
03667  }
03668  else
03669    toggleMarkup(false);
03670 
03671 }
03672 //-----------------------------------------------------------------------------
03673 void KMComposeWin::toggleMarkup(bool markup)
03674 {
03675   if ( markup ) {
03676     if ( !mUseHTMLEditor ) {
03677     kdDebug(5006) << "setting RichText editor" << endl;
03678     mUseHTMLEditor = true; // set it directly to true. setColor hits another toggleMarkup
03679 
03680     // set all highlighted text caused by spelling back to black
03681     int paraFrom, indexFrom, paraTo, indexTo;
03682     mEditor->getSelection ( &paraFrom, &indexFrom, &paraTo, &indexTo);
03683     mEditor->selectAll();
03684     // save the buttonstates because setColor calls fontChanged
03685     bool _bold = textBoldAction->isChecked();
03686     bool _italic = textItalicAction->isChecked();
03687     mEditor->setColor(QColor(0,0,0));
03688     textBoldAction->setChecked(_bold);
03689     textItalicAction->setChecked(_italic);
03690     mEditor->setSelection ( paraFrom, indexFrom, paraTo, indexTo);
03691 
03692     mEditor->setTextFormat(Qt::RichText);
03693     mEditor->setModified(true);
03694     markupAction->setChecked(true);
03695     toolBar( "htmlToolBar" )->show();
03696     mEditor->deleteAutoSpellChecking();
03697     mAutoSpellCheckingAction->setChecked(false);
03698     slotAutoSpellCheckingToggled(false);
03699    }
03700   }
03701   else if ( mUseHTMLEditor ) {
03702     kdDebug(5006) << "setting PlainText editor" << endl;
03703     mUseHTMLEditor = false;
03704     mEditor->setTextFormat(Qt::PlainText);
03705     QString text = mEditor->text();
03706     mEditor->setText(text); // otherwise the text still looks formatted
03707     mEditor->setModified(true);
03708     toolBar("htmlToolBar")->hide();
03709     mEditor->initializeAutoSpellChecking( mDictionaryCombo->spellConfig());
03710     slotAutoSpellCheckingToggled(true);
03711   }
03712   else if ( !markup && !mUseHTMLEditor )
03713     {
03714       toolBar("htmlToolBar")->hide();
03715     }
03716 }
03717 
03718 void KMComposeWin::slotSubjectTextSpellChecked()
03719 {
03720   mSubjectTextWasSpellChecked = true;
03721 }
03722 
03723 //-----------------------------------------------------------------------------
03724 void KMComposeWin::slotAutoSpellCheckingToggled( bool on )
03725 {
03726   if ( mEditor->autoSpellChecking(on) == -1 )
03727     mAutoSpellCheckingAction->setChecked(false); // set it to false again
03728 }
03729 //-----------------------------------------------------------------------------
03730 void KMComposeWin::slotSpellcheck()
03731 {
03732   if (mSpellCheckInProgress) return;
03733   mSubjectTextWasSpellChecked = false;
03734   mSpellCheckInProgress=TRUE;
03735   /*
03736     connect (mEditor, SIGNAL (spellcheck_progress (unsigned)),
03737     this, SLOT (spell_progress (unsigned)));
03738     */
03739 
03740   mEditor->spellcheck();
03741 }
03742 
03743 
03744 //-----------------------------------------------------------------------------
03745 void KMComposeWin::slotSpellcheckDone(int result)
03746 {
03747   kdDebug(5006) << "spell check complete: result = " << result << endl;
03748   mSpellCheckInProgress=FALSE;
03749 
03750   switch( result )
03751   {
03752     case KS_CANCEL:
03753       statusBar()->changeItem(i18n(" Spell check canceled."),0);
03754       break;
03755     case KS_STOP:
03756       statusBar()->changeItem(i18n(" Spell check stopped."),0);
03757       break;
03758     default:
03759       statusBar()->changeItem(i18n(" Spell check complete."),0);
03760       break;
03761   }
03762   QTimer::singleShot( 2000, this, SLOT(slotSpellcheckDoneClearStatus()) );
03763 }
03764 
03765 void KMComposeWin::slotSpellcheckDoneClearStatus()
03766 {
03767   statusBar()->changeItem("", 0);
03768 }
03769 
03770 
03771 //-----------------------------------------------------------------------------
03772 void KMComposeWin::focusNextPrevEdit(const QWidget* aCur, bool aNext)
03773 {
03774   QWidget* cur;
03775 
03776   if (!aCur)
03777   {
03778     cur=mEdtList.last();
03779   }
03780   else
03781   {
03782     for (cur=mEdtList.first(); aCur!=cur && cur; cur=mEdtList.next())
03783       ;
03784     if (!cur) return;
03785     if (aNext) cur = mEdtList.next();
03786     else cur = mEdtList.prev();
03787   }
03788   if (cur) cur->setFocus();
03789   else if (aNext) mEditor->setFocus(); //Key up from first doea nothing (sven)
03790 }
03791 
03792 //-----------------------------------------------------------------------------
03793 void KMComposeWin::slotIdentityChanged(uint uoid)
03794 {
03795   const KPIM::Identity & ident =
03796     kmkernel->identityManager()->identityForUoid( uoid );
03797   if ( ident.isNull() ) return;
03798 
03799   //Turn on/off signature actions if identity has no signature.
03800   slotUpdateSignatureActions();
03801 
03802   if(!ident.fullEmailAddr().isNull())
03803     mEdtFrom->setText(ident.fullEmailAddr());
03804   // make sure the From field is shown if it's empty
03805   if ( from().isEmpty() )
03806     mShowHeaders |= HDR_FROM;
03807   mEdtReplyTo->setText(ident.replyToAddr());
03808   // don't overwrite the BCC field when the user has edited it and the
03809   // BCC field of the new identity is empty
03810   if( !mEdtBcc->edited() || !ident.bcc().isEmpty() )
03811     mEdtBcc->setText(ident.bcc());
03812   // make sure the BCC field is shown because else it's ignored
03813   if (! ident.bcc().isEmpty()) {
03814     mShowHeaders |= HDR_BCC;
03815   }
03816   if (ident.organization().isEmpty())
03817     mMsg->removeHeaderField("Organization");
03818   else
03819     mMsg->setHeaderField("Organization", ident.organization());
03820 
03821   if (!mBtnTransport->isChecked()) {
03822     QString transp = ident.transport();
03823     if (transp.isEmpty())
03824     {
03825       mMsg->removeHeaderField("X-KMail-Transport");
03826       transp = mTransport->text(0);
03827     }
03828     else
03829       mMsg->setHeaderField("X-KMail-Transport", transp);
03830     bool found = false;
03831     int i;
03832     for (i = 0; i < mTransport->count(); i++) {
03833       if (mTransport->text(i) == transp) {
03834         found = true;
03835         mTransport->setCurrentItem(i);
03836         break;
03837       }
03838     }
03839     if (found == false) {
03840       if (i == mTransport->maxCount()) mTransport->setMaxCount(i + 1);
03841       mTransport->insertItem(transp,i);
03842       mTransport->setCurrentItem(i);
03843     }
03844   }
03845 
03846   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
03847 
03848   if ( !mBtnFcc->isChecked() )
03849   {
03850     if ( ident.fcc().isEmpty() )
03851       mFcc->setFolder( kmkernel->sentFolder() );
03852     else
03853       setFcc( ident.fcc() );
03854   }
03855 
03856   QString edtText = mEditor->text();
03857   bool appendNewSig = true;
03858   const bool prepended = GlobalSettings::prependSignatures();
03859   // try to truncate the old sig
03860   if( !mOldSigText.isEmpty() )
03861   {
03862     if( !prepended && edtText.endsWith( mOldSigText ) )
03863       edtText.truncate( edtText.length() - mOldSigText.length() );
03864     else if ( prepended && edtText.find( mOldSigText ) != -1 ) {
03865       edtText.remove(  mOldSigText );
03866     }
03867     else
03868       appendNewSig = false;
03869   }
03870   // now append the new sig
03871   mOldSigText = prepended ? ident.signature().rawText() : ident.signatureText();
03872   if( appendNewSig )
03873   {
03874     if( !mOldSigText.isEmpty() && mAutoSign ) {
03875       if ( prepended ) {
03876         edtText.prepend( mOldSigText );
03877       } else {
03878         edtText.append( mOldSigText );
03879       }
03880     }
03881     mEditor->setText( edtText );
03882   }
03883 
03884   // disable certain actions if there is no PGP user identity set
03885   // for this profile
03886   bool bNewIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
03887   bool bNewIdentityHasEncryptionKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
03888   mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
03889               !ident.pgpEncryptionKey().isEmpty() );
03890   // save the state of the sign and encrypt button
03891   if ( !bNewIdentityHasEncryptionKey && mLastIdentityHasEncryptionKey ) {
03892     mLastEncryptActionState = mEncryptAction->isChecked();
03893     setEncryption( false );
03894   }
03895   if ( !bNewIdentityHasSigningKey && mLastIdentityHasSigningKey ) {
03896     mLastSignActionState = mSignAction->isChecked();
03897     setSigning( false );
03898   }
03899   // restore the last state of the sign and encrypt button
03900   if ( bNewIdentityHasEncryptionKey && !mLastIdentityHasEncryptionKey )
03901       setEncryption( mLastEncryptActionState );
03902   if ( bNewIdentityHasSigningKey && !mLastIdentityHasSigningKey )
03903     setSigning( mLastSignActionState );
03904 
03905   mLastIdentityHasSigningKey = bNewIdentityHasSigningKey;
03906   mLastIdentityHasEncryptionKey = bNewIdentityHasEncryptionKey;
03907 
03908   mEditor->setModified(TRUE);
03909   mId = uoid;
03910 
03911   // make sure the From and BCC fields are shown if necessary
03912   rethinkFields( false );
03913 }
03914 
03915 //-----------------------------------------------------------------------------
03916 void KMComposeWin::slotSpellcheckConfig()
03917 {
03918   KWin kwin;
03919   QTabDialog qtd (this, "tabdialog", true);
03920   KSpellConfig mKSpellConfig (&qtd);
03921 
03922   qtd.addTab (&mKSpellConfig, i18n("Spellchecker"));
03923   qtd.setCancelButton ();
03924 
03925   kwin.setIcons (qtd.winId(), kapp->icon(), kapp->miniIcon());
03926   qtd.setCancelButton(KStdGuiItem::cancel().text());
03927   qtd.setOkButton(KStdGuiItem::ok().text());
03928 
03929   if (qtd.exec())
03930     mKSpellConfig.writeGlobalSettings();
03931 }
03932 
03933 //----------------------------------------------------------------------------
03934 void KMComposeWin::slotMenuTextCompletion()
03935 {
03936    //Get the currently selected item in the sub menu
03937    int nItem = mTextCompletionAction->currentItem();
03938 
03939    //Set proper value for that item (NOTE the coversion, very important).
03940    KConfig *config = KMKernel::config();
03941    KConfigGroup composer( config, "Composer" );
03942    composer.writeEntry( "Completion Mode", KMGlobalNS::completionModes[ nItem ].mode );
03943    config->sync();
03944 
03945    //trigger this to read config and set the lineEdits automatically.
03946    slotConfigChanged();
03947 
03948 }
03949 
03950 //-----------------------------------------------------------------------------
03951 void KMComposeWin::slotConfigureAddressCompletion()
03952 {
03953   kdDebug(5006) << k_funcinfo << endl;
03954   KPIM::LdapSearch ls;
03955   KPIM::CompletionOrderEditor editor( &ls, this );
03956   editor.exec();
03957 }
03958 
03959 //-----------------------------------------------------------------------------
03960 void KMComposeWin::slotStatusMessage(const QString &message)
03961 {
03962     statusBar()->changeItem( message, 0 );
03963 }
03964 
03965 void KMComposeWin::slotEditToolbars()
03966 {
03967   saveMainWindowSettings(KMKernel::config(), "Composer");
03968   KEditToolbar dlg(guiFactory(), this);
03969 
03970   connect( &dlg, SIGNAL(newToolbarConfig()),
03971            SLOT(slotUpdateToolbars()) );
03972 
03973   dlg.exec();
03974 }
03975 
03976 void KMComposeWin::slotUpdateToolbars()
03977 {
03978   createGUI("kmcomposerui.rc");
03979   applyMainWindowSettings(KMKernel::config(), "Composer");
03980 }
03981 
03982 void KMComposeWin::slotEditKeys()
03983 {
03984   KKeyDialog::configure( actionCollection(),
03985                          false /*don't allow one-letter shortcuts*/
03986                          );
03987 }
03988 
03989 void KMComposeWin::setReplyFocus( bool hasMessage )
03990 {
03991   mEditor->setFocus();
03992   if ( hasMessage )
03993     mEditor->setCursorPosition( 1, 0 );
03994 }
03995 
03996 void KMComposeWin::setFocusToSubject()
03997 {
03998   mEdtSubject->setFocus();
03999 }
04000 
04001 int KMComposeWin::autoSaveInterval() const
04002 {
04003   return GlobalSettings::self()->autosaveInterval() * 1000 * 60;
04004 }
04005 
04006 void KMComposeWin::initAutoSave()
04007 {
04008   kdDebug(5006) << k_funcinfo << endl;
04009   // make sure the autosave folder exists
04010   KMFolderMaildir::createMaildirFolders( KMKernel::localDataPath() + "autosave" );
04011   if ( mAutoSaveFilename.isEmpty() ) {
04012     mAutoSaveFilename = KMFolderMaildir::constructValidFileName();
04013   }
04014 
04015   updateAutoSave();
04016 }
04017 
04018 void KMComposeWin::updateAutoSave()
04019 {
04020   if ( autoSaveInterval() == 0 ) {
04021     delete mAutoSaveTimer; mAutoSaveTimer = 0;
04022   }
04023   else {
04024     if ( !mAutoSaveTimer ) {
04025       mAutoSaveTimer = new QTimer( this );
04026       connect( mAutoSaveTimer, SIGNAL( timeout() ),
04027                this, SLOT( autoSaveMessage() ) );
04028     }
04029     mAutoSaveTimer->start( autoSaveInterval() );
04030   }
04031 }
04032 
04033 void KMComposeWin::setAutoSaveFilename( const QString & filename )
04034 {
04035   if ( !mAutoSaveFilename.isEmpty() )
04036     KMFolderMaildir::removeFile( KMKernel::localDataPath() + "autosave",
04037                                  mAutoSaveFilename );
04038   mAutoSaveFilename = filename;
04039 }
04040 
04041 void KMComposeWin::cleanupAutoSave()
04042 {
04043   delete mAutoSaveTimer; mAutoSaveTimer = 0;
04044   if ( !mAutoSaveFilename.isEmpty() ) {
04045     kdDebug(5006) << k_funcinfo << "deleting autosave file "
04046                   << mAutoSaveFilename << endl;
04047     KMFolderMaildir::removeFile( KMKernel::localDataPath() + "autosave",
04048                                  mAutoSaveFilename );
04049     mAutoSaveFilename = QString();
04050   }
04051 }
04052 
04053 
04054 
04055 void KMComposeWin::slotCompletionModeChanged( KGlobalSettings::Completion mode)
04056 {
04057     KConfig *config = KMKernel::config();
04058     KConfigGroupSaver cs( config, "Composer" );
04059     config->writeEntry( "Completion Mode", (int) mode );
04060     config->sync(); // maybe not?
04061 
04062     // sync all the lineedits to the same completion mode
04063     mEdtFrom->setCompletionMode( mode );
04064     mEdtReplyTo->setCompletionMode( mode );
04065     mEdtTo->setCompletionMode( mode );
04066     mEdtCc->setCompletionMode( mode );
04067     mEdtBcc->setCompletionMode( mode );
04068 
04069     // sync the Text Completion submenu if lineEdits are changed.
04070     for ( int i=0; i < KMGlobalNS::numCompletionModes; ++i ) {
04071       if ( KMGlobalNS::completionModes[i].mode == mode )
04072        mTextCompletionAction->setCurrentItem( i );
04073     }
04074 
04075 }
04076 
04077 void KMComposeWin::slotConfigChanged()
04078 {
04079     readConfig();
04080     updateAutoSave();
04081 }
04082 
04083 /*
04084 * checks if the drafts-folder has been deleted
04085 * that is not nice so we set the system-drafts-folder
04086 */
04087 void KMComposeWin::slotFolderRemoved(KMFolder* folder)
04088 {
04089         if ( (mFolder) && (folder->idString() == mFolder->idString()) )
04090         {
04091                 mFolder = kmkernel->draftsFolder();
04092                 kdDebug(5006) << "restoring drafts to " << mFolder->idString() << endl;
04093         }
04094         if (mMsg) mMsg->setParent(0);
04095 }
04096 
04097 
04098 
04099 void KMComposeWin::slotSetAlwaysSend( bool bAlways )
04100 {
04101     mAlwaysSend = bAlways;
04102 }
04103 
04104 void KMComposeWin::slotListAction( const QString& style )
04105 {
04106     toggleMarkup(true);
04107     if ( style == i18n( "Standard" ) )
04108        mEditor->setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc );
04109     else if ( style == i18n( "Bulleted List (Disc)" ) )
04110        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc );
04111     else if ( style == i18n( "Bulleted List (Circle)" ) )
04112        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListCircle );
04113     else if ( style == i18n( "Bulleted List (Square)" ) )
04114        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListSquare );
04115     else if ( style == i18n( "Ordered List (Decimal)" ))
04116        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDecimal );
04117     else if ( style == i18n( "Ordered List (Alpha lower)" ) )
04118        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListLowerAlpha );
04119     else if ( style == i18n( "Ordered List (Alpha upper)" ) )
04120        mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListUpperAlpha );
04121     mEditor->viewport()->setFocus();
04122 }
04123 
04124 void KMComposeWin::slotFontAction( const QString& font)
04125 {
04126     toggleMarkup(true);
04127     mEditor->QTextEdit::setFamily( font );
04128     mEditor->viewport()->setFocus();
04129 }
04130 
04131 void KMComposeWin::slotSizeAction( int size )
04132 {
04133     toggleMarkup(true);
04134     mEditor->setPointSize( size );
04135     mEditor->viewport()->setFocus();
04136 }
04137 
04138 void KMComposeWin::slotAlignLeft()
04139 {
04140     toggleMarkup(true);
04141     mEditor->QTextEdit::setAlignment( AlignLeft );
04142 }
04143 
04144 void KMComposeWin::slotAlignCenter()
04145 {
04146     toggleMarkup(true);
04147     mEditor->QTextEdit::setAlignment( AlignHCenter );
04148 }
04149 
04150 void KMComposeWin::slotAlignRight()
04151 {
04152     toggleMarkup(true);
04153     mEditor->QTextEdit::setAlignment( AlignRight );
04154 }
04155 
04156 void KMComposeWin::slotTextBold()
04157 {
04158     toggleMarkup(true);
04159     mEditor->QTextEdit::setBold( textBoldAction->isChecked() );
04160 }
04161 
04162 void KMComposeWin::slotTextItalic()
04163 {
04164     toggleMarkup(true);
04165     mEditor->QTextEdit::setItalic( textItalicAction->isChecked() );
04166 }
04167 
04168 void KMComposeWin::slotTextUnder()
04169 {
04170     toggleMarkup(true);
04171     mEditor->QTextEdit::setUnderline( textUnderAction->isChecked() );
04172 }
04173 
04174 void KMComposeWin::slotFormatReset()
04175 {
04176   mEditor->setColor(mForeColor);
04177   mEditor->setCurrentFont( mSaveFont ); // fontChanged is called now
04178 }
04179 void KMComposeWin::slotTextColor()
04180 {
04181   QColor color = mEditor->color();
04182 
04183   if ( KColorDialog::getColor( color, this ) ) {
04184     toggleMarkup(true);
04185     mEditor->setColor( color );
04186   }
04187 }
04188 
04189 void KMComposeWin::fontChanged( const QFont &f )
04190 {
04191   QFontDatabase *fontdb = new QFontDatabase();
04192 
04193   if ( fontdb->bold(f.family(), "Bold") ) {
04194     textBoldAction->setChecked( f.bold() );
04195     textBoldAction->setEnabled(true);
04196   }
04197   else
04198     textBoldAction->setEnabled(false);
04199 
04200   if ( fontdb->italic(f.family(), "Italic") ) {
04201     textItalicAction->setChecked( f.italic() );
04202     textItalicAction->setEnabled(true);
04203   }
04204   else
04205     textItalicAction->setEnabled(false);
04206 
04207   textUnderAction->setChecked( f.underline() );
04208 
04209   fontAction->setFont( f.family() );
04210   fontSizeAction->setFontSize( f.pointSize() );
04211   delete fontdb;
04212 }
04213 
04214 void KMComposeWin::alignmentChanged( int a )
04215 {
04216     //toggleMarkup();
04217     alignLeftAction->setChecked( ( a == AlignAuto ) || ( a & AlignLeft ) );
04218     alignCenterAction->setChecked( ( a & AlignHCenter ) );
04219     alignRightAction->setChecked( ( a & AlignRight ) );
04220 }
04221 
04222 namespace {
04223   class KToggleActionResetter {
04224     KToggleAction * mAction;
04225     bool mOn;
04226   public:
04227     KToggleActionResetter( KToggleAction * action, bool on )
04228       : mAction( action ),  mOn( on ) {}
04229     ~KToggleActionResetter() {
04230       if ( mAction )
04231         mAction->setChecked( mOn );
04232     }
04233     void disable() { mAction = 0; }
04234   };
04235 }
04236 
04237 
04238 void KMComposeWin::slotEncryptBodyChiasmusToggled( bool on ) {
04239 #ifdef KLEO_CHIASMUS
04240     Q_ASSERT( mEncryptChiasmusAction->isEnabled() );
04241     if ( on ) {
04242       // FIXME remove once body encryption is fixed
04243       // user wants to enable body chiamsus encryption, which means
04244       // we currently need to disable signing and encryption using the
04245       // other mechanisms. Warn the user.
04246       if ( mEncryptAction->isChecked() || mSignAction->isChecked() ) {
04247         int ret = KMessageBox::warningContinueCancel( this,
04248             i18n("It is currently not possible to use chiasmus body encryption "
04249               "with signed or otherwise encrypted messages. Do you want to "
04250               "continue with chiasmus body encryption and disable other signature "
04251               "and encryption methods, or cancel, which will not encrypt the body "
04252               "using chiasmus?" ),
04253             i18n("Chiasmus body encryption not possible") );
04254         if ( ret == KMessageBox::Continue ) {
04255           setSigning( false, true );
04256           setEncryption( false, true );
04257         } else {
04258           mEncryptBodyChiasmusAction->setEnabled( false ); // reset
04259           return;
04260         }
04261 
04262       }
04263     }
04264     mEncryptBodyWithChiasmus = on;
04265 #endif
04266 }
04267 
04268 void KMComposeWin::slotEncryptChiasmusToggled( bool on ) {
04269 #ifdef KLEO_CHIASMUS
04270     mEncryptWithChiasmus = false;
04271     mEncryptBodyChiasmusAction->setEnabled( on );
04272 #endif
04273 
04274   if ( !on )
04275     return;
04276 
04277 #ifdef KLEO_CHIASMUS
04278   KToggleActionResetter resetter( mEncryptChiasmusAction, false );
04279 #endif
04280 
04281 #ifndef KLEO_CHIASMUS
04282 
04283   KMessageBox::information( this, i18n( "This version of KMail was not compiled "
04284                                         "with support for Chiasmus. You might want "
04285                                         "to recompile KMail with --enable-chiasmus." ),
04286                             i18n( "Missing Chiasmus Support" ) );
04287   return;
04288 
04289 #else
04290 
04291   const Kleo::CryptoBackend::Protocol * chiasmus =
04292     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
04293 
04294   if ( !chiasmus ) {
04295     const QString msg = Kleo::CryptoBackendFactory::instance()->knowsAboutProtocol( "Chiasmus" )
04296       ? i18n( "Please configure a Crypto Backend to use for "
04297               "Chiasmus encryption first.\n"
04298               "You can do this in the Crypto Backends tab of "
04299               "the configure dialog's Security page." )
04300       : i18n( "It looks as though libkleopatra was compiled without "
04301               "Chiasmus support. You might want to recompile "
04302               "libkleopatra with --enable-chiasmus.");
04303     KMessageBox::information( this, msg, i18n("No Chiasmus Backend Configured" ) );
04304     return;
04305   }
04306 
04307   std::auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
04308   if ( !job.get() ) {
04309     const QString msg = i18n( "Chiasmus backend does not offer the "
04310                               "\"x-obtain-keys\" function. Please report this bug." );
04311     KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
04312     return;
04313   }
04314 
04315   if ( job->exec() ) {
04316     job->showErrorDialog( this, i18n( "Chiasmus Backend Error" ) );
04317     return;
04318   }
04319 
04320   const QVariant result = job->property( "result" );
04321   if ( result.type() != QVariant::StringList ) {
04322     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
04323                               "The \"x-obtain-keys\" function did not return a "
04324                               "string list. Please report this bug." );
04325     KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
04326     return;
04327   }
04328 
04329   const QStringList keys = result.toStringList();
04330   if ( keys.empty() ) {
04331     const QString msg = i18n( "No keys have been found. Please check that a "
04332                               "valid key path has been set in the Chiasmus "
04333                               "configuration." );
04334     KMessageBox::information( this, msg, i18n( "No Chiasmus Keys Found" ) );
04335     return;
04336   }
04337 
04338   ChiasmusKeySelector selectorDlg( this, i18n( "Chiasmus Encryption Key Selection" ),
04339                                    keys, mChiasmusKey, mChiasmusOptions );
04340   if ( selectorDlg.exec() != QDialog::Accepted )
04341     return;
04342 
04343   mChiasmusOptions = selectorDlg.options();
04344   mChiasmusKey = selectorDlg.key();
04345   assert( !mChiasmusKey.isEmpty() );
04346   mEncryptWithChiasmus = true;
04347   resetter.disable();
04348 #endif // KLEO_CHIASMUS
04349 }
04350 
04351 
04352 void KMComposeWin::slotUpdateSignatureActions()
04353 {
04354   //Check if an identity has signature or not and turn on/off actions in the
04355   //edit menu accordingly.
04356   const KPIM::Identity & ident =
04357     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
04358   QString sig = ident.signatureText();
04359 
04360   if ( sig.isEmpty() ) {
04361      mAppendSignatureAction->setEnabled( false );
04362      mPrependSignatureAction->setEnabled( false );
04363      mInsertSignatureAction->setEnabled( false );
04364   }
04365   else {
04366       mAppendSignatureAction->setEnabled( true );
04367       mPrependSignatureAction->setEnabled( true );
04368       mInsertSignatureAction->setEnabled( true );
04369   }
04370 }
04371 
04372 void KMEdit::contentsDragEnterEvent(QDragEnterEvent *e)
04373 {
04374     if (e->provides(MailListDrag::format()))
04375         e->accept(true);
04376     else
04377         return KEdit::contentsDragEnterEvent(e);
04378 }
04379 
04380 void KMEdit::contentsDragMoveEvent(QDragMoveEvent *e)
04381 {
04382     if (e->provides(MailListDrag::format()))
04383         e->accept();
04384     else
04385         return KEdit::contentsDragMoveEvent(e);
04386 }
04387 
04388 void KMEdit::keyPressEvent( QKeyEvent* e )
04389 {
04390     if( e->key() == Key_Return ) {
04391         int line, col;
04392         getCursorPosition( &line, &col );
04393         QString lineText = text( line );
04394         // returns line with additional trailing space (bug in Qt?), cut it off
04395         lineText.truncate( lineText.length() - 1 );
04396         // special treatment of quoted lines only if the cursor is neither at
04397         // the begin nor at the end of the line
04398         if( ( col > 0 ) && ( col < int( lineText.length() ) ) ) {
04399             bool isQuotedLine = false;
04400             uint bot = 0; // bot = begin of text after quote indicators
04401             while( bot < lineText.length() ) {
04402                 if( ( lineText[bot] == '>' ) || ( lineText[bot] == '|' ) ) {
04403                     isQuotedLine = true;
04404                     ++bot;
04405                 }
04406                 else if( lineText[bot].isSpace() ) {
04407                     ++bot;
04408                 }
04409                 else {
04410                     break;
04411                 }
04412             }
04413 
04414             KEdit::keyPressEvent( e );
04415 
04416             // duplicate quote indicators of the previous line before the new
04417             // line if the line actually contained text (apart from the quote
04418             // indicators) and the cursor is behind the quote indicators
04419             if( isQuotedLine
04420                 && ( bot != lineText.length() )
04421                 && ( col >= int( bot ) ) ) {
04422 
04423         // The cursor position might have changed unpredictably if there was selected
04424         // text which got replaced by a new line, so we query it again:
04425         getCursorPosition( &line, &col );
04426                 QString newLine = text( line );
04427                 // remove leading white space from the new line and instead
04428                 // add the quote indicators of the previous line
04429                 unsigned int leadingWhiteSpaceCount = 0;
04430                 while( ( leadingWhiteSpaceCount < newLine.length() )
04431                        && newLine[leadingWhiteSpaceCount].isSpace() ) {
04432                     ++leadingWhiteSpaceCount;
04433                 }
04434                 newLine = newLine.replace( 0, leadingWhiteSpaceCount,
04435                                            lineText.left( bot ) );
04436                 removeParagraph( line );
04437                 insertParagraph( newLine, line );
04438                 // place the cursor at the begin of the new line since
04439                 // we assume that the user split the quoted line in order
04440                 // to add a comment to the first part of the quoted line
04441                 setCursorPosition( line, 0 );
04442             }
04443         }
04444         else
04445             KEdit::keyPressEvent( e );
04446     }
04447     else
04448         KEdit::keyPressEvent( e );
04449 }
04450 
04451 void KMEdit::contentsDropEvent(QDropEvent *e)
04452 {
04453     if (e->provides(MailListDrag::format())) {
04454         // Decode the list of serial numbers stored as the drag data
04455         QByteArray serNums;
04456         MailListDrag::decode( e, serNums );
04457         QBuffer serNumBuffer(serNums);
04458         serNumBuffer.open(IO_ReadOnly);
04459         QDataStream serNumStream(&serNumBuffer);
04460         unsigned long serNum;
04461         KMFolder *folder = 0;
04462         int idx;
04463         QPtrList<KMMsgBase> messageList;
04464         while (!serNumStream.atEnd()) {
04465             KMMsgBase *msgBase = 0;
04466             serNumStream >> serNum;
04467             kmkernel->msgDict()->getLocation(serNum, &folder, &idx);
04468             if (folder)
04469                 msgBase = folder->getMsgBase(idx);
04470             if (msgBase)
04471                 messageList.append( msgBase );
04472         }
04473         serNumBuffer.close();
04474         uint identity = folder ? folder->identity() : 0;
04475         KMCommand *command =
04476             new KMForwardAttachedCommand(mComposer, messageList,
04477                                          identity, mComposer);
04478         command->start();
04479     }
04480     else if( KURLDrag::canDecode( e ) ) {
04481         KURL::List urlList;
04482         if( KURLDrag::decode( e, urlList ) ) {
04483             for( KURL::List::Iterator it = urlList.begin();
04484                  it != urlList.end(); ++it ) {
04485                 mComposer->addAttach( *it );
04486             }
04487         }
04488     }
04489     else {
04490         return KEdit::contentsDropEvent(e);
04491     }
04492 }
04493 
04494 //=============================================================================
04495 //
04496 //   Class  KMAtmListViewItem
04497 //
04498 //=============================================================================
04499 
04500 KMAtmListViewItem::KMAtmListViewItem(QListView *parent) :
04501   QObject(), QListViewItem( parent )
04502 {
04503 
04504   mCBSignEnabled = false;
04505   mCBEncryptEnabled = false;
04506 
04507   mListview = parent;
04508   mCBEncrypt = new QCheckBox(mListview->viewport());
04509   mCBSign = new QCheckBox(mListview->viewport());
04510 
04511   mCBEncrypt->hide();
04512   mCBSign->hide();
04513 }
04514 
04515 KMAtmListViewItem::~KMAtmListViewItem()
04516 {
04517   delete mCBEncrypt;
04518   mCBEncrypt = 0;
04519   delete mCBSign;
04520   mCBSign = 0;
04521 }
04522 
04523 void KMAtmListViewItem::paintCell( QPainter * p, const QColorGroup & cg,
04524                                   int column, int width, int align )
04525 {
04526   // this is also called for the encrypt/sign columns to assure that the
04527   // background is cleared
04528   QListViewItem::paintCell( p, cg, column, width, align );
04529   if( 4 == column || 5 == column ) {
04530     QRect r = mListview->itemRect( this );
04531     if ( !r.size().isValid() ) {
04532         mListview->ensureItemVisible( this );
04533         mListview->repaintContents( FALSE );
04534         r = mListview->itemRect( this );
04535     }
04536     int colWidth = mListview->header()->sectionSize( column );
04537     r.setX( mListview->header()->sectionPos( column )
04538             - mListview->header()->offset()
04539             + colWidth / 2
04540             - r.height() / 2
04541             - 1 );
04542     r.setY( r.y() + 1 );
04543     r.setWidth(  r.height() - 2 );
04544     r.setHeight( r.height() - 2 );
04545     r = QRect( mListview->viewportToContents( r.topLeft() ), r.size() );
04546 
04547     QCheckBox* cb = (4 == column) ? mCBEncrypt : mCBSign;
04548     cb->resize( r.size() );
04549     mListview->moveChild( cb, r.x(), r.y() );
04550 
04551     QColor bg;
04552     if (isSelected())
04553       bg = cg.highlight();
04554     else
04555       bg = cg.base();
04556 
04557     bool enabled = (4 == column) ? mCBEncryptEnabled : mCBSignEnabled;
04558     cb->setPaletteBackgroundColor(bg);
04559     if (enabled) cb->show();
04560   }
04561 }
04562 
04563 void KMAtmListViewItem::enableCryptoCBs(bool on)
04564 {
04565   if( mCBEncrypt ) {
04566     mCBEncryptEnabled = on;
04567     mCBEncrypt->setEnabled( on );
04568     mCBEncrypt->setShown( on );
04569   }
04570   if( mCBSign ) {
04571     mCBSignEnabled = on;
04572     mCBSign->setEnabled( on );
04573     mCBSign->setShown( on );
04574   }
04575 }
04576 
04577 void KMAtmListViewItem::setEncrypt(bool on)
04578 {
04579   if( mCBEncrypt )
04580     mCBEncrypt->setChecked( on );
04581 }
04582 
04583 bool KMAtmListViewItem::isEncrypt()
04584 {
04585   if( mCBEncrypt )
04586     return mCBEncrypt->isChecked();
04587   else
04588     return false;
04589 }
04590 
04591 void KMAtmListViewItem::setSign(bool on)
04592 {
04593   if( mCBSign )
04594     mCBSign->setChecked( on );
04595 }
04596 
04597 bool KMAtmListViewItem::isSign()
04598 {
04599   if( mCBSign )
04600     return mCBSign->isChecked();
04601   else
04602     return false;
04603 }
04604 
04605 
04606 
04607 //=============================================================================
04608 //
04609 //   Class  KMLineEdit
04610 //
04611 //=============================================================================
04612 
04613 KMLineEdit::KMLineEdit(KMComposeWin* composer, bool useCompletion,
04614                        QWidget *parent, const char *name)
04615     : KPIM::AddresseeLineEdit(parent,useCompletion,name), mComposer(composer)
04616 {
04617   allowSemiColonAsSeparator( GlobalSettings::allowSemicolonAsAddressSeparator() );
04618 }
04619 
04620 
04621 //-----------------------------------------------------------------------------
04622 void KMLineEdit::keyPressEvent(QKeyEvent *e)
04623 {
04624     // ---sven's Return is same Tab and arrow key navigation start ---
04625     if ((e->key() == Key_Enter || e->key() == Key_Return) &&
04626         !completionBox()->isVisible())
04627     {
04628       mComposer->focusNextPrevEdit(this,TRUE);
04629       return;
04630     }
04631     if (e->key() == Key_Up)
04632     {
04633       mComposer->focusNextPrevEdit(this,FALSE); // Go up
04634       return;
04635     }
04636     if (e->key() == Key_Down)
04637     {
04638       mComposer->focusNextPrevEdit(this,TRUE); // Go down
04639       return;
04640     }
04641     // ---sven's Return is same Tab and arrow key navigation end ---
04642   AddresseeLineEdit::keyPressEvent(e);
04643 }
04644 
04645 QPopupMenu *KMLineEdit::createPopupMenu()
04646 {
04647     QPopupMenu *menu = KPIM::AddresseeLineEdit::createPopupMenu();
04648     if ( !menu )
04649         return 0;
04650 
04651     menu->insertSeparator();
04652     menu->insertItem( i18n( "Edit Recent Addresses..." ),
04653                       this, SLOT( editRecentAddresses() ) );
04654 
04655     return menu;
04656 }
04657 
04658 void KMLineEdit::editRecentAddresses()
04659 {
04660   KRecentAddress::RecentAddressDialog dlg( this );
04661   dlg.setAddresses( RecentAddresses::self( KMKernel::config() )->addresses() );
04662   if ( dlg.exec() ) {
04663     RecentAddresses::self( KMKernel::config() )->clear();
04664     QStringList addrList = dlg.addresses();
04665     QStringList::Iterator it;
04666     for ( it = addrList.begin(); it != addrList.end(); ++it )
04667       RecentAddresses::self( KMKernel::config() )->add( *it );
04668 
04669     loadContacts();
04670   }
04671 }
04672 
04673 
04674 //-----------------------------------------------------------------------------
04675 void KMLineEdit::loadContacts()
04676 {
04677     // was: KABC::AddressLineEdit::loadAddresses()
04678     AddresseeLineEdit::loadContacts();
04679 
04680     QStringList recent =
04681       RecentAddresses::self( KMKernel::config() )->addresses();
04682     QStringList::Iterator it = recent.begin();
04683     QString name, email;
04684     int idx = addCompletionSource( i18n( "Recent Addresses" ) );
04685     for ( ; it != recent.end(); ++it ) {
04686       //kdDebug(5006) << "KMLineEdit::loadContacts() found: \"" << *it << "\"" << endl;
04687       KABC::Addressee addr;
04688       KPIM::getNameAndMail(*it, name, email);
04689       addr.setNameFromString( name );
04690       addr.insertEmail( email, true );
04691       addContact( addr, 120, idx ); // more weight than kabc entries and more than ldap results
04692     }
04693 }
04694 
04695 
04696 KMLineEditSpell::KMLineEditSpell(KMComposeWin* composer, bool useCompletion,
04697                        QWidget *parent, const char *name)
04698     : KMLineEdit(composer,useCompletion,parent,name)
04699 {
04700 }
04701 
04702 
04703 void KMLineEditSpell::highLightWord( unsigned int length, unsigned int pos )
04704 {
04705     setSelection ( pos, length );
04706 }
04707 
04708 void KMLineEditSpell::spellCheckDone( const QString &s )
04709 {
04710     if( s != text() )
04711         setText( s );
04712 }
04713 
04714 void KMLineEditSpell::spellCheckerMisspelling( const QString &_text, const QStringList&, unsigned int pos)
04715 {
04716      highLightWord( _text.length(),pos );
04717 }
04718 
04719 void KMLineEditSpell::spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos)
04720 {
04721     if( old!= corr )
04722     {
04723         setSelection ( pos, old.length() );
04724         insert( corr );
04725         setSelection ( pos, corr.length() );
04726         emit subjectTextSpellChecked();
04727     }
04728 }
04729 
04730 
04731 //=============================================================================
04732 //
04733 //   Class  KMEdit
04734 //
04735 //=============================================================================
04736 KMEdit::KMEdit(QWidget *parent, KMComposeWin* composer,
04737                KSpellConfig* autoSpellConfig,
04738                const char *name)
04739   : KEdit( parent, name ),
04740     mComposer( composer ),
04741     mKSpell( 0 ),
04742     mSpellingFilter( 0 ),
04743     mExtEditorTempFile( 0 ),
04744     mExtEditorTempFileWatcher( 0 ),
04745     mExtEditorProcess( 0 ),
04746     mUseExtEditor( false ),
04747     mWasModifiedBeforeSpellCheck( false ),
04748     mSpellChecker( 0 ),
04749     mSpellLineEdit( false )
04750 {
04751   installEventFilter(this);
04752   KCursor::setAutoHideCursor( this, true, true );
04753   setOverwriteEnabled( true );
04754 
04755   initializeAutoSpellChecking( autoSpellConfig );
04756 }
04757 
04758 //-----------------------------------------------------------------------------
04759 void KMEdit::initializeAutoSpellChecking( KSpellConfig* autoSpellConfig )
04760 {
04761   if ( mSpellChecker )
04762     return; // already initialized
04763   KConfigGroup readerConfig( KMKernel::config(), "Reader" );
04764   QColor defaultColor1( 0x00, 0x80, 0x00 ); // defaults from kmreaderwin.cpp
04765   QColor defaultColor2( 0x00, 0x70, 0x00 );
04766   QColor defaultColor3( 0x00, 0x60, 0x00 );
04767   QColor defaultForeground( kapp->palette().active().text() );
04768   QColor col1;
04769   if (!readerConfig.readBoolEntry("defaultColors",TRUE))
04770     col1 = readerConfig.readColorEntry( "ForegroundColor", &defaultForeground );
04771   else
04772     col1 = defaultForeground;
04773   QColor col2 = readerConfig.readColorEntry( "QuotedText3", &defaultColor3 );
04774   QColor col3 = readerConfig.readColorEntry( "QuotedText2", &defaultColor2 );
04775   QColor col4 = readerConfig.readColorEntry( "QuotedText1", &defaultColor1 );
04776   QColor c = Qt::red;
04777   QColor misspelled = readerConfig.readColorEntry( "MisspelledColor", &c );
04778 
04779   mSpellChecker = new KDictSpellingHighlighter( this, /*active*/ true,
04780                                                 /*autoEnabled*/ false,
04781                                                 /*spellColor*/ misspelled,
04782                                                 /*colorQuoting*/ true,
04783                                                 col1, col2, col3, col4,
04784                                                 autoSpellConfig );
04785   connect( mSpellChecker, SIGNAL(activeChanged(const QString &)),
04786            mComposer, SLOT(slotStatusMessage(const QString &)));
04787   connect( mSpellChecker, SIGNAL(newSuggestions(const QString&, const QStringList&, unsigned int)),
04788            this, SLOT(addSuggestion(const QString&, const QStringList&, unsigned int)) );
04789 }
04790 
04791 //-----------------------------------------------------------------------------
04792 void KMEdit::deleteAutoSpellChecking()
04793 { // because the highlighter doesn't support RichText, delete its instance.
04794   delete mSpellChecker;
04795   mSpellChecker =0;
04796 }
04797 //-----------------------------------------------------------------------------
04798 void KMEdit::addSuggestion(const QString& text, const QStringList& lst, unsigned int )
04799 {
04800   mReplacements[text] = lst;
04801 }
04802 
04803 void KMEdit::setSpellCheckingActive(bool spellCheckingActive)
04804 {
04805   if ( mSpellChecker ) {
04806     mSpellChecker->setActive(spellCheckingActive);
04807   }
04808 }
04809 
04810 //-----------------------------------------------------------------------------
04811 KMEdit::~KMEdit()
04812 {
04813   removeEventFilter(this);
04814 
04815   delete mKSpell;
04816   delete mSpellChecker;
04817   mSpellChecker = 0;
04818 
04819 }
04820 
04821 
04822 //-----------------------------------------------------------------------------
04823 QString KMEdit::brokenText()
04824 {
04825   QString temp, line;
04826 
04827   int num_lines = numLines();
04828   for (int i = 0; i < num_lines; ++i)
04829   {
04830     int lastLine = 0;
04831     line = textLine(i);
04832     for (int j = 0; j < (int)line.length(); ++j)
04833     {
04834       if (lineOfChar(i, j) > lastLine)
04835       {
04836         lastLine = lineOfChar(i, j);
04837         temp += '\n';
04838       }
04839       temp += line[j];
04840     }
04841     if (i + 1 < num_lines) temp += '\n';
04842   }
04843 
04844   return temp;
04845 }
04846 
04847 //-----------------------------------------------------------------------------
04848 bool KMEdit::eventFilter(QObject*o, QEvent* e)
04849 {
04850   if (o == this)
04851     KCursor::autoHideEventFilter(o, e);
04852 
04853   if (e->type() == QEvent::KeyPress)
04854   {
04855     QKeyEvent *k = (QKeyEvent*)e;
04856 
04857     if (mUseExtEditor) {
04858       if (k->key() == Key_Up)
04859       {
04860         mComposer->focusNextPrevEdit(0, false); //take me up
04861         return TRUE;
04862       }
04863 
04864       // ignore modifier keys (cf. bug 48841)
04865       if ( (k->key() == Key_Shift) || (k->key() == Key_Control) ||
04866            (k->key() == Key_Meta) || (k->key() == Key_Alt) )
04867         return true;
04868       if (mExtEditorTempFile) return TRUE;
04869       QString sysLine = mExtEditor;
04870       mExtEditorTempFile = new KTempFile();
04871 
04872       mExtEditorTempFile->setAutoDelete(true);
04873 
04874       (*mExtEditorTempFile->textStream()) << text();
04875 
04876       mExtEditorTempFile->close();
04877       // replace %f in the system line
04878       sysLine.replace( "%f", mExtEditorTempFile->name() );
04879       mExtEditorProcess = new KProcess();
04880       sysLine += " ";
04881       while (!sysLine.isEmpty())
04882       {
04883         *mExtEditorProcess << sysLine.left(sysLine.find(" ")).local8Bit();
04884         sysLine.remove(0, sysLine.find(" ") + 1);
04885       }
04886       connect(mExtEditorProcess, SIGNAL(processExited(KProcess*)),
04887               SLOT(slotExternalEditorDone(KProcess*)));
04888       if (!mExtEditorProcess->start())
04889       {
04890         KMessageBox::error( topLevelWidget(),
04891                             i18n("Unable to start external editor.") );
04892         killExternalEditor();
04893       } else {
04894         mExtEditorTempFileWatcher = new KDirWatch( this, "mExtEditorTempFileWatcher" );
04895         connect( mExtEditorTempFileWatcher, SIGNAL(dirty(const QString&)),
04896                  SLOT(slotExternalEditorTempFileChanged(const QString&)) );
04897         mExtEditorTempFileWatcher->addFile( mExtEditorTempFile->name() );
04898       }
04899       return TRUE;
04900     } else {
04901     // ---sven's Arrow key navigation start ---
04902     // Key Up in first line takes you to Subject line.
04903     if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0
04904       && lineOfChar(0, currentColumn()) == 0)
04905     {
04906       deselect();
04907       mComposer->focusNextPrevEdit(0, false); //take me up
04908       return TRUE;
04909     }
04910     // ---sven's Arrow key navigation end ---
04911 
04912     if (k->key() == Key_Backtab && k->state() == ShiftButton)
04913     {
04914       deselect();
04915       mComposer->focusNextPrevEdit(0, false);
04916       return TRUE;
04917     }
04918 
04919     }
04920   } else if ( e->type() == QEvent::ContextMenu ) {
04921     QContextMenuEvent *event = (QContextMenuEvent*) e;
04922 
04923     int para = 1, charPos, firstSpace, lastSpace;
04924 
04925     //Get the character at the position of the click
04926     charPos = charAt( viewportToContents(event->pos()), &para );
04927     QString paraText = text( para );
04928 
04929     if( !paraText.at(charPos).isSpace() )
04930     {
04931       //Get word right clicked on
04932       const QRegExp wordBoundary( "[\\s\\W]" );
04933       firstSpace = paraText.findRev( wordBoundary, charPos ) + 1;
04934       lastSpace = paraText.find( wordBoundary, charPos );
04935       if( lastSpace == -1 )
04936         lastSpace = paraText.length();
04937       QString word = paraText.mid( firstSpace, lastSpace - firstSpace );
04938       //Continue if this word was misspelled
04939       if( !word.isEmpty() && mReplacements.contains( word ) )
04940       {
04941         KPopupMenu p;
04942         p.insertTitle( i18n("Suggestions") );
04943 
04944         //Add the suggestions to the popup menu
04945         QStringList reps = mReplacements[word];
04946         if( reps.count() > 0 )
04947         {
04948           int listPos = 0;
04949           for ( QStringList::Iterator it = reps.begin(); it != reps.end(); ++it ) {
04950             p.insertItem( *it, listPos );
04951             listPos++;
04952           }
04953         }
04954         else
04955         {
04956           p.insertItem( QString::fromLatin1("No Suggestions"), -2 );
04957         }
04958 
04959         //Execute the popup inline
04960         int id = p.exec( mapToGlobal( event->pos() ) );
04961 
04962         if( id > -1 )
04963         {
04964           //Save the cursor position
04965           int parIdx = 1, txtIdx = 1;
04966           getCursorPosition(&parIdx, &txtIdx);
04967           setSelection(para, firstSpace, para, lastSpace);
04968           insert(mReplacements[word][id]);
04969           // Restore the cursor position; if the cursor was behind the
04970           // misspelled word then adjust the cursor position
04971           if ( para == parIdx && txtIdx >= lastSpace )
04972             txtIdx += mReplacements[word][id].length() - word.length();
04973           setCursorPosition(parIdx, txtIdx);
04974         }
04975         //Cancel original event
04976         return true;
04977       }
04978     }
04979   }
04980 
04981   return KEdit::eventFilter(o, e);
04982 }
04983 
04984 
04985 //-----------------------------------------------------------------------------
04986 int KMEdit::autoSpellChecking( bool on )
04987 {
04988   if ( textFormat() == Qt::RichText ) {
04989      // syntax highlighter doesn't support extended text properties
04990      if ( on )
04991        KMessageBox::sorry(this, i18n("Automatic spellchecking is not possible on text with markup."));
04992      return -1;
04993   }
04994 
04995   // don't autoEnable spell checking if the user turned spell checking off
04996   mSpellChecker->setAutomatic( on );
04997   mSpellChecker->setActive( on );
04998   return 1;
04999 }
05000 
05001 
05002 //-----------------------------------------------------------------------------
05003 void KMEdit::slotExternalEditorTempFileChanged( const QString & fileName ) {
05004   if ( !mExtEditorTempFile )
05005     return;
05006   if ( fileName != mExtEditorTempFile->name() )
05007     return;
05008   // read data back in from file
05009   setAutoUpdate(false);
05010   clear();
05011 
05012   insertLine(QString::fromLocal8Bit(KPIM::kFileToString( fileName, true, false )), -1);
05013   setAutoUpdate(true);
05014   repaint();
05015 }
05016 
05017 void KMEdit::slotExternalEditorDone( KProcess * proc ) {
05018   assert(proc == mExtEditorProcess);
05019   // make sure, we update even when KDirWatcher is too slow:
05020   slotExternalEditorTempFileChanged( mExtEditorTempFile->name() );
05021   killExternalEditor();
05022 }
05023 
05024 void KMEdit::killExternalEditor() {
05025   delete mExtEditorTempFileWatcher; mExtEditorTempFileWatcher = 0;
05026   delete mExtEditorTempFile; mExtEditorTempFile = 0;
05027   delete mExtEditorProcess; mExtEditorProcess = 0;
05028 }
05029 
05030 
05031 bool KMEdit::checkExternalEditorFinished() {
05032   if ( !mExtEditorProcess )
05033     return true;
05034   switch ( KMessageBox::warningYesNoCancel( topLevelWidget(),
05035            i18n("The external editor is still running.\n"
05036                 "Abort the external editor or leave it open?"),
05037            i18n("External Editor"),
05038            i18n("Abort Editor"), i18n("Leave Editor Open") ) ) {
05039   case KMessageBox::Yes:
05040     killExternalEditor();
05041     return true;
05042   case KMessageBox::No:
05043     return true;
05044   default:
05045     return false;
05046   }
05047 }
05048 
05049 //-----------------------------------------------------------------------------
05050 void KMEdit::spellcheck()
05051 {
05052   if ( mKSpell )
05053     return;
05054   mWasModifiedBeforeSpellCheck = isModified();
05055   mSpellLineEdit = !mSpellLineEdit;
05056 //  maybe for later, for now plaintext is given to KSpell
05057 //  if (textFormat() == Qt::RichText ) {
05058 //    kdDebug(5006) << "KMEdit::spellcheck, spellchecking for RichText" << endl;
05059 //    mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this,
05060 //                    SLOT(slotSpellcheck2(KSpell*)),0,true,false,KSpell::HTML);
05061 //  }
05062 //  else {
05063     mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this,
05064                       SLOT(slotSpellcheck2(KSpell*)));
05065 //  }
05066 
05067   QStringList l = KSpellingHighlighter::personalWords();
05068   for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
05069       mKSpell->addPersonal( *it );
05070   }
05071   connect (mKSpell, SIGNAL( death()),
05072           this, SLOT (slotSpellDone()));
05073   connect (mKSpell, SIGNAL (misspelling (const QString &, const QStringList &, unsigned int)),
05074           this, SLOT (slotMisspelling (const QString &, const QStringList &, unsigned int)));
05075   connect (mKSpell, SIGNAL (corrected (const QString &, const QString &, unsigned int)),
05076           this, SLOT (slotCorrected (const QString &, const QString &, unsigned int)));
05077   connect (mKSpell, SIGNAL (done(const QString &)),
05078           this, SLOT (slotSpellResult (const QString&)));
05079 }
05080 
05081 void KMEdit::cut()
05082 {
05083   KEdit::cut();
05084   if ( textFormat() != Qt::RichText )
05085     mSpellChecker->restartBackgroundSpellCheck();
05086 }
05087 
05088 void KMEdit::clear()
05089 {
05090   KEdit::clear();
05091   if ( textFormat() != Qt::RichText )
05092     mSpellChecker->restartBackgroundSpellCheck();
05093 }
05094 
05095 void KMEdit::del()
05096 {
05097   KEdit::del();
05098   if ( textFormat() != Qt::RichText )
05099     mSpellChecker->restartBackgroundSpellCheck();
05100 }
05101 
05102 
05103 void KMEdit::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos)
05104 {
05105     kdDebug(5006)<<"void KMEdit::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos) : "<<text <<endl;
05106     if( mSpellLineEdit )
05107         mComposer->sujectLineWidget()->spellCheckerMisspelling( text, lst, pos);
05108     else
05109         misspelling(text, lst, pos);
05110 }
05111 
05112 void KMEdit::slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos)
05113 {
05114     kdDebug(5006)<<"slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos) : "<<oldWord<<endl;
05115     if( mSpellLineEdit )
05116         mComposer->sujectLineWidget()->spellCheckerCorrected( oldWord, newWord, pos);
05117     else {
05118         unsigned int l = 0;
05119         unsigned int cnt = 0;
05120         bool _bold,_underline,_italic;
05121         QColor _color;
05122         QFont _font;
05123         posToRowCol (pos, l, cnt);
05124         setCursorPosition(l, cnt+1); // the new word will get the same markup now as the first character of the word
05125         _bold = bold();
05126         _underline = underline();
05127         _italic = italic();
05128         _color = color();
05129         _font = currentFont();
05130         corrected(oldWord, newWord, pos);
05131         setSelection (l, cnt, l, cnt+newWord.length());
05132         setBold(_bold);
05133         setItalic(_italic);
05134         setUnderline(_underline);
05135         setColor(_color);
05136         setCurrentFont(_font);
05137     }
05138 
05139 }
05140 
05141 //-----------------------------------------------------------------------------
05142 void KMEdit::slotSpellcheck2(KSpell*)
05143 {
05144     if( !mSpellLineEdit)
05145     {
05146         spellcheck_start();
05147 
05148         QString quotePrefix;
05149         if(mComposer && mComposer->msg())
05150         {
05151             // read the quote indicator from the preferences
05152             KConfig *config=KMKernel::config();
05153             KConfigGroupSaver saver(config, "General");
05154 
05155             int languageNr = config->readNumEntry("reply-current-language",0);
05156             config->setGroup( QString("KMMessage #%1").arg(languageNr) );
05157 
05158             quotePrefix = config->readEntry("indent-prefix", ">%_");
05159             quotePrefix = mComposer->msg()->formatString(quotePrefix);
05160         }
05161 
05162         kdDebug(5006) << "spelling: new SpellingFilter with prefix=\"" << quotePrefix << "\"" << endl;
05163         QTextEdit plaintext;
05164         plaintext.setText(text());
05165         plaintext.setTextFormat(Qt::PlainText);
05166         mSpellingFilter = new SpellingFilter(plaintext.text(), quotePrefix, SpellingFilter::FilterUrls,
05167                                              SpellingFilter::FilterEmailAddresses);
05168 
05169         mKSpell->check(mSpellingFilter->filteredText());
05170     }
05171     else if( mComposer )
05172         mKSpell->check( mComposer->sujectLineWidget()->text());
05173 }
05174 
05175 //-----------------------------------------------------------------------------
05176 void KMEdit::slotSpellResult(const QString &s)
05177 {
05178     if( !mSpellLineEdit)
05179         spellcheck_stop();
05180 
05181   int dlgResult = mKSpell->dlgResult();
05182   if ( dlgResult == KS_CANCEL )
05183   {
05184       if( mSpellLineEdit)
05185       {
05186           //stop spell check
05187           mSpellLineEdit = false;
05188           QString tmpText( s );
05189           tmpText =  tmpText.remove('\n');
05190 
05191           if( tmpText != mComposer->sujectLineWidget()->text() )
05192               mComposer->sujectLineWidget()->setText( tmpText );
05193       }
05194       else
05195       {
05196           setModified(true);
05197       }
05198   }
05199   mKSpell->cleanUp();
05200   KDictSpellingHighlighter::dictionaryChanged();
05201 
05202   emit spellcheck_done( dlgResult );
05203 }
05204 
05205 //-----------------------------------------------------------------------------
05206 void KMEdit::slotSpellDone()
05207 {
05208   kdDebug(5006)<<" void KMEdit::slotSpellDone()\n";
05209   KSpell::spellStatus status = mKSpell->status();
05210   delete mKSpell;
05211   mKSpell = 0;
05212 
05213   kdDebug(5006) << "spelling: delete SpellingFilter" << endl;
05214   delete mSpellingFilter;
05215   mSpellingFilter = 0;
05216   mComposer->sujectLineWidget()->deselect();
05217   if (status == KSpell::Error)
05218   {
05219      KMessageBox::sorry( topLevelWidget(),
05220                          i18n("ISpell/Aspell could not be started. Please "
05221                               "make sure you have ISpell or Aspell properly "
05222                               "configured and in your PATH.") );
05223      emit spellcheck_done( KS_CANCEL );
05224   }
05225   else if (status == KSpell::Crashed)
05226   {
05227      spellcheck_stop();
05228      KMessageBox::sorry( topLevelWidget(),
05229                          i18n("ISpell/Aspell seems to have crashed.") );
05230      emit spellcheck_done( KS_CANCEL );
05231   }
05232   else
05233   {
05234       if( mSpellLineEdit )
05235           spellcheck();
05236       else if( !mComposer->subjectTextWasSpellChecked() && status == KSpell::FinishedNoMisspellingsEncountered )
05237           KMessageBox::information( topLevelWidget(),
05238                                     i18n("No misspellings encountered.") );
05239   }
05240 }
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu May 3 20:23:08 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003