kmail Library API Documentation

kmcomposewin.cpp

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