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