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