kmail Library API Documentation

kmcomposewin.cpp

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