kmail Library API Documentation

kmcomposewin.cpp

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