00001
00002
00003
00004
00005
00006
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
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
00144
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
00180 mBtnReplyTo = new QPushButton("...",mMainWidget);
00181
00182
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
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
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
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
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
00363 case 2:
00364 slotSendLater();
00365 break;
00366 }
00367 }
00368
00369
00370 void KMComposeWin::addAttachment(KURL url,QString )
00371 {
00372 addAttach(url);
00373 }
00374
00375
00376 void KMComposeWin::addAttachment(const QString &name,
00377 const QCString &,
00378 const QByteArray &data,
00379 const QCString &type,
00380 const QCString &subType,
00381 const QCString ¶mAttr,
00382 const QString ¶mValue,
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
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
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 {
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 {
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 {
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
00633
00634
00635 applyChanges( true, true );
00636
00637
00638 qApp->enter_loop();
00639
00640
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
00652
00653
00654
00655
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;
00684
00685 int id;
00686
00687
00688
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
00723
00724
00725 if (!act->isChecked())
00726 {
00727
00728 if (id > 0) mShowHeaders = mShowHeaders & ~id;
00729 else mShowHeaders = abs(mShowHeaders);
00730 }
00731 else
00732 {
00733
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
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 );
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
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())
00925 {
00926
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
00935 {
00936
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
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
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
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
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
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
01111 const KPIM::Identity & ident =
01112 kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
01113
01114
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
01122 mAttachPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() );
01123
01124
01125
01126 mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
01127 !ident.pgpEncryptionKey().isEmpty() );
01128
01129 if ( !Kleo::CryptoBackendFactory::instance()->openpgp() && !Kleo::CryptoBackendFactory::instance()->smime() ) {
01130
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
01247 mEditor->setModified(FALSE);
01248 QFontMetrics fm(mBodyFont);
01249 mEditor->setTabStopWidth(fm.width(QChar(' ')) * 8);
01250
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
01263 slotUpdateFont();
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
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
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)
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
01377
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
01386 block->decrypt();
01387 else
01388
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
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
01422
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
01434
01435
01436 slotIdentityChanged( mId );
01437 }
01438
01439 KPIM::IdentityManager * im = kmkernel->identityManager();
01440
01441 const KPIM::Identity & ident = im->identityForUoid( mIdentity->currentIdentity() );
01442
01443
01444
01445 QString mdnAddr = newMsg->headerField("Disposition-Notification-To");
01446 mRequestMDNAction->setChecked( ( !mdnAddr.isEmpty() &&
01447 im->thatIsMe( mdnAddr ) ) || mAutoRequestMDN );
01448
01449
01450 mUrgentAction->setChecked( newMsg->isUrgent() );
01451
01452
01453 switch ( mMsg->encryptionState() ) {
01454 case KMMsgFullyEncrypted:
01455 case KMMsgPartiallyEncrypted:
01456 mLastEncryptActionState = true;
01457 break;
01458 case KMMsgNotEncrypted:
01459 mLastEncryptActionState = false;
01460 break;
01461 default:
01462 break;
01463 }
01464
01465
01466 switch ( mMsg->signatureState() ) {
01467 case KMMsgFullySigned:
01468 case KMMsgPartiallySigned:
01469 mLastSignActionState = true;
01470 break;
01471 case KMMsgNotSigned:
01472 mLastSignActionState = false;
01473 break;
01474 default:
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
01501
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
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
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
01554 kdDebug(5006) << "KMComposeWin::setMsg() : text/* found" << endl;
01555 firstAttachment = 1;
01556 }
01557 }
01558
01559 if ( firstAttachment != 0 )
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
01571
01572
01573
01574
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
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
01590
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
01617
01618
01619
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
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
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
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
01700
01701 QString subj = subject();
01702 gotMatch = ( KMMessage::stripOffPrefixes( subj ) == subj )
01703 && ( rx.search( subj ) >= 0 );
01704
01705 if ( !gotMatch ) {
01706
01707
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
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
01758 mComposer = new MessageComposer( this );
01759 connect( mComposer, SIGNAL( done( bool ) ),
01760 this, SLOT( slotComposerDone( bool ) ) );
01761
01762
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
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
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
02054
02055
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
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
02125
02126 if( mimeType == "text/html" )
02127 name = "index.html";
02128 else {
02129
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
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
02177 KConfigGroup composer(KMKernel::config(), "Composer");
02178 if (!composer.hasKey("showMessagePartDialogOnAttach"))
02179
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
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
02265 if( 0 == mAtmListView->columnWidth( mAtmColEncrypt ) ) {
02266
02267
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
02278 for( int col=0; col < mAtmColEncrypt; col++ )
02279 totalWidth += mAtmListView->columnWidth( col );
02280 int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
02281 - mAtmSignColWidth;
02282
02283
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
02292
02293
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
02306 if( 0 != mAtmListView->columnWidth( mAtmColEncrypt ) ) {
02307 mAtmEncryptColWidth = mAtmListView->columnWidth( mAtmColEncrypt );
02308 mAtmSignColWidth = mAtmListView->columnWidth( mAtmColSign );
02309 int totalWidth = 0;
02310
02311 for( int col=0; col < mAtmListView->columns(); col++ )
02312 totalWidth += mAtmListView->columnWidth( col );
02313 int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
02314 - mAtmSignColWidth;
02315
02316
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
02325
02326
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
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
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
02391 addAttach(msgPart);
02392 rethinkFields();
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 ,
02404 false ,
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
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
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 );
02808 }
02809
02810
02811
02812 void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
02813 {
02814 if ( !mEncryptAction->isEnabled() )
02815 encrypt = false;
02816
02817
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
02833 mEncryptAction->setChecked( encrypt );
02834
02835
02836 if ( encrypt )
02837 mEncryptAction->setIcon("encrypted");
02838 else
02839 mEncryptAction->setIcon("decrypted");
02840
02841
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 );
02856 }
02857
02858
02859
02860 void KMComposeWin::setSigning( bool sign, bool setByUser )
02861 {
02862 if ( !mSignAction->isEnabled() )
02863 sign = false;
02864
02865
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
02881 mSignAction->setChecked( sign );
02882
02883
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
02995
02996
02997
02998
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
03074 (*it)->cleanupHeader();
03075
03076
03077 (*it)->setComplete( true );
03078
03079 if (mSaveInDrafts) {
03080 KMFolder* draftsFolder = 0, *imapDraftsFolder = 0;
03081
03082 if ( !(*it)->drafts().isEmpty() ) {
03083 draftsFolder = kmkernel->folderMgr()->findIdString( (*it)->drafts() );
03084 if ( draftsFolder == 0 )
03085
03086
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
03116 draftsFolder->unGetMsg(draftsFolder->count() - 1);
03117 (*it) = draftsFolder->getMsg(draftsFolder->count() - 1);
03118
03119 if (imapDraftsFolder) {
03120
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;
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:
03182 doSend(TRUE);
03183 break;
03184 case KMessageBox::No:
03185 doSend(FALSE);
03186 break;
03187 case KMessageBox::Cancel:
03188 break;
03189 default:
03190 ;
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
03236 fontChanged( mEditor->currentFont().family() );
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;
03252
03253
03254 int paraFrom, indexFrom, paraTo, indexTo;
03255 mEditor->getSelection ( ¶From, &indexFrom, ¶To, &indexTo);
03256 mEditor->selectAll();
03257
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);
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);
03301 }
03302
03303 void KMComposeWin::slotSpellcheck()
03304 {
03305 if (mSpellCheckInProgress) return;
03306 mSubjectTextWasSpellChecked = false;
03307 mSpellCheckInProgress=TRUE;
03308
03309
03310
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();
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
03375 if ( from().isEmpty() )
03376 mShowHeaders |= HDR_FROM;
03377 mEdtReplyTo->setText(ident.replyToAddr());
03378
03379
03380 if( !mEdtBcc->edited() || !ident.bcc().isEmpty() )
03381 mEdtBcc->setText(ident.bcc());
03382
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
03429 if( !mOldSigText.isEmpty() )
03430 {
03431 if( edtText.endsWith( mOldSigText ) )
03432 edtText.truncate( edtText.length() - mOldSigText.length() );
03433 else
03434 appendNewSig = false;
03435 }
03436
03437 mOldSigText = ident.signatureText();
03438 if( appendNewSig )
03439 {
03440 if( !mOldSigText.isEmpty() && mAutoSign )
03441 edtText.append( mOldSigText );
03442 mEditor->setText( edtText );
03443 }
03444
03445
03446
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
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
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
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
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();
03542
03543
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
03558
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 );
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
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
03719 lineText.truncate( lineText.length() - 1 );
03720
03721
03722 if( ( col > 0 ) && ( col < int( lineText.length() ) ) ) {
03723 bool isQuotedLine = false;
03724 uint bot = 0;
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
03741
03742
03743 if( isQuotedLine
03744 && ( bot != lineText.length() )
03745 && ( col >= int( bot ) ) ) {
03746
03747
03748
03749 getCursorPosition( &line, &col );
03750 QString newLine = text( line );
03751
03752
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
03763
03764
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
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
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
03851
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
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
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);
03957 return;
03958 }
03959 if (e->key() == Key_Down)
03960 {
03961 mComposer->focusNextPrevEdit(this,TRUE);
03962 return;
03963 }
03964
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
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
04009 KABC::Addressee addr;
04010 KPIM::getNameAndMail(*it, name, email);
04011 addr.setNameFromString( name );
04012 addr.insertEmail( email, true );
04013 addContact( addr, 120 );
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
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;
04084 KConfigGroup readerConfig( KMKernel::config(), "Reader" );
04085 QColor defaultColor1( 0,0,221 );
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, true,
04097 false,
04098 misspelled,
04099 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 {
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);
04178 return TRUE;
04179 }
04180
04181
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
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
04219
04220 if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0
04221 && lineOfChar(0, currentColumn()) == 0)
04222 {
04223 deselect();
04224 mComposer->focusNextPrevEdit(0, false);
04225 return TRUE;
04226 }
04227
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
04243 charPos = charAt( viewportToContents(event->pos()), ¶ );
04244 QString paraText = text( para );
04245
04246 if( !paraText.at(charPos).isSpace() )
04247 {
04248
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
04256 if( !word.isEmpty() && mReplacements.contains( word ) )
04257 {
04258 KPopupMenu p;
04259 p.insertTitle( i18n("Suggestions") );
04260
04261
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
04277 int id = p.exec( mapToGlobal( event->pos() ) );
04278
04279 if( id > -1 )
04280 {
04281
04282 int parIdx = 1, txtIdx = 1;
04283 getCursorPosition(&parIdx, &txtIdx);
04284 setSelection(para, firstSpace, para, lastSpace);
04285 insert(mReplacements[word][id]);
04286
04287
04288 if ( para == parIdx && txtIdx >= lastSpace )
04289 txtIdx += mReplacements[word][id].length() - word.length();
04290 setCursorPosition(parIdx, txtIdx);
04291 }
04292
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
04307 if ( on )
04308 KMessageBox::sorry(this, i18n("Automatic spellchecking is not possible on text with markup."));
04309 return -1;
04310 }
04311
04312
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
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
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
04374
04375
04376
04377
04378
04379
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);
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
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
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 }