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