kmail Library API Documentation

kmcomposewin.cpp

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