kmail

kmcomposewin.cpp
1// kmcomposewin.cpp
2// Author: Markus Wuebben <markus.wuebben@kde.org>
3// This code is published under the GPL.
4
5#undef GrayScale
6#undef Color
7#include <config.h>
8
9#define REALLY_WANT_KMCOMPOSEWIN_H
10#include "kmcomposewin.h"
11#undef REALLY_WANT_KMCOMPOSEWIN_H
12
13#include "kmedit.h"
14#include "kmlineeditspell.h"
15#include "kmatmlistview.h"
16
17#include "kmmainwin.h"
18#include "kmreadermainwin.h"
19#include "messagesender.h"
20#include "kmmsgpartdlg.h"
21#include <kpgpblock.h>
22#include <kaddrbook.h>
23#include "kmaddrbook.h"
24#include "kmmsgdict.h"
25#include "kmfolderimap.h"
26#include "kmfoldermgr.h"
27#include "kmfoldercombobox.h"
28#include "kmtransport.h"
29#include "kmcommands.h"
30#include "kcursorsaver.h"
31#include "partNode.h"
32#include "encodingdetector.h"
33#include "attachmentlistview.h"
34#include "transportmanager.h"
35using KMail::AttachmentListView;
36#include "dictionarycombobox.h"
38#include "addressesdialog.h"
39using KPIM::AddressesDialog;
40#include "addresseeemailselection.h"
41using KPIM::AddresseeEmailSelection;
42using KPIM::AddresseeSelectorDialog;
43#include <maillistdrag.h>
44using KPIM::MailListDrag;
45#include "recentaddresses.h"
46using TDERecentAddress::RecentAddresses;
47#include "kleo_util.h"
48#include "stl_util.h"
49#include "recipientseditor.h"
50#include "editorwatcher.h"
51
52#include "attachmentcollector.h"
53#include "objecttreeparser.h"
54
55#include "kmfoldermaildir.h"
56
57#include <libkpimidentities/identitymanager.h>
58#include <libkpimidentities/identitycombo.h>
59#include <libkpimidentities/identity.h>
60#include <libtdepim/tdefileio.h>
61#include <libemailfunctions/email.h>
62#include <kleo/cryptobackendfactory.h>
63#include <kleo/exportjob.h>
64#include <kleo/specialjob.h>
65#include <ui/progressdialog.h>
66#include <ui/keyselectiondialog.h>
67
68#include <gpgmepp/context.h>
69#include <gpgmepp/key.h>
70
71#include <tdeio/netaccess.h>
72
73#include "tdelistboxdialog.h"
74
75#include "messagecomposer.h"
76#include "chiasmuskeyselector.h"
77
78#include <kcharsets.h>
79#include <tdecompletionbox.h>
80#include <kcursor.h>
81#include <kcombobox.h>
82#include <tdestdaccel.h>
83#include <tdepopupmenu.h>
84#include <kedittoolbar.h>
85#include <kkeydialog.h>
86#include <kdebug.h>
87#include <tdefiledialog.h>
88#include <twin.h>
89#include <kinputdialog.h>
90#include <tdemessagebox.h>
91#include <kurldrag.h>
92#include <tdeio/scheduler.h>
93#include <tdetempfile.h>
94#include <tdelocale.h>
95#include <tdeapplication.h>
96#include <kstatusbar.h>
97#include <tdeaction.h>
98#include <kstdaction.h>
99#include <kdirwatch.h>
100#include <kstdguiitem.h>
101#include <kiconloader.h>
102#include <kpushbutton.h>
103#include <kuserprofile.h>
104#include <krun.h>
105#include <ktempdir.h>
106#include <tdestandarddirs.h>
107//#include <keditlistbox.h>
108#include "globalsettings.h"
109#include "replyphrases.h"
110
111#include <tdespell.h>
112#include <tdespelldlg.h>
113#include <spellingfilter.h>
114#include <ksyntaxhighlighter.h>
115#include <kcolordialog.h>
116#include <kzip.h>
117#include <ksavefile.h>
118
119#include <tqtabdialog.h>
120#include <tqregexp.h>
121#include <tqbuffer.h>
122#include <tqtooltip.h>
123#include <tqtextcodec.h>
124#include <tqheader.h>
125#include <tqwhatsthis.h>
126#include <tqfontdatabase.h>
127
128#include <mimelib/mimepp.h>
129
130#include <algorithm>
131#include <memory>
132
133#include <sys/stat.h>
134#include <sys/types.h>
135#include <stdlib.h>
136#include <unistd.h>
137#include <errno.h>
138#include <fcntl.h>
139#include <assert.h>
140
141#include "kmcomposewin.moc"
142
143#include "snippetwidget.h"
144
145KMail::Composer * KMail::makeComposer( KMMessage * msg, uint identitiy ) {
146 return KMComposeWin::create( msg, identitiy );
147}
148
149KMail::Composer * KMComposeWin::create( KMMessage * msg, uint identitiy ) {
150 return new KMComposeWin( msg, identitiy );
151}
152
153//-----------------------------------------------------------------------------
154KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
155 : MailComposerIface(), KMail::Composer( "kmail-composer#" ),
156 mSpellCheckInProgress( false ),
157 mDone( false ),
158 mAtmModified( false ),
159 mAtmSelectNew( 0 ),
160 mMsg( 0 ),
161 mAttachMenu( 0 ),
162 mSigningAndEncryptionExplicitlyDisabled( false ),
163 mFolder( 0 ),
164 mUseHTMLEditor( false ),
165 mId( id ),
166 mAttachPK( 0 ), mAttachMPK( 0 ),
167 mAttachRemoveAction( 0 ), mAttachSaveAction( 0 ), mAttachPropertiesAction( 0 ),
168 mAppendSignatureAction( 0 ), mPrependSignatureAction( 0 ), mInsertSignatureAction( 0 ),
169 mSignAction( 0 ), mEncryptAction( 0 ), mRequestMDNAction( 0 ),
170 mUrgentAction( 0 ), mAllFieldsAction( 0 ), mFromAction( 0 ),
171 mReplyToAction( 0 ), mToAction( 0 ), mCcAction( 0 ), mBccAction( 0 ),
172 mSubjectAction( 0 ),
173 mIdentityAction( 0 ), mTransportAction( 0 ), mFccAction( 0 ),
174 mWordWrapAction( 0 ), mFixedFontAction( 0 ), mAutoSpellCheckingAction( 0 ),
175 mDictionaryAction( 0 ), mSnippetAction( 0 ),
176 mEncodingAction( 0 ),
177 mCryptoModuleAction( 0 ),
178 mEncryptChiasmusAction( 0 ),
179 mEncryptWithChiasmus( false ),
180 mComposer( 0 ),
181 mLabelWidth( 0 ),
182 mAutoSaveTimer( 0 ), mLastAutoSaveErrno( 0 ),
183 mSignatureStateIndicator( 0 ), mEncryptionStateIndicator( 0 ),
184 mPreserveUserCursorPosition( false ),
185 mPreventFccOverwrite( false ),
186 mCheckForRecipients( true ),
187 mCheckForForgottenAttachments( true ),
188 mIgnoreStickyFields( false )
189{
190 mClassicalRecipients = GlobalSettings::self()->recipientsEditorType() ==
191 GlobalSettings::EnumRecipientsEditorType::Classic;
192
193 mSubjectTextWasSpellChecked = false;
194 if (kmkernel->xmlGuiInstance())
195 setInstance( kmkernel->xmlGuiInstance() );
196 mMainWidget = new TQWidget(this);
197 // splitter between the headers area and the actual editor
198 mHeadersToEditorSplitter = new TQSplitter( TQt::Vertical, mMainWidget, "mHeadersToEditorSplitter" );
199 mHeadersToEditorSplitter->setChildrenCollapsible( false );
200 mHeadersArea = new TQWidget( mHeadersToEditorSplitter );
201 mHeadersArea->setSizePolicy( mHeadersToEditorSplitter->sizePolicy().horData(), TQSizePolicy::Maximum );
202 TQVBoxLayout *v = new TQVBoxLayout( mMainWidget );
203 v->addWidget( mHeadersToEditorSplitter );
204 mIdentity = new KPIM::IdentityCombo(kmkernel->identityManager(), mHeadersArea);
205 TQToolTip::add( mIdentity,
206 i18n( "Select an identity for this message" ) );
207
208 mDictionaryCombo = new DictionaryComboBox( mHeadersArea );
209 TQToolTip::add( mDictionaryCombo,
210 i18n( "Select the dictionary to use when spell-checking this message" ) );
211
212 mFcc = new KMFolderComboBox(mHeadersArea);
213 mFcc->showOutboxFolder( false );
214 TQToolTip::add( mFcc,
215 i18n( "Select the sent-mail folder where a copy of this message will be saved" ) );
216
217 mTransport = new TQComboBox(true, mHeadersArea);
218 TQToolTip::add( mTransport,
219 i18n( "Select the outgoing account to use for sending this message" ) );
220
221 mEdtFrom = new KMLineEdit(false,mHeadersArea, "fromLine");
222 TQToolTip::add( mEdtFrom,
223 i18n( "Set the \"From:\" email address for this message" ) );
224
225 mEdtReplyTo = new KMLineEdit(true,mHeadersArea, "replyToLine");
226 TQToolTip::add( mEdtReplyTo,
227 i18n( "Set the \"Reply-To:\" email address for this message" ) );
228 connect(mEdtReplyTo,TQ_SIGNAL(completionModeChanged(TDEGlobalSettings::Completion)),
229 TQ_SLOT(slotCompletionModeChanged(TDEGlobalSettings::Completion)));
230
231 if ( mClassicalRecipients ) {
232 mRecipientsEditor = 0;
233
234 mEdtTo = new KMLineEdit(true,mHeadersArea, "toLine");
235 mEdtCc = new KMLineEdit(true,mHeadersArea, "ccLine");
236 mEdtBcc = new KMLineEdit(true,mHeadersArea, "bccLine");
237
238 mLblTo = new TQLabel(mHeadersArea);
239 mLblCc = new TQLabel(mHeadersArea);
240 mLblBcc = new TQLabel(mHeadersArea);
241
242 mBtnTo = new TQPushButton("...",mHeadersArea);
243 mBtnCc = new TQPushButton("...",mHeadersArea);
244 mBtnBcc = new TQPushButton("...",mHeadersArea);
245 //mBtnFrom = new TQPushButton("...",mHeadersArea);
246
247 TQString tip = i18n("Select email address(es)");
248 TQToolTip::add( mBtnTo, tip );
249 TQToolTip::add( mBtnCc, tip );
250 TQToolTip::add( mBtnBcc, tip );
251
252 mBtnTo->setFocusPolicy(TQWidget::NoFocus);
253 mBtnCc->setFocusPolicy(TQWidget::NoFocus);
254 mBtnBcc->setFocusPolicy(TQWidget::NoFocus);
255 //mBtnFrom->setFocusPolicy(TQWidget::NoFocus);
256
257 connect(mBtnTo,TQ_SIGNAL(clicked()),TQ_SLOT(slotAddrBookTo()));
258 connect(mBtnCc,TQ_SIGNAL(clicked()),TQ_SLOT(slotAddrBookTo()));
259 connect(mBtnBcc,TQ_SIGNAL(clicked()),TQ_SLOT(slotAddrBookTo()));
260 //connect(mBtnFrom,TQ_SIGNAL(clicked()),TQ_SLOT(slotAddrBookFrom()));
261
262 connect(mEdtTo,TQ_SIGNAL(completionModeChanged(TDEGlobalSettings::Completion)),
263 TQ_SLOT(slotCompletionModeChanged(TDEGlobalSettings::Completion)));
264 connect(mEdtCc,TQ_SIGNAL(completionModeChanged(TDEGlobalSettings::Completion)),
265 TQ_SLOT(slotCompletionModeChanged(TDEGlobalSettings::Completion)));
266 connect(mEdtBcc,TQ_SIGNAL(completionModeChanged(TDEGlobalSettings::Completion)),
267 TQ_SLOT(slotCompletionModeChanged(TDEGlobalSettings::Completion)));
268
269 mEdtTo->setFocus();
270 } else {
271 mEdtTo = 0;
272 mEdtCc = 0;
273 mEdtBcc = 0;
274
275 mLblTo = 0;
276 mLblCc = 0;
277 mLblBcc = 0;
278
279 mBtnTo = 0;
280 mBtnCc = 0;
281 mBtnBcc = 0;
282 //mBtnFrom = 0;
283
284 mRecipientsEditor = new RecipientsEditor( mHeadersArea );
285 connect( mRecipientsEditor,
286 TQ_SIGNAL( completionModeChanged( TDEGlobalSettings::Completion ) ),
287 TQ_SLOT( slotCompletionModeChanged( TDEGlobalSettings::Completion ) ) );
288 connect( mRecipientsEditor, TQ_SIGNAL(sizeHintChanged()), TQ_SLOT(recipientEditorSizeHintChanged()) );
289
290 mRecipientsEditor->setFocus();
291 }
292 mEdtSubject = new KMLineEditSpell(false,mHeadersArea, "subjectLine");
293 TQToolTip::add( mEdtSubject,
294 i18n( "Set a subject for this message" ) );
295
296 mLblIdentity = new TQLabel( i18n("&Identity:"), mHeadersArea );
297 mDictionaryLabel = new TQLabel( i18n("&Dictionary:"), mHeadersArea );
298 mLblFcc = new TQLabel( i18n("&Sent-Mail folder:"), mHeadersArea );
299 mLblTransport = new TQLabel( i18n("&Mail transport:"), mHeadersArea );
300 mLblFrom = new TQLabel( i18n("sender address field", "&From:"), mHeadersArea );
301 mLblReplyTo = new TQLabel( i18n("&Reply to:"), mHeadersArea );
302 mLblSubject = new TQLabel( i18n("S&ubject:"), mHeadersArea );
303
304 TQString sticky = i18n("Sticky");
305 mBtnIdentity = new TQCheckBox(sticky,mHeadersArea);
306 TQToolTip::add( mBtnIdentity,
307 i18n( "Use the selected value as your identity for future messages" ) );
308 mBtnFcc = new TQCheckBox(sticky,mHeadersArea);
309 TQToolTip::add( mBtnFcc,
310 i18n( "Use the selected value as your sent-mail folder for future messages" ) );
311 mBtnTransport = new TQCheckBox(sticky,mHeadersArea);
312 TQToolTip::add( mBtnTransport,
313 i18n( "Use the selected value as your outgoing account for future messages" ) );
314 mBtnDictionary = new TQCheckBox( sticky, mHeadersArea );
315 TQToolTip::add( mBtnDictionary,
316 i18n( "Use the selected value as your dictionary for future messages" ) );
317
318 //setWFlags( WType_TopLevel | WStyle_Dialog );
319 mHtmlMarkup = GlobalSettings::self()->useHtmlMarkup();
320 mShowHeaders = GlobalSettings::self()->headers();
321 mDone = false;
322 mGrid = 0;
323 mAtmListView = 0;
324 mAtmList.setAutoDelete(true);
325 mAtmTempList.setAutoDelete(true);
326 mAtmModified = false;
327 mAutoDeleteMsg = false;
328 mFolder = 0;
329 mAutoCharset = true;
330 mFixedFontAction = 0;
331 mTempDir = 0;
332 // the attachment view is separated from the editor by a splitter
333 mSplitter = new TQSplitter( TQt::Vertical, mHeadersToEditorSplitter, "mSplitter" );
334 mSplitter->setChildrenCollapsible( false );
335 mSnippetSplitter = new TQSplitter( TQt::Horizontal, mSplitter, "mSnippetSplitter");
336 mSnippetSplitter->setChildrenCollapsible( false );
337
338 TQWidget *editorAndCryptoStateIndicators = new TQWidget( mSnippetSplitter );
339 TQVBoxLayout *vbox = new TQVBoxLayout( editorAndCryptoStateIndicators );
340 TQHBoxLayout *hbox = new TQHBoxLayout( vbox );
341 {
342 mSignatureStateIndicator = new TQLabel( editorAndCryptoStateIndicators );
343 mSignatureStateIndicator->setAlignment( TQt::AlignHCenter );
344 hbox->addWidget( mSignatureStateIndicator );
345
346 TDEConfigGroup reader( KMKernel::config(), "Reader" );
347 TQPalette p( mSignatureStateIndicator->palette() );
348
349 TQColor defaultSignedColor( 0x40, 0xFF, 0x40 ); // light green // pgp ok, trusted key
350 TQColor defaultEncryptedColor( 0x00, 0x80, 0xFF ); // light blue // pgp encrypted
351 p.setColor( TQColorGroup::Background, reader.readColorEntry( "PGPMessageOkKeyOk", &defaultSignedColor ) );
352 mSignatureStateIndicator->setPalette( p );
353
354 mEncryptionStateIndicator = new TQLabel( editorAndCryptoStateIndicators );
355 mEncryptionStateIndicator->setAlignment( TQt::AlignHCenter );
356 hbox->addWidget( mEncryptionStateIndicator );
357 p.setColor( TQColorGroup::Background, reader.readColorEntry( "PGPMessageEncr" , &defaultEncryptedColor ) );
358 mEncryptionStateIndicator->setPalette( p );
359 }
360
361 mEditor = new KMEdit( editorAndCryptoStateIndicators, this, mDictionaryCombo->spellConfig() );
362 vbox->addWidget( mEditor );
363
364 mSnippetWidget = new SnippetWidget( mEditor, actionCollection(), mSnippetSplitter );
365 mSnippetWidget->setShown( GlobalSettings::self()->showSnippetManager() );
366
367 // mSplitter->moveToFirst( editorAndCryptoStateIndicators );
368 mSplitter->setOpaqueResize( true );
369
370 mEditor->initializeAutoSpellChecking();
371 mEditor->setTextFormat(TQt::PlainText);
372 mEditor->setAcceptDrops( true );
373
374 TQWhatsThis::add( mBtnIdentity,
375 GlobalSettings::self()->stickyIdentityItem()->whatsThis() );
376 TQWhatsThis::add( mBtnFcc,
377 GlobalSettings::self()->stickyFccItem()->whatsThis() );
378 TQWhatsThis::add( mBtnTransport,
379 GlobalSettings::self()->stickyTransportItem()->whatsThis() );
380 TQWhatsThis::add( mBtnTransport,
381 GlobalSettings::self()->stickyDictionaryItem()->whatsThis() );
382
383 mSpellCheckInProgress=false;
384
385 setCaption( i18n("Composer") );
386 setMinimumSize(200,200);
387
388 mBtnIdentity->setFocusPolicy(TQWidget::NoFocus);
389 mBtnFcc->setFocusPolicy(TQWidget::NoFocus);
390 mBtnTransport->setFocusPolicy(TQWidget::NoFocus);
391 mBtnDictionary->setFocusPolicy( TQWidget::NoFocus );
392
393 mAtmListView = new AttachmentListView( this, mSplitter,
394 "attachment list view" );
395 mAtmListView->setSelectionMode( TQListView::Extended );
396 mAtmListView->addColumn( i18n("Name"), 200 );
397 mAtmListView->addColumn( i18n("Size"), 80 );
398 mAtmListView->addColumn( i18n("Encoding"), 120 );
399 int atmColType = mAtmListView->addColumn( i18n("Type"), 120 );
400 // Stretch "Type".
401 mAtmListView->header()->setStretchEnabled( true, atmColType );
402 mAtmEncryptColWidth = 80;
403 mAtmSignColWidth = 80;
404 mAtmCompressColWidth = 100;
405 mAtmColCompress = mAtmListView->addColumn( i18n("Compress"),
406 mAtmCompressColWidth );
407 mAtmColEncrypt = mAtmListView->addColumn( i18n("Encrypt"),
408 mAtmEncryptColWidth );
409 mAtmColSign = mAtmListView->addColumn( i18n("Sign"),
410 mAtmSignColWidth );
411 mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
412 mAtmListView->setColumnWidth( mAtmColSign, 0 );
413 mAtmListView->setAllColumnsShowFocus( true );
414
415 connect( mAtmListView,
416 TQ_SIGNAL( doubleClicked( TQListViewItem* ) ),
417 TQ_SLOT( slotAttachEdit() ) );
418 connect( mAtmListView,
419 TQ_SIGNAL( rightButtonPressed( TQListViewItem*, const TQPoint&, int ) ),
420 TQ_SLOT( slotAttachPopupMenu( TQListViewItem*, const TQPoint&, int ) ) );
421 connect( mAtmListView,
422 TQ_SIGNAL( selectionChanged() ),
423 TQ_SLOT( slotUpdateAttachActions() ) );
424 connect( mAtmListView,
425 TQ_SIGNAL( attachmentDeleted() ),
426 TQ_SLOT( slotAttachRemove() ) );
427 connect( mAtmListView,
428 TQ_SIGNAL( dragStarted() ),
429 TQ_SLOT( slotAttachmentDragStarted() ) );
430 mAttachMenu = 0;
431
432 readConfig();
433 setupStatusBar();
434 setupActions();
435 setupEditor();
436 slotUpdateSignatureAndEncrypionStateIndicators();
437
438 applyMainWindowSettings(KMKernel::config(), "Composer");
439
440 connect( mEdtSubject, TQ_SIGNAL( subjectTextSpellChecked() ),
441 TQ_SLOT( slotSubjectTextSpellChecked() ) );
442 connect(mEdtSubject,TQ_SIGNAL(textChanged(const TQString&)),
443 TQ_SLOT(slotUpdWinTitle(const TQString&)));
444 connect(mIdentity,TQ_SIGNAL(identityChanged(uint)),
445 TQ_SLOT(slotIdentityChanged(uint)));
446 connect( kmkernel->identityManager(), TQ_SIGNAL(changed(uint)),
447 TQ_SLOT(slotIdentityChanged(uint)));
448
449 connect(mEdtFrom,TQ_SIGNAL(completionModeChanged(TDEGlobalSettings::Completion)),
450 TQ_SLOT(slotCompletionModeChanged(TDEGlobalSettings::Completion)));
451 connect(kmkernel->folderMgr(),TQ_SIGNAL(folderRemoved(KMFolder*)),
452 TQ_SLOT(slotFolderRemoved(KMFolder*)));
453 connect(kmkernel->imapFolderMgr(),TQ_SIGNAL(folderRemoved(KMFolder*)),
454 TQ_SLOT(slotFolderRemoved(KMFolder*)));
455 connect(kmkernel->dimapFolderMgr(),TQ_SIGNAL(folderRemoved(KMFolder*)),
456 TQ_SLOT(slotFolderRemoved(KMFolder*)));
457 connect( kmkernel, TQ_SIGNAL( configChanged() ),
458 this, TQ_SLOT( slotConfigChanged() ) );
459
460 connect (mEditor, TQ_SIGNAL (spellcheck_done(int)),
461 this, TQ_SLOT (slotSpellcheckDone (int)));
462 connect (mEditor, TQ_SIGNAL( attachPNGImageData(const TQByteArray &) ),
463 this, TQ_SLOT ( slotAttachPNGImageData(const TQByteArray &) ) );
464 connect (mEditor, TQ_SIGNAL( focusChanged(bool) ),
465 this, TQ_SLOT (editorFocusChanged(bool)) );
466
467 mMainWidget->resize(480,510);
468 setCentralWidget(mMainWidget);
469 rethinkFields();
470
471 if ( !mClassicalRecipients ) {
472 // This is ugly, but if it isn't called the line edits in the recipients
473 // editor aren't wide enough until the first resize event comes.
474 rethinkFields();
475 }
476
477 if ( GlobalSettings::self()->useExternalEditor() ) {
478 mEditor->setUseExternalEditor(true);
479 mEditor->setExternalEditorPath( GlobalSettings::self()->externalEditor() );
480 }
481
482 initAutoSave();
483 slotUpdateSignatureActions();
484 mMsg = 0;
485 if (aMsg)
486 setMsg(aMsg);
487 fontChanged( mEditor->currentFont() ); // set toolbar buttons to correct values
488
489 mDone = true;
490}
491
492//-----------------------------------------------------------------------------
493KMComposeWin::~KMComposeWin()
494{
495 writeConfig();
496 if (mFolder && mMsg)
497 {
498 mAutoDeleteMsg = false;
499 mFolder->addMsg(mMsg);
500 // Ensure that the message is correctly and fully parsed
501 mFolder->unGetMsg( mFolder->count() - 1 );
502 }
503 if (mAutoDeleteMsg) {
504 delete mMsg;
505 mMsg = 0;
506 }
507 TQMap<TDEIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.begin();
508 while ( it != mMapAtmLoadData.end() )
509 {
510 TDEIO::Job *job = it.key();
511 mMapAtmLoadData.remove( it );
512 job->kill();
513 it = mMapAtmLoadData.begin();
514 }
515 deleteAll( mComposedMessages );
516
517 for ( std::set<KTempDir*>::iterator it = mTempDirs.begin() ; it != mTempDirs.end() ; ++it ) {
518 delete *it;
519 }
520}
521
522void KMComposeWin::setAutoDeleteWindow( bool f )
523{
524 if ( f )
525 setWFlags( getWFlags() | WDestructiveClose );
526 else
527 setWFlags( getWFlags() & ~WDestructiveClose );
528}
529
530//-----------------------------------------------------------------------------
531void KMComposeWin::send(int how)
532{
533 switch (how) {
534 case 1:
535 slotSendNow();
536 break;
537 default:
538 case 0:
539 // TODO: find out, what the default send method is and send it this way
540 case 2:
541 slotSendLater();
542 break;
543 }
544}
545
546//-----------------------------------------------------------------------------
547void KMComposeWin::addAttachmentsAndSend(const KURL::List &urls, const TQString &/*comment*/, int how)
548{
549 if (urls.isEmpty())
550 {
551 send(how);
552 return;
553 }
554 mAttachFilesSend = how;
555 mAttachFilesPending = urls;
556 connect(this, TQ_SIGNAL(attachmentAdded(const KURL&, bool)), TQ_SLOT(slotAttachedFile(const KURL&)));
557 for( KURL::List::ConstIterator itr = urls.begin(); itr != urls.end(); ++itr ) {
558 if (!addAttach( *itr ))
559 mAttachFilesPending.remove(mAttachFilesPending.find(*itr)); // only remove one copy of the url
560 }
561
562 if (mAttachFilesPending.isEmpty() && mAttachFilesSend == how)
563 {
564 send(mAttachFilesSend);
565 mAttachFilesSend = -1;
566 }
567}
568
569void KMComposeWin::slotAttachedFile(const KURL &url)
570{
571 if (mAttachFilesPending.isEmpty())
572 return;
573 mAttachFilesPending.remove(mAttachFilesPending.find(url)); // only remove one copy of url
574 if (mAttachFilesPending.isEmpty())
575 {
576 send(mAttachFilesSend);
577 mAttachFilesSend = -1;
578 }
579}
580
581//-----------------------------------------------------------------------------
582void KMComposeWin::addAttachment(KURL url,TQString /*comment*/)
583{
584 addAttach(url);
585}
586
587//-----------------------------------------------------------------------------
588void KMComposeWin::addAttachment(const TQString &name,
589 const TQCString &/*cte*/,
590 const TQByteArray &data,
591 const TQCString &type,
592 const TQCString &subType,
593 const TQCString &paramAttr,
594 const TQString &paramValue,
595 const TQCString &contDisp)
596{
597 if (!data.isEmpty()) {
598 KMMessagePart *msgPart = new KMMessagePart;
599 msgPart->setName(name);
600 if( type == "message" && subType == "rfc822" ) {
601 msgPart->setMessageBody( data );
602 } else {
603 TQValueList<int> dummy;
604 msgPart->setBodyAndGuessCte(data, dummy,
605 kmkernel->msgSender()->sendQuotedPrintable());
606 }
607 msgPart->setTypeStr(type);
608 msgPart->setSubtypeStr(subType);
609 msgPart->setParameter(paramAttr,paramValue);
610 msgPart->setContentDisposition(contDisp);
611 addAttach(msgPart);
612 }
613}
614
615//-----------------------------------------------------------------------------
616void KMComposeWin::slotAttachPNGImageData(const TQByteArray &image)
617{
618 bool ok;
619
620 TQString attName = KInputDialog::getText( "KMail", i18n("Name of the attachment:"), TQString(), &ok, this );
621 if ( !ok )
622 return;
623
624 if ( !attName.lower().endsWith(".png") ) attName += ".png";
625
626 addAttachment( attName, "base64", image, "image", "png", TQCString(), TQString(), TQCString() );
627}
628
629//-----------------------------------------------------------------------------
630void KMComposeWin::setBody(TQString body)
631{
632 mEditor->setText(body);
633}
634
635//-----------------------------------------------------------------------------
636bool KMComposeWin::event(TQEvent *e)
637{
638 if (e->type() == TQEvent::ApplicationPaletteChange)
639 {
640 readColorConfig();
641 }
642 return KMail::Composer::event(e);
643}
644
645
646//-----------------------------------------------------------------------------
647void KMComposeWin::readColorConfig(void)
648{
649 if ( GlobalSettings::self()->useDefaultColors() ) {
650 mForeColor = TQColor(tdeApp->palette().active().text());
651 mBackColor = TQColor(tdeApp->palette().active().base());
652 } else {
653 mForeColor = GlobalSettings::self()->foregroundColor();
654 mBackColor = GlobalSettings::self()->backgroundColor();
655 }
656
657 // Color setup
658 mPalette = tdeApp->palette();
659 TQColorGroup cgrp = mPalette.active();
660 cgrp.setColor( TQColorGroup::Base, mBackColor);
661 cgrp.setColor( TQColorGroup::Text, mForeColor);
662 mPalette.setDisabled(cgrp);
663 mPalette.setActive(cgrp);
664 mPalette.setInactive(cgrp);
665
666 mEdtFrom->setPalette(mPalette);
667 mEdtReplyTo->setPalette(mPalette);
668 if ( mClassicalRecipients ) {
669 mEdtTo->setPalette(mPalette);
670 mEdtCc->setPalette(mPalette);
671 mEdtBcc->setPalette(mPalette);
672 }
673 mEdtSubject->setPalette(mPalette);
674 mTransport->setPalette(mPalette);
675 mEditor->setPalette(mPalette);
676 mFcc->setPalette(mPalette);
677}
678
679//-----------------------------------------------------------------------------
680void KMComposeWin::readConfig( bool reload /* = false */ )
681{
682 mDefCharset = KMMessage::defaultCharset();
683 mBtnIdentity->setChecked( GlobalSettings::self()->stickyIdentity() );
684 if (mBtnIdentity->isChecked()) {
685 mId = (GlobalSettings::self()->previousIdentity()!=0) ?
686 GlobalSettings::self()->previousIdentity() : mId;
687 }
688 mBtnFcc->setChecked( GlobalSettings::self()->stickyFcc() );
689 mBtnTransport->setChecked( GlobalSettings::self()->stickyTransport() );
690 mBtnDictionary->setChecked( GlobalSettings::self()->stickyDictionary() );
691 TQStringList transportHistory = GlobalSettings::self()->transportHistory();
692 TQString currentTransport = GlobalSettings::self()->currentTransport();
693
694 mEdtFrom->setCompletionMode( (TDEGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
695 mEdtReplyTo->setCompletionMode( (TDEGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
696 if ( mClassicalRecipients ) {
697 mEdtTo->setCompletionMode( (TDEGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
698 mEdtCc->setCompletionMode( (TDEGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
699 mEdtBcc->setCompletionMode( (TDEGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
700 }
701 else
702 mRecipientsEditor->setCompletionMode( (TDEGlobalSettings::Completion)GlobalSettings::self()->completionMode() );
703
704 readColorConfig();
705
706 if ( GlobalSettings::self()->useDefaultFonts() ) {
707 mBodyFont = TDEGlobalSettings::generalFont();
708 mFixedFont = TDEGlobalSettings::fixedFont();
709 } else {
710 mBodyFont = GlobalSettings::self()->composerFont();
711 mFixedFont = GlobalSettings::self()->fixedFont();
712 }
713
714 slotUpdateFont();
715 mEdtFrom->setFont(mBodyFont);
716 mEdtReplyTo->setFont(mBodyFont);
717 if ( mClassicalRecipients ) {
718 mEdtTo->setFont(mBodyFont);
719 mEdtCc->setFont(mBodyFont);
720 mEdtBcc->setFont(mBodyFont);
721 }
722 mEdtSubject->setFont(mBodyFont);
723
724 if ( !reload ) {
725 TQSize siz = GlobalSettings::self()->composerSize();
726 if (siz.width() < 200) siz.setWidth(200);
727 if (siz.height() < 200) siz.setHeight(200);
728 resize(siz);
729
730 if ( !GlobalSettings::self()->snippetSplitterPosition().isEmpty() ) {
731 mSnippetSplitter->setSizes( GlobalSettings::self()->snippetSplitterPosition() );
732 } else {
733 TQValueList<int> defaults;
734 defaults << (int)(width() * 0.8) << (int)(width() * 0.2);
735 mSnippetSplitter->setSizes( defaults );
736 }
737 }
738
739 mIdentity->setCurrentIdentity( mId );
740
741 kdDebug(5006) << "KMComposeWin::readConfig. " << mIdentity->currentIdentityName() << endl;
742 const KPIM::Identity & ident =
743 kmkernel->identityManager()->identityForUoid( mIdentity->currentIdentity() );
744
745 mTransport->clear();
746 mTransport->insertStringList( KMTransportInfo::availableTransports() );
747 while ( transportHistory.count() > (uint)GlobalSettings::self()->maxTransportEntries() )
748 transportHistory.remove( transportHistory.last() );
749 mTransport->insertStringList( transportHistory );
750 mTransport->setCurrentText( GlobalSettings::self()->defaultTransport() );
751 if ( mBtnTransport->isChecked() ) {
752 setTransport( currentTransport );
753 }
754
755 if ( mBtnDictionary->isChecked() ) {
756 mDictionaryCombo->setCurrentByDictionaryName( GlobalSettings::self()->previousDictionary() );
757 } else {
758 mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
759 }
760
761 TQString fccName = "";
762 if ( mBtnFcc->isChecked() ) {
763 fccName = GlobalSettings::self()->previousFcc();
764 } else if ( !ident.fcc().isEmpty() ) {
765 fccName = ident.fcc();
766 }
767
768 setFcc( fccName );
769}
770
771//-----------------------------------------------------------------------------
772void KMComposeWin::writeConfig(void)
773{
774 GlobalSettings::self()->setHeaders( mShowHeaders );
775 GlobalSettings::self()->setStickyFcc( mBtnFcc->isChecked() );
776 if ( !mIgnoreStickyFields ) {
777 GlobalSettings::self()->setCurrentTransport( mTransport->currentText() );
778 GlobalSettings::self()->setStickyTransport( mBtnTransport->isChecked() );
779 GlobalSettings::self()->setStickyDictionary( mBtnDictionary->isChecked() );
780 GlobalSettings::self()->setStickyIdentity( mBtnIdentity->isChecked() );
781 GlobalSettings::self()->setPreviousIdentity( mIdentity->currentIdentity() );
782 }
783 GlobalSettings::self()->setPreviousFcc( mFcc->getFolder()->idString() );
784 GlobalSettings::self()->setPreviousDictionary( mDictionaryCombo->currentDictionaryName() );
785 GlobalSettings::self()->setAutoSpellChecking(
786 mAutoSpellCheckingAction->isChecked() );
787 TQStringList transportHistory = GlobalSettings::self()->transportHistory();
788 transportHistory.remove(mTransport->currentText());
789 if (KMTransportInfo::availableTransports().findIndex(mTransport
790 ->currentText()) == -1) {
791 transportHistory.prepend(mTransport->currentText());
792 }
793 GlobalSettings::self()->setTransportHistory( transportHistory );
794 GlobalSettings::self()->setUseFixedFont( mFixedFontAction->isChecked() );
795 GlobalSettings::self()->setUseHtmlMarkup( mHtmlMarkup );
796 GlobalSettings::self()->setComposerSize( size() );
797 GlobalSettings::self()->setShowSnippetManager( mSnippetAction->isChecked() );
798
799 TDEConfigGroupSaver saver( KMKernel::config(), "Geometry" );
800 saveMainWindowSettings( KMKernel::config(), "Composer" );
801 GlobalSettings::setSnippetSplitterPosition( mSnippetSplitter->sizes() );
802
803 // make sure config changes are written to disk, cf. bug 127538
804 GlobalSettings::self()->writeConfig();
805}
806
807//-----------------------------------------------------------------------------
808void KMComposeWin::autoSaveMessage()
809{
810 kdDebug(5006) << k_funcinfo << endl;
811 if ( !mMsg || mComposer || mAutoSaveFilename.isEmpty() )
812 return;
813 kdDebug(5006) << k_funcinfo << "autosaving message" << endl;
814
815 if ( mAutoSaveTimer )
816 mAutoSaveTimer->stop();
817
818 connect( this, TQ_SIGNAL( applyChangesDone( bool ) ),
819 this, TQ_SLOT( slotContinueAutoSave() ) );
820 // This method is called when KMail crashed, so don't try signing/encryption
821 // and don't disable controls because it is also called from a timer and
822 // then the disabling is distracting.
823 applyChanges( true, true );
824
825 // Don't continue before the applyChanges is done!
826}
827
828void KMComposeWin::slotContinueAutoSave()
829{
830 disconnect( this, TQ_SIGNAL( applyChangesDone( bool ) ),
831 this, TQ_SLOT( slotContinueAutoSave() ) );
832
833 // Ok, it's done now - continue dead letter saving
834 if ( mComposedMessages.isEmpty() ) {
835 kdDebug(5006) << "Composing the message failed." << endl;
836 return;
837 }
838 KMMessage *msg = mComposedMessages.first();
839 if ( !msg ) // a bit of extra defensiveness
840 return;
841
842 kdDebug(5006) << k_funcinfo << "opening autoSaveFile " << mAutoSaveFilename
843 << endl;
844 const TQString filename =
845 KMKernel::localDataPath() + "autosave/cur/" + mAutoSaveFilename;
846 KSaveFile autoSaveFile( filename, 0600 );
847 int status = autoSaveFile.status();
848 kdDebug(5006) << k_funcinfo << "autoSaveFile.status() = " << status << endl;
849 if ( status == 0 ) { // no error
850 kdDebug(5006) << "autosaving message in " << filename << endl;
851 int fd = autoSaveFile.handle();
852 const DwString& msgStr = msg->asDwString();
853 if ( ::write( fd, msgStr.data(), msgStr.length() ) == -1 )
854 status = errno;
855 }
856 if ( status == 0 ) {
857 kdDebug(5006) << k_funcinfo << "closing autoSaveFile" << endl;
858 autoSaveFile.close();
859 mLastAutoSaveErrno = 0;
860 }
861 else {
862 kdDebug(5006) << k_funcinfo << "autosaving failed" << endl;
863 autoSaveFile.abort();
864 if ( status != mLastAutoSaveErrno ) {
865 // don't show the same error message twice
866 KMessageBox::queuedMessageBox( 0, KMessageBox::Sorry,
867 i18n("Autosaving the message as %1 "
868 "failed.\n"
869 "Reason: %2" )
870 .arg( filename, strerror( status ) ),
871 i18n("Autosaving Failed") );
872 mLastAutoSaveErrno = status;
873 }
874 }
875
876 if ( autoSaveInterval() > 0 )
877 updateAutoSave();
878}
879
880//-----------------------------------------------------------------------------
881void KMComposeWin::slotView(void)
882{
883 if (!mDone)
884 return; // otherwise called from rethinkFields during the construction
885 // which is not the intended behavior
886 int id;
887
888 //This sucks awfully, but no, I cannot get an activated(int id) from
889 // actionContainer()
890 if (!sender()->isA("TDEToggleAction"))
891 return;
892 TDEToggleAction *act = (TDEToggleAction *) sender();
893
894 if (act == mAllFieldsAction)
895 id = 0;
896 else if (act == mIdentityAction)
897 id = HDR_IDENTITY;
898 else if (act == mTransportAction)
899 id = HDR_TRANSPORT;
900 else if (act == mFromAction)
901 id = HDR_FROM;
902 else if (act == mReplyToAction)
903 id = HDR_REPLY_TO;
904 else if (act == mToAction)
905 id = HDR_TO;
906 else if (act == mCcAction)
907 id = HDR_CC;
908 else if (act == mBccAction)
909 id = HDR_BCC;
910 else if (act == mSubjectAction)
911 id = HDR_SUBJECT;
912 else if (act == mFccAction)
913 id = HDR_FCC;
914 else if ( act == mDictionaryAction )
915 id = HDR_DICTIONARY;
916 else
917 {
918 id = 0;
919 kdDebug(5006) << "Something is wrong (Oh, yeah?)" << endl;
920 return;
921 }
922
923 // sanders There's a bug here this logic doesn't work if no
924 // fields are shown and then show all fields is selected.
925 // Instead of all fields being shown none are.
926 if (!act->isChecked())
927 {
928 // hide header
929 if (id > 0) mShowHeaders = mShowHeaders & ~id;
930 else mShowHeaders = abs(mShowHeaders);
931 }
932 else
933 {
934 // show header
935 if (id > 0) mShowHeaders |= id;
936 else mShowHeaders = -abs(mShowHeaders);
937 }
938 rethinkFields(true);
939}
940
941int KMComposeWin::calcColumnWidth(int which, long allShowing, int width)
942{
943 if ( (allShowing & which) == 0 )
944 return width;
945
946 TQLabel *w;
947 if ( which == HDR_IDENTITY )
948 w = mLblIdentity;
949 else if ( which == HDR_DICTIONARY )
950 w = mDictionaryLabel;
951 else if ( which == HDR_FCC )
952 w = mLblFcc;
953 else if ( which == HDR_TRANSPORT )
954 w = mLblTransport;
955 else if ( which == HDR_FROM )
956 w = mLblFrom;
957 else if ( which == HDR_REPLY_TO )
958 w = mLblReplyTo;
959 else if ( which == HDR_SUBJECT )
960 w = mLblSubject;
961 else
962 return width;
963
964 w->setBuddy( mEditor ); // set dummy so we don't calculate width of '&' for this label.
965 w->adjustSize();
966 w->show();
967 return TQMAX( width, w->sizeHint().width() );
968}
969
970void KMComposeWin::rethinkFields(bool fromSlot)
971{
972 //This sucks even more but again no ids. sorry (sven)
973 int mask, row, numRows;
974 long showHeaders;
975
976 if (mShowHeaders < 0)
977 showHeaders = HDR_ALL;
978 else
979 showHeaders = mShowHeaders;
980
981 for (mask=1,mNumHeaders=0; mask<=showHeaders; mask<<=1)
982 if ((showHeaders&mask) != 0) mNumHeaders++;
983
984 numRows = mNumHeaders + 1;
985
986 delete mGrid;
987
988 mGrid = new TQGridLayout( mHeadersArea, numRows, 3, KDialogBase::marginHint()/2, KDialogBase::spacingHint());
989 mGrid->setColStretch(0, 1);
990 mGrid->setColStretch(1, 100);
991 mGrid->setColStretch(2, 1);
992 mGrid->setRowStretch( mNumHeaders + 1, 100 );
993
994 row = 0;
995 kdDebug(5006) << "KMComposeWin::rethinkFields" << endl;
996 if (mRecipientsEditor)
997 mLabelWidth = mRecipientsEditor->setFirstColumnWidth( 0 );
998 mLabelWidth = calcColumnWidth( HDR_IDENTITY, showHeaders, mLabelWidth );
999 mLabelWidth = calcColumnWidth( HDR_DICTIONARY, showHeaders, mLabelWidth );
1000 mLabelWidth = calcColumnWidth( HDR_FCC, showHeaders, mLabelWidth );
1001 mLabelWidth = calcColumnWidth( HDR_TRANSPORT, showHeaders, mLabelWidth );
1002 mLabelWidth = calcColumnWidth( HDR_FROM, showHeaders, mLabelWidth );
1003 mLabelWidth = calcColumnWidth( HDR_REPLY_TO, showHeaders, mLabelWidth );
1004 mLabelWidth = calcColumnWidth( HDR_SUBJECT, showHeaders, mLabelWidth );
1005
1006 if (!fromSlot) mAllFieldsAction->setChecked(showHeaders==HDR_ALL);
1007
1008 if (!fromSlot) mIdentityAction->setChecked(abs(mShowHeaders)&HDR_IDENTITY);
1009 rethinkHeaderLine(showHeaders,HDR_IDENTITY, row,
1010 mLblIdentity, mIdentity, mBtnIdentity);
1011
1012 if (!fromSlot) mDictionaryAction->setChecked(abs(mShowHeaders)&HDR_DICTIONARY);
1013 rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row,
1014 mDictionaryLabel, mDictionaryCombo, mBtnDictionary );
1015
1016 if (!fromSlot) mFccAction->setChecked(abs(mShowHeaders)&HDR_FCC);
1017 rethinkHeaderLine(showHeaders,HDR_FCC, row,
1018 mLblFcc, mFcc, mBtnFcc);
1019
1020 if (!fromSlot) mTransportAction->setChecked(abs(mShowHeaders)&HDR_TRANSPORT);
1021 rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row,
1022 mLblTransport, mTransport, mBtnTransport);
1023
1024 if (!fromSlot) mFromAction->setChecked(abs(mShowHeaders)&HDR_FROM);
1025 rethinkHeaderLine(showHeaders,HDR_FROM, row,
1026 mLblFrom, mEdtFrom /*, mBtnFrom */ );
1027
1028 TQWidget *prevFocus = mEdtFrom;
1029
1030 if (!fromSlot) mReplyToAction->setChecked(abs(mShowHeaders)&HDR_REPLY_TO);
1031 rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,
1032 mLblReplyTo, mEdtReplyTo, 0);
1033 if ( showHeaders & HDR_REPLY_TO ) {
1034 prevFocus = connectFocusMoving( prevFocus, mEdtReplyTo );
1035 }
1036
1037 if ( mClassicalRecipients ) {
1038 if (!fromSlot) mToAction->setChecked(abs(mShowHeaders)&HDR_TO);
1039 rethinkHeaderLine(showHeaders, HDR_TO, row,
1040 mLblTo, mEdtTo, mBtnTo,
1041 i18n("Primary Recipients"),
1042 i18n("<qt>The email addresses you put "
1043 "in this field receive a copy of the email.</qt>"));
1044 if ( showHeaders & HDR_TO ) {
1045 prevFocus = connectFocusMoving( prevFocus, mEdtTo );
1046 }
1047
1048 if (!fromSlot) mCcAction->setChecked(abs(mShowHeaders)&HDR_CC);
1049 rethinkHeaderLine(showHeaders, HDR_CC, row,
1050 mLblCc, mEdtCc, mBtnCc,
1051 i18n("Additional Recipients"),
1052 i18n("<qt>The email addresses you put "
1053 "in this field receive a copy of the email. "
1054 "Technically it is the same thing as putting all the "
1055 "addresses in the <b>To:</b> field but differs in "
1056 "that it usually symbolises the receiver of the "
1057 "Carbon Copy (CC) is a listener, not the main "
1058 "recipient.</qt>"));
1059 if ( showHeaders & HDR_CC ) {
1060 prevFocus = connectFocusMoving( prevFocus, mEdtCc );
1061 }
1062
1063 if (!fromSlot) mBccAction->setChecked(abs(mShowHeaders)&HDR_BCC);
1064 rethinkHeaderLine(showHeaders,HDR_BCC, row,
1065 mLblBcc, mEdtBcc, mBtnBcc,
1066 i18n("Hidden Recipients"),
1067 i18n("<qt>Essentially the same thing "
1068 "as the <b>Copy To:</b> field but differs in that "
1069 "all other recipients do not see who receives a "
1070 "blind copy.</qt>"));
1071 if ( showHeaders & HDR_BCC ) {
1072 prevFocus = connectFocusMoving( prevFocus, mEdtBcc );
1073 }
1074 } else {
1075 mGrid->addMultiCellWidget( mRecipientsEditor, row, row, 0, 2 );
1076 ++row;
1077
1078 if ( showHeaders & HDR_REPLY_TO ) {
1079 connect( mEdtReplyTo, TQ_SIGNAL( focusDown() ), mRecipientsEditor,
1080 TQ_SLOT( setFocusTop() ) );
1081 } else {
1082 connect( mEdtFrom, TQ_SIGNAL( focusDown() ), mRecipientsEditor,
1083 TQ_SLOT( setFocusTop() ) );
1084 }
1085 if ( showHeaders & HDR_REPLY_TO ) {
1086 connect( mRecipientsEditor, TQ_SIGNAL( focusUp() ), mEdtReplyTo, TQ_SLOT( setFocus() ) );
1087 } else {
1088 connect( mRecipientsEditor, TQ_SIGNAL( focusUp() ), mEdtFrom, TQ_SLOT( setFocus() ) );
1089 }
1090
1091 connect( mRecipientsEditor, TQ_SIGNAL( focusDown() ), mEdtSubject,
1092 TQ_SLOT( setFocus() ) );
1093 connect( mEdtSubject, TQ_SIGNAL( focusUp() ), mRecipientsEditor,
1094 TQ_SLOT( setFocusBottom() ) );
1095
1096 prevFocus = mRecipientsEditor;
1097 }
1098 if (!fromSlot) mSubjectAction->setChecked(abs(mShowHeaders)&HDR_SUBJECT);
1099 rethinkHeaderLine(showHeaders,HDR_SUBJECT, row,
1100 mLblSubject, mEdtSubject);
1101 connectFocusMoving( mEdtSubject, mEditor );
1102
1103 assert(row<=mNumHeaders);
1104
1105
1106 if( !mAtmList.isEmpty() )
1107 mAtmListView->show();
1108 else
1109 mAtmListView->hide();
1110 resize(this->size());
1111 repaint();
1112
1113 mHeadersArea->setMaximumHeight( mHeadersArea->sizeHint().height() );
1114 mGrid->activate();
1115 mHeadersArea->show();
1116
1117 slotUpdateAttachActions();
1118 mIdentityAction->setEnabled(!mAllFieldsAction->isChecked());
1119 mDictionaryAction->setEnabled( !mAllFieldsAction->isChecked() );
1120 mTransportAction->setEnabled(!mAllFieldsAction->isChecked());
1121 mFromAction->setEnabled(!mAllFieldsAction->isChecked());
1122 if ( mReplyToAction ) mReplyToAction->setEnabled(!mAllFieldsAction->isChecked());
1123 if ( mToAction ) mToAction->setEnabled(!mAllFieldsAction->isChecked());
1124 if ( mCcAction ) mCcAction->setEnabled(!mAllFieldsAction->isChecked());
1125 if ( mBccAction ) mBccAction->setEnabled(!mAllFieldsAction->isChecked());
1126 mFccAction->setEnabled(!mAllFieldsAction->isChecked());
1127 mSubjectAction->setEnabled(!mAllFieldsAction->isChecked());
1128 if (mRecipientsEditor)
1129 mRecipientsEditor->setFirstColumnWidth( mLabelWidth );
1130}
1131
1132TQWidget *KMComposeWin::connectFocusMoving( TQWidget *prev, TQWidget *next )
1133{
1134 connect( prev, TQ_SIGNAL( focusDown() ), next, TQ_SLOT( setFocus() ) );
1135 connect( next, TQ_SIGNAL( focusUp() ), prev, TQ_SLOT( setFocus() ) );
1136
1137 return next;
1138}
1139
1140//-----------------------------------------------------------------------------
1141void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
1142 TQLabel* aLbl,
1143 TQLineEdit* aEdt, TQPushButton* aBtn,
1144 const TQString &toolTip, const TQString &whatsThis )
1145{
1146 if (aValue & aMask)
1147 {
1148 if ( !toolTip.isEmpty() )
1149 TQToolTip::add( aLbl, toolTip );
1150 if ( !whatsThis.isEmpty() )
1151 TQWhatsThis::add( aLbl, whatsThis );
1152 aLbl->setFixedWidth( mLabelWidth );
1153 aLbl->setBuddy(aEdt);
1154 mGrid->addWidget(aLbl, aRow, 0);
1155 aEdt->setBackgroundColor( mBackColor );
1156 aEdt->show();
1157
1158 if (aBtn) {
1159 mGrid->addWidget(aEdt, aRow, 1);
1160
1161 mGrid->addWidget(aBtn, aRow, 2);
1162 aBtn->show();
1163 } else {
1164 mGrid->addMultiCellWidget(aEdt, aRow, aRow, 1, 2 );
1165 }
1166 aRow++;
1167 }
1168 else
1169 {
1170 aLbl->hide();
1171 aEdt->hide();
1172 if (aBtn) aBtn->hide();
1173 }
1174}
1175
1176//-----------------------------------------------------------------------------
1177void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
1178 TQLabel* aLbl,
1179 TQComboBox* aCbx, TQCheckBox* aChk)
1180{
1181 if (aValue & aMask)
1182 {
1183 aLbl->adjustSize();
1184 aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
1185 aLbl->setMinimumSize(aLbl->size());
1186 aLbl->show();
1187 aLbl->setBuddy(aCbx);
1188 mGrid->addWidget(aLbl, aRow, 0);
1189 aCbx->show();
1190 aCbx->setMinimumSize(100, aLbl->height()+2);
1191
1192 mGrid->addWidget(aCbx, aRow, 1);
1193 if ( aChk ) {
1194 mGrid->addWidget(aChk, aRow, 2);
1195 aChk->setFixedSize(aChk->sizeHint().width(), aLbl->height());
1196 aChk->show();
1197 }
1198 aRow++;
1199 }
1200 else
1201 {
1202 aLbl->hide();
1203 aCbx->hide();
1204 if ( aChk )
1205 aChk->hide();
1206 }
1207}
1208
1209//-----------------------------------------------------------------------------
1210void KMComposeWin::getTransportMenu()
1211{
1212 TQStringList availTransports;
1213
1214 mActNowMenu->clear();
1215 mActLaterMenu->clear();
1216 availTransports = KMail::TransportManager::transportNames();
1217 TQStringList::Iterator it;
1218 int id = 0;
1219 for(it = availTransports.begin(); it != availTransports.end() ; ++it, id++)
1220 {
1221 mActNowMenu->insertItem((*it).replace("&", "&&"), id);
1222 mActLaterMenu->insertItem((*it).replace("&", "&&"), id);
1223 }
1224}
1225
1226
1227//-----------------------------------------------------------------------------
1228void KMComposeWin::setupActions(void)
1229{
1230 TDEActionMenu *actActionNowMenu, *actActionLaterMenu;
1231
1232 if (kmkernel->msgSender()->sendImmediate()) //default == send now?
1233 {
1234 //default = send now, alternative = queue
1235 ( void ) new TDEAction( i18n("&Send Mail"), "mail-send", CTRL+Key_Return,
1236 this, TQ_SLOT(slotSendNow()), actionCollection(),"send_default");
1237
1238 // FIXME: change to mail_send_via icon when this exits.
1239 actActionNowMenu = new TDEActionMenu (i18n("&Send Mail Via"), "mail-send",
1240 actionCollection(), "send_default_via" );
1241
1242 (void) new TDEAction (i18n("Send &Later"), "queue", 0, this,
1243 TQ_SLOT(slotSendLater()), actionCollection(),"send_alternative");
1244 actActionLaterMenu = new TDEActionMenu (i18n("Send &Later Via"), "queue",
1245 actionCollection(), "send_alternative_via" );
1246
1247 }
1248 else //no, default = send later
1249 {
1250 //default = queue, alternative = send now
1251 (void) new TDEAction (i18n("Send &Later"), "queue",
1252 CTRL+Key_Return,
1253 this, TQ_SLOT(slotSendLater()), actionCollection(),"send_default");
1254 actActionLaterMenu = new TDEActionMenu (i18n("Send &Later Via"), "queue",
1255 actionCollection(), "send_default_via" );
1256
1257 ( void ) new TDEAction( i18n("&Send Mail"), "mail-send", 0,
1258 this, TQ_SLOT(slotSendNow()), actionCollection(),"send_alternative");
1259
1260 // FIXME: change to mail_send_via icon when this exits.
1261 actActionNowMenu = new TDEActionMenu (i18n("&Send Mail Via"), "mail-send",
1262 actionCollection(), "send_alternative_via" );
1263
1264 }
1265
1266 // needed for sending "default transport"
1267 actActionNowMenu->setDelayed(true);
1268 actActionLaterMenu->setDelayed(true);
1269
1270 connect( actActionNowMenu, TQ_SIGNAL( activated() ), this,
1271 TQ_SLOT( slotSendNow() ) );
1272 connect( actActionLaterMenu, TQ_SIGNAL( activated() ), this,
1273 TQ_SLOT( slotSendLater() ) );
1274
1275
1276 mActNowMenu = actActionNowMenu->popupMenu();
1277 mActLaterMenu = actActionLaterMenu->popupMenu();
1278
1279 connect( mActNowMenu, TQ_SIGNAL( activated( int ) ), this,
1280 TQ_SLOT( slotSendNowVia( int ) ) );
1281 connect( mActNowMenu, TQ_SIGNAL( aboutToShow() ), this,
1282 TQ_SLOT( getTransportMenu() ) );
1283
1284 connect( mActLaterMenu, TQ_SIGNAL( activated( int ) ), this,
1285 TQ_SLOT( slotSendLaterVia( int ) ) );
1286 connect( mActLaterMenu, TQ_SIGNAL( aboutToShow() ), this,
1287 TQ_SLOT( getTransportMenu() ) );
1288
1289
1290
1291
1292 (void) new TDEAction (i18n("Save as &Draft"), "document-save", 0,
1293 this, TQ_SLOT(slotSaveDraft()),
1294 actionCollection(), "save_in_drafts");
1295 (void) new TDEAction (i18n("Save as &Template"), "document-save", 0,
1296 this, TQ_SLOT(slotSaveTemplate()),
1297 actionCollection(), "save_in_templates");
1298 (void) new TDEAction (i18n("&Insert File..."), "document-open", 0,
1299 this, TQ_SLOT(slotInsertFile()),
1300 actionCollection(), "insert_file");
1301 mRecentAction = new TDERecentFilesAction (i18n("&Insert File Recent"),
1302 "document-open", 0,
1303 this, TQ_SLOT(slotInsertRecentFile(const KURL&)),
1304 actionCollection(), "insert_file_recent");
1305
1306 mRecentAction->loadEntries( KMKernel::config() );
1307
1308 (void) new TDEAction (i18n("&Address Book"), "contents",0,
1309 this, TQ_SLOT(slotAddrBook()),
1310 actionCollection(), "addressbook");
1311 (void) new TDEAction (i18n("&New Composer"), "mail-message-new",
1312 TDEStdAccel::shortcut(TDEStdAccel::New),
1313 this, TQ_SLOT(slotNewComposer()),
1314 actionCollection(), "new_composer");
1315 (void) new TDEAction (i18n("New Main &Window"), "window-new", 0,
1316 this, TQ_SLOT(slotNewMailReader()),
1317 actionCollection(), "open_mailreader");
1318
1319 if ( !mClassicalRecipients ) {
1320 new TDEAction( i18n("Select &Recipients..."), CTRL + Key_L, mRecipientsEditor,
1321 TQ_SLOT( selectRecipients() ), actionCollection(), "select_recipients" );
1322 new TDEAction( i18n("Save &Distribution List..."), 0, mRecipientsEditor,
1323 TQ_SLOT( saveDistributionList() ), actionCollection(),
1324 "save_distribution_list" );
1325 }
1326
1327 //KStdAction::save(this, TQ_SLOT(), actionCollection(), "save_message");
1328 KStdAction::print (this, TQ_SLOT(slotPrint()), actionCollection());
1329 KStdAction::close (this, TQ_SLOT(slotClose()), actionCollection());
1330
1331 KStdAction::undo (this, TQ_SLOT(slotUndo()), actionCollection());
1332 KStdAction::redo (this, TQ_SLOT(slotRedo()), actionCollection());
1333 KStdAction::cut (this, TQ_SLOT(slotCut()), actionCollection());
1334 KStdAction::copy (this, TQ_SLOT(slotCopy()), actionCollection());
1335 KStdAction::pasteText (this, TQ_SLOT(slotPasteClipboard()), actionCollection());
1336 KStdAction::selectAll (this, TQ_SLOT(slotMarkAll()), actionCollection());
1337
1338 KStdAction::find (this, TQ_SLOT(slotFind()), actionCollection());
1339 KStdAction::findNext(this, TQ_SLOT(slotSearchAgain()), actionCollection());
1340
1341 KStdAction::replace (this, TQ_SLOT(slotReplace()), actionCollection());
1342 KStdAction::spelling (this, TQ_SLOT(slotSpellcheck()), actionCollection(), "tools-check-spelling");
1343
1344 mPasteQuotation = new TDEAction (i18n("Pa&ste as Quotation"),0,this,TQ_SLOT( slotPasteClipboardAsQuotation()),
1345 actionCollection(), "paste_quoted");
1346
1347 (void) new TDEAction (i18n("Paste as Attac&hment"),0,this,TQ_SLOT( slotPasteClipboardAsAttachment()),
1348 actionCollection(), "paste_att");
1349
1350 TDEAction * addq = new TDEAction(i18n("Add &Quote Characters"), 0, this,
1351 TQ_SLOT(slotAddQuotes()), actionCollection(), "tools_quote");
1352 connect( mEditor, TQ_SIGNAL(selectionAvailable(bool)),
1353 addq, TQ_SLOT(setEnabled(bool)) );
1354
1355 TDEAction * remq = new TDEAction(i18n("Re&move Quote Characters"), 0, this,
1356 TQ_SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote");
1357 connect( mEditor, TQ_SIGNAL(selectionAvailable(bool)),
1358 remq, TQ_SLOT(setEnabled(bool)) );
1359
1360
1361 (void) new TDEAction (i18n("Cl&ean Spaces"), 0, this, TQ_SLOT(slotCleanSpace()),
1362 actionCollection(), "clean_spaces");
1363
1364 mFixedFontAction = new TDEToggleAction( i18n("Use Fi&xed Font"), 0, this,
1365 TQ_SLOT(slotUpdateFont()), actionCollection(), "toggle_fixedfont" );
1366 mFixedFontAction->setChecked( GlobalSettings::self()->useFixedFont() );
1367
1368 //these are checkable!!!
1369 mUrgentAction = new TDEToggleAction (i18n("&Urgent"), 0,
1370 actionCollection(),
1371 "urgent");
1372 mRequestMDNAction = new TDEToggleAction ( i18n("&Request Disposition Notification"), 0,
1373 actionCollection(),
1374 "options_request_mdn");
1375 mRequestMDNAction->setChecked(GlobalSettings::self()->requestMDN());
1376 //----- Message-Encoding Submenu
1377 mEncodingAction = new TDESelectAction( i18n( "Se&t Encoding" ), "charset",
1378 0, this, TQ_SLOT(slotSetCharset() ),
1379 actionCollection(), "charsets" );
1380 mWordWrapAction = new TDEToggleAction (i18n("&Wordwrap"), 0,
1381 actionCollection(), "wordwrap");
1382 mWordWrapAction->setChecked(GlobalSettings::self()->wordWrap());
1383 connect(mWordWrapAction, TQ_SIGNAL(toggled(bool)), TQ_SLOT(slotWordWrapToggled(bool)));
1384
1385 mSnippetAction = new TDEToggleAction ( i18n("&Snippets"), 0,
1386 actionCollection(), "snippets");
1387 connect(mSnippetAction, TQ_SIGNAL(toggled(bool)), mSnippetWidget, TQ_SLOT(setShown(bool)) );
1388 mSnippetAction->setChecked( GlobalSettings::self()->showSnippetManager() );
1389
1390 mAutoSpellCheckingAction =
1391 new TDEToggleAction( i18n( "&Automatic Spellchecking" ), "tools-check-spelling", 0,
1392 actionCollection(), "options_auto_spellchecking" );
1393 const bool spellChecking = GlobalSettings::self()->autoSpellChecking();
1394 mAutoSpellCheckingAction->setEnabled( !GlobalSettings::self()->useExternalEditor() );
1395 mAutoSpellCheckingAction->setChecked( !GlobalSettings::self()->useExternalEditor() && spellChecking );
1396 slotAutoSpellCheckingToggled( !GlobalSettings::self()->useExternalEditor() && spellChecking );
1397 connect( mAutoSpellCheckingAction, TQ_SIGNAL( toggled( bool ) ),
1398 this, TQ_SLOT( slotAutoSpellCheckingToggled( bool ) ) );
1399
1400 TQStringList encodings = KMMsgBase::supportedEncodings(true);
1401 encodings.prepend( i18n("Auto-Detect"));
1402 mEncodingAction->setItems( encodings );
1403 mEncodingAction->setCurrentItem( -1 );
1404
1405 //these are checkable!!!
1406 markupAction = new TDEToggleAction (i18n("Formatting (HTML)"), 0, this,
1407 TQ_SLOT(slotToggleMarkup()),
1408 actionCollection(), "html");
1409
1410 mAllFieldsAction = new TDEToggleAction (i18n("&All Fields"), 0, this,
1411 TQ_SLOT(slotView()),
1412 actionCollection(), "show_all_fields");
1413 mIdentityAction = new TDEToggleAction (i18n("&Identity"), 0, this,
1414 TQ_SLOT(slotView()),
1415 actionCollection(), "show_identity");
1416 mDictionaryAction = new TDEToggleAction (i18n("&Dictionary"), 0, this,
1417 TQ_SLOT(slotView()),
1418 actionCollection(), "show_dictionary");
1419 mFccAction = new TDEToggleAction (i18n("&Sent-Mail Folder"), 0, this,
1420 TQ_SLOT(slotView()),
1421 actionCollection(), "show_fcc");
1422 mTransportAction = new TDEToggleAction (i18n("&Mail Transport"), 0, this,
1423 TQ_SLOT(slotView()),
1424 actionCollection(), "show_transport");
1425 mFromAction = new TDEToggleAction (i18n("&From"), 0, this,
1426 TQ_SLOT(slotView()),
1427 actionCollection(), "show_from");
1428 mReplyToAction = new TDEToggleAction (i18n("&Reply To"), 0, this,
1429 TQ_SLOT(slotView()),
1430 actionCollection(), "show_reply_to");
1431 if ( mClassicalRecipients ) {
1432 mToAction = new TDEToggleAction (i18n("&To"), 0, this,
1433 TQ_SLOT(slotView()),
1434 actionCollection(), "show_to");
1435 mCcAction = new TDEToggleAction (i18n("&CC"), 0, this,
1436 TQ_SLOT(slotView()),
1437 actionCollection(), "show_cc");
1438 mBccAction = new TDEToggleAction (i18n("&BCC"), 0, this,
1439 TQ_SLOT(slotView()),
1440 actionCollection(), "show_bcc");
1441 }
1442 mSubjectAction = new TDEToggleAction (i18n("S&ubject"), 0, this,
1443 TQ_SLOT(slotView()),
1444 actionCollection(), "show_subject");
1445 //end of checkable
1446
1447 mAppendSignatureAction = new TDEAction (i18n("Append S&ignature"), 0, this,
1448 TQ_SLOT(slotAppendSignature()),
1449 actionCollection(), "append_signature");
1450 mPrependSignatureAction = new TDEAction (i18n("Prepend S&ignature"), 0, this,
1451 TQ_SLOT(slotPrependSignature()),
1452 actionCollection(), "prepend_signature");
1453
1454 mInsertSignatureAction = new TDEAction (i18n("Insert Signature At C&ursor Position"), "edit", 0, this,
1455 TQ_SLOT(slotInsertSignatureAtCursor()),
1456 actionCollection(), "insert_signature_at_cursor_position");
1457
1458 mAttachPK = new TDEAction (i18n("Attach &Public Key..."), 0, this,
1459 TQ_SLOT(slotInsertPublicKey()),
1460 actionCollection(), "attach_public_key");
1461 mAttachMPK = new TDEAction (i18n("Attach &My Public Key"), 0, this,
1462 TQ_SLOT(slotInsertMyPublicKey()),
1463 actionCollection(), "attach_my_public_key");
1464 (void) new TDEAction (i18n("&Attach File..."), "attach",
1465 0, this, TQ_SLOT(slotAttachFile()),
1466 actionCollection(), "attach");
1467 mAttachRemoveAction = new TDEAction (i18n("&Remove Attachment"), 0, this,
1468 TQ_SLOT(slotAttachRemove()),
1469 actionCollection(), "remove");
1470 mAttachSaveAction = new TDEAction (i18n("&Save Attachment As..."), "document-save",0,
1471 this, TQ_SLOT(slotAttachSave()),
1472 actionCollection(), "attach_save");
1473 mAttachPropertiesAction = new TDEAction (i18n("Attachment Pr&operties"), 0, this,
1474 TQ_SLOT(slotAttachProperties()),
1475 actionCollection(), "attach_properties");
1476
1477 setStandardToolBarMenuEnabled(true);
1478
1479 KStdAction::keyBindings(this, TQ_SLOT(slotEditKeys()), actionCollection());
1480 KStdAction::configureToolbars(this, TQ_SLOT(slotEditToolbars()), actionCollection());
1481 KStdAction::preferences(kmkernel, TQ_SLOT(slotShowConfigurationDialog()), actionCollection());
1482
1483 (void) new TDEAction (i18n("&Spellchecker..."), 0, this, TQ_SLOT(slotSpellcheckConfig()),
1484 actionCollection(), "setup_spellchecker");
1485
1486 if ( Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ) ) {
1487 TDEToggleAction * a = new TDEToggleAction( i18n( "Encrypt Message with Chiasmus..." ),
1488 "chidecrypted", 0, actionCollection(),
1489 "encrypt_message_chiasmus" );
1490 a->setCheckedState( KGuiItem( i18n( "Encrypt Message with Chiasmus..." ), "chiencrypted" ) );
1491 mEncryptChiasmusAction = a;
1492 connect( mEncryptChiasmusAction, TQ_SIGNAL(toggled(bool)),
1493 this, TQ_SLOT(slotEncryptChiasmusToggled(bool)) );
1494 } else {
1495 mEncryptChiasmusAction = 0;
1496 }
1497
1498 mEncryptAction = new TDEToggleAction (i18n("&Encrypt Message"),
1499 "decrypted", 0,
1500 actionCollection(), "encrypt_message");
1501 mSignAction = new TDEToggleAction (i18n("&Sign Message"),
1502 "signature", 0,
1503 actionCollection(), "sign_message");
1504 // get PGP user id for the chosen identity
1505 const KPIM::Identity & ident =
1506 kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
1507 // PENDING(marc): check the uses of this member and split it into
1508 // smime/openpgp and or enc/sign, if necessary:
1509 mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
1510 mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();
1511
1512 mLastEncryptActionState = false;
1513 mLastSignActionState = GlobalSettings::self()->pgpAutoSign();
1514
1515 // "Attach public key" is only possible if OpenPGP support is available:
1516 mAttachPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() );
1517
1518 // "Attach my public key" is only possible if OpenPGP support is
1519 // available and the user specified his key for the current identity:
1520 mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
1521 !ident.pgpEncryptionKey().isEmpty() );
1522
1523 if ( !Kleo::CryptoBackendFactory::instance()->openpgp() && !Kleo::CryptoBackendFactory::instance()->smime() ) {
1524 // no crypto whatsoever
1525 mEncryptAction->setEnabled( false );
1526 setEncryption( false );
1527 mSignAction->setEnabled( false );
1528 setSigning( false );
1529 } else {
1530 const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp()
1531 && !ident.pgpSigningKey().isEmpty();
1532 const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime()
1533 && !ident.smimeSigningKey().isEmpty();
1534
1535 setEncryption( false );
1536 setSigning( ( canOpenPGPSign || canSMIMESign ) && GlobalSettings::self()->pgpAutoSign() );
1537 }
1538
1539 connect(mEncryptAction, TQ_SIGNAL(toggled(bool)),
1540 TQ_SLOT(slotEncryptToggled( bool )));
1541 connect(mSignAction, TQ_SIGNAL(toggled(bool)),
1542 TQ_SLOT(slotSignToggled( bool )));
1543
1544 TQStringList l;
1545 for ( int i = 0 ; i < numCryptoMessageFormats ; ++i )
1546 l.push_back( Kleo::cryptoMessageFormatToLabel( cryptoMessageFormats[i] ) );
1547
1548 mCryptoModuleAction = new TDESelectAction( i18n( "&Cryptographic Message Format" ), 0,
1549 this, TQ_SLOT(slotSelectCryptoModule()),
1550 actionCollection(), "options_select_crypto" );
1551 mCryptoModuleAction->setItems( l );
1552 mCryptoModuleAction->setCurrentItem( format2cb( ident.preferredCryptoMessageFormat() ) );
1553 mCryptoModuleAction->setToolTip( i18n( "Select a cryptographic format for this message" ) );
1554 slotSelectCryptoModule( true /* initialize */ );
1555
1556 TQStringList styleItems;
1557 styleItems << i18n( "Standard" );
1558 styleItems << i18n( "Bulleted List (Disc)" );
1559 styleItems << i18n( "Bulleted List (Circle)" );
1560 styleItems << i18n( "Bulleted List (Square)" );
1561 styleItems << i18n( "Ordered List (Decimal)" );
1562 styleItems << i18n( "Ordered List (Alpha lower)" );
1563 styleItems << i18n( "Ordered List (Alpha upper)" );
1564
1565 listAction = new TDESelectAction( i18n( "Select Style" ), 0, actionCollection(),
1566 "text_list" );
1567 listAction->setItems( styleItems );
1568 listAction->setToolTip( i18n( "Select a list style" ) );
1569 connect( listAction, TQ_SIGNAL( activated( const TQString& ) ),
1570 TQ_SLOT( slotListAction( const TQString& ) ) );
1571 fontAction = new TDEFontAction( "Select Font", 0, actionCollection(),
1572 "text_font" );
1573 fontAction->setToolTip( i18n( "Select a font" ) );
1574 connect( fontAction, TQ_SIGNAL( activated( const TQString& ) ),
1575 TQ_SLOT( slotFontAction( const TQString& ) ) );
1576 fontSizeAction = new TDEFontSizeAction( "Select Size", 0, actionCollection(),
1577 "text_size" );
1578 fontSizeAction->setToolTip( i18n( "Select a font size" ) );
1579 connect( fontSizeAction, TQ_SIGNAL( fontSizeChanged( int ) ),
1580 TQ_SLOT( slotSizeAction( int ) ) );
1581
1582 alignLeftAction = new TDEToggleAction (i18n("Align Left"), "format-text-direction-ltr", 0,
1583 this, TQ_SLOT(slotAlignLeft()), actionCollection(),
1584 "align_left");
1585 alignLeftAction->setChecked( true );
1586 alignRightAction = new TDEToggleAction (i18n("Align Right"), "format-text-direction-rtl", 0,
1587 this, TQ_SLOT(slotAlignRight()), actionCollection(),
1588 "align_right");
1589 alignCenterAction = new TDEToggleAction (i18n("Align Center"), "text_center", 0,
1590 this, TQ_SLOT(slotAlignCenter()), actionCollection(),
1591 "align_center");
1592 textBoldAction = new TDEToggleAction( i18n("&Bold"), "format-text-bold", CTRL+Key_B,
1593 this, TQ_SLOT(slotTextBold()),
1594 actionCollection(), "format-text-bold");
1595 textItalicAction = new TDEToggleAction( i18n("&Italic"), "format-text-italic", CTRL+Key_I,
1596 this, TQ_SLOT(slotTextItalic()),
1597 actionCollection(), "format-text-italic");
1598 textUnderAction = new TDEToggleAction( i18n("&Underline"), "format-text-underline", CTRL+Key_U,
1599 this, TQ_SLOT(slotTextUnder()),
1600 actionCollection(), "format-text-underline");
1601 actionFormatReset = new TDEAction( i18n( "Reset Font Settings" ), "eraser", 0,
1602 this, TQ_SLOT( slotFormatReset() ),
1603 actionCollection(), "format_reset");
1604 actionFormatColor = new TDEAction( i18n( "Text Color..." ), "colorize", 0,
1605 this, TQ_SLOT( slotTextColor() ),
1606 actionCollection(), "format_color");
1607
1608 // editorFocusChanged(false);
1609 createGUI("kmcomposerui.rc");
1610
1611 connect( toolBar("htmlToolBar"), TQ_SIGNAL( visibilityChanged(bool) ),
1612 this, TQ_SLOT( htmlToolBarVisibilityChanged(bool) ) );
1613
1614 // In Kontact, this entry would read "Configure Kontact", but bring
1615 // up KMail's config dialog. That's sensible, though, so fix the label.
1616 TDEAction* configureAction = actionCollection()->action("options_configure" );
1617 if ( configureAction )
1618 configureAction->setText( i18n("Configure KMail..." ) );
1619}
1620
1621//-----------------------------------------------------------------------------
1622void KMComposeWin::setupStatusBar(void)
1623{
1624 statusBar()->insertItem("", 0, 1);
1625 statusBar()->setItemAlignment(0, AlignLeft | AlignVCenter);
1626
1627 statusBar()->insertItem(i18n( " Spellcheck: %1 ").arg( " " ), 3, 0, true );
1628 statusBar()->insertItem(i18n( " Column: %1 ").arg(" "), 2, 0, true);
1629 statusBar()->insertItem(i18n( " Line: %1 ").arg(" "), 1, 0, true);
1630}
1631
1632
1633//-----------------------------------------------------------------------------
1634void KMComposeWin::updateCursorPosition()
1635{
1636 int col,line;
1637 TQString temp;
1638 line = mEditor->currentLine();
1639 col = mEditor->currentColumn();
1640 temp = i18n(" Line: %1 ").arg(line+1);
1641 statusBar()->changeItem(temp,1);
1642 temp = i18n(" Column: %1 ").arg(col+1);
1643 statusBar()->changeItem(temp,2);
1644}
1645
1646
1647//-----------------------------------------------------------------------------
1648void KMComposeWin::setupEditor(void)
1649{
1650 //TQPopupMenu* menu;
1651 mEditor->setModified(false);
1652 TQFontMetrics fm(mBodyFont);
1653 mEditor->setTabStopWidth(fm.width(TQChar(' ')) * 8);
1654 //mEditor->setFocusPolicy(TQWidget::ClickFocus);
1655
1656 slotWordWrapToggled( GlobalSettings::self()->wordWrap() );
1657
1658 // Font setup
1659 slotUpdateFont();
1660
1661 /* installRBPopup() is broken in tdelibs, we should wait for
1662 the new klibtextedit (dnaber, 2002-01-01)
1663 menu = new TQPopupMenu(this);
1664 //#ifdef BROKEN
1665 menu->insertItem(i18n("Undo"),mEditor,
1666 TQ_SLOT(undo()), TDEStdAccel::shortcut(TDEStdAccel::Undo));
1667 menu->insertItem(i18n("Redo"),mEditor,
1668 TQ_SLOT(redo()), TDEStdAccel::shortcut(TDEStdAccel::Redo));
1669 menu->insertSeparator();
1670 //#endif //BROKEN
1671 menu->insertItem(i18n("Cut"), this, TQ_SLOT(slotCut()));
1672 menu->insertItem(i18n("Copy"), this, TQ_SLOT(slotCopy()));
1673 menu->insertItem(i18n("Paste"), this, TQ_SLOT(slotPasteClipboard()));
1674 menu->insertItem(i18n("Mark All"),this, TQ_SLOT(slotMarkAll()));
1675 menu->insertSeparator();
1676 menu->insertItem(i18n("Find..."), this, TQ_SLOT(slotFind()));
1677 menu->insertItem(i18n("Replace..."), this, TQ_SLOT(slotReplace()));
1678 menu->insertSeparator();
1679 menu->insertItem(i18n("Fixed Font Widths"), this, TQ_SLOT(slotUpdateFont()));
1680 mEditor->installRBPopup(menu);
1681 */
1682 updateCursorPosition();
1683 connect(mEditor,TQ_SIGNAL(CursorPositionChanged()),TQ_SLOT(updateCursorPosition()));
1684 connect( mEditor, TQ_SIGNAL( currentFontChanged( const TQFont & ) ),
1685 this, TQ_SLOT( fontChanged( const TQFont & ) ) );
1686 connect( mEditor, TQ_SIGNAL( currentAlignmentChanged( int ) ),
1687 this, TQ_SLOT( alignmentChanged( int ) ) );
1688
1689}
1690
1691
1692//-----------------------------------------------------------------------------
1693static TQString cleanedUpHeaderString( const TQString & s )
1694{
1695 // remove invalid characters from the header strings
1696 TQString res( s );
1697 res.replace( '\r', "" );
1698 res.replace( '\n', " " );
1699 return res.stripWhiteSpace();
1700}
1701
1702//-----------------------------------------------------------------------------
1703TQString KMComposeWin::subject() const
1704{
1705 return cleanedUpHeaderString( mEdtSubject->text() );
1706}
1707
1708//-----------------------------------------------------------------------------
1709TQString KMComposeWin::to() const
1710{
1711 if ( mEdtTo ) {
1712 return cleanedUpHeaderString( mEdtTo->text() );
1713 } else if ( mRecipientsEditor ) {
1714 return mRecipientsEditor->recipientString( Recipient::To );
1715 } else {
1716 return TQString();
1717 }
1718}
1719
1720//-----------------------------------------------------------------------------
1721TQString KMComposeWin::cc() const
1722{
1723 if ( mEdtCc && !mEdtCc->isHidden() ) {
1724 return cleanedUpHeaderString( mEdtCc->text() );
1725 } else if ( mRecipientsEditor ) {
1726 return mRecipientsEditor->recipientString( Recipient::Cc );
1727 } else {
1728 return TQString();
1729 }
1730}
1731
1732//-----------------------------------------------------------------------------
1733TQString KMComposeWin::bcc() const
1734{
1735 if ( mEdtBcc && !mEdtBcc->isHidden() ) {
1736 return cleanedUpHeaderString( mEdtBcc->text() );
1737 } else if ( mRecipientsEditor ) {
1738 return mRecipientsEditor->recipientString( Recipient::Bcc );
1739 } else {
1740 return TQString();
1741 }
1742}
1743
1744//-----------------------------------------------------------------------------
1745TQString KMComposeWin::from() const
1746{
1747 return cleanedUpHeaderString( mEdtFrom->text() );
1748}
1749
1750//-----------------------------------------------------------------------------
1751TQString KMComposeWin::replyTo() const
1752{
1753 if ( mEdtReplyTo ) {
1754 return cleanedUpHeaderString( mEdtReplyTo->text() );
1755 } else {
1756 return TQString();
1757 }
1758}
1759
1760//-----------------------------------------------------------------------------
1761void KMComposeWin::verifyWordWrapLengthIsAdequate(const TQString &body)
1762{
1763 int maxLineLength = 0;
1764 int curPos;
1765 int oldPos = 0;
1766 if (mEditor->TQTextEdit::wordWrap() == TQTextEdit::FixedColumnWidth) {
1767 for (curPos = 0; curPos < (int)body.length(); ++curPos)
1768 if (body[curPos] == '\n') {
1769 if ((curPos - oldPos) > maxLineLength)
1770 maxLineLength = curPos - oldPos;
1771 oldPos = curPos;
1772 }
1773 if ((curPos - oldPos) > maxLineLength)
1774 maxLineLength = curPos - oldPos;
1775 if (mEditor->wrapColumnOrWidth() < maxLineLength) // column
1776 mEditor->setWrapColumnOrWidth(maxLineLength);
1777 }
1778}
1779
1780//-----------------------------------------------------------------------------
1781void KMComposeWin::decryptOrStripOffCleartextSignature( TQCString& body )
1782{
1783 TQPtrList<Kpgp::Block> pgpBlocks;
1784 TQStrList nonPgpBlocks;
1785 if( Kpgp::Module::prepareMessageForDecryption( body,
1786 pgpBlocks, nonPgpBlocks ) )
1787 {
1788 // Only decrypt/strip off the signature if there is only one OpenPGP
1789 // block in the message
1790 if( pgpBlocks.count() == 1 )
1791 {
1792 Kpgp::Block* block = pgpBlocks.first();
1793 if( ( block->type() == Kpgp::PgpMessageBlock ) ||
1794 ( block->type() == Kpgp::ClearsignedBlock ) )
1795 {
1796 if( block->type() == Kpgp::PgpMessageBlock )
1797 // try to decrypt this OpenPGP block
1798 block->decrypt();
1799 else
1800 // strip off the signature
1801 block->verify();
1802
1803 body = nonPgpBlocks.first()
1804 + block->text()
1805 + nonPgpBlocks.last();
1806 }
1807 }
1808 }
1809}
1810
1811//-----------------------------------------------------------------------------
1812void KMComposeWin::setTransport( const TQString & transport )
1813{
1814 kdDebug(5006) << "KMComposeWin::setTransport( \"" << transport << "\" )" << endl;
1815 // Don't change the transport combobox if transport is empty
1816 if ( transport.isEmpty() )
1817 return;
1818
1819 bool transportFound = false;
1820 for ( int i = 0; i < mTransport->count(); ++i ) {
1821 if ( mTransport->text(i) == transport ) {
1822 transportFound = true;
1823 mTransport->setCurrentItem(i);
1824 kdDebug(5006) << "transport found, it's no. " << i << " in the list" << endl;
1825 break;
1826 }
1827 }
1828 if ( !transportFound ) { // unknown transport
1829 kdDebug(5006) << "unknown transport \"" << transport << "\"" << endl;
1830 if ( transport.startsWith("smtp://") || transport.startsWith("smtps://") ||
1831 transport.startsWith("file://") ) {
1832 // set custom transport
1833 mTransport->setEditText( transport );
1834 }
1835 else {
1836 // neither known nor custom transport -> use default transport
1837 mTransport->setCurrentText( GlobalSettings::self()->defaultTransport() );
1838 }
1839 }
1840}
1841
1842//-----------------------------------------------------------------------------
1843void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
1844 bool allowDecryption, bool isModified)
1845{
1846 //assert(newMsg!=0);
1847 if(!newMsg)
1848 {
1849 kdDebug(5006) << "KMComposeWin::setMsg() : newMsg == 0!" << endl;
1850 return;
1851 }
1852 mMsg = newMsg;
1853 KPIM::IdentityManager * im = kmkernel->identityManager();
1854
1855 mEdtFrom->setText(mMsg->from());
1856 mEdtReplyTo->setText(mMsg->replyTo());
1857 if ( mClassicalRecipients ) {
1858 mEdtTo->setText(mMsg->to());
1859 mEdtCc->setText(mMsg->cc());
1860 mEdtBcc->setText(mMsg->bcc());
1861 } else {
1862 mRecipientsEditor->setRecipientString( mMsg->to(), Recipient::To );
1863 mRecipientsEditor->setRecipientString( mMsg->cc(), Recipient::Cc );
1864 mRecipientsEditor->setRecipientString( mMsg->bcc(), Recipient::Bcc );
1865 mRecipientsEditor->setFocusBottom();
1866 }
1867 mEdtSubject->setText(mMsg->subject());
1868
1869 const bool stickyIdentity = mBtnIdentity->isChecked() && !mIgnoreStickyFields;
1870 const bool messageHasIdentity = !newMsg->headerField("X-KMail-Identity").isEmpty();
1871 if (!stickyIdentity && messageHasIdentity)
1872 mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
1873
1874 // don't overwrite the header values with identity specific values
1875 // unless the identity is sticky
1876 if ( !stickyIdentity ) {
1877 disconnect(mIdentity,TQ_SIGNAL(identityChanged(uint)),
1878 this, TQ_SLOT(slotIdentityChanged(uint)));
1879 }
1880 // load the mId into the gui, sticky or not, without emitting
1881 mIdentity->setCurrentIdentity( mId );
1882 const uint idToApply = mId;
1883 if ( !stickyIdentity ) {
1884 connect(mIdentity,TQ_SIGNAL(identityChanged(uint)),
1885 this, TQ_SLOT(slotIdentityChanged(uint)));
1886 } else {
1887 // load the message's state into the mId, without applying it to the gui
1888 // that's so we can detect that the id changed (because a sticky was set)
1889 // on apply()
1890 if ( messageHasIdentity )
1891 mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
1892 else
1893 mId = im->defaultIdentity().uoid();
1894 }
1895 // manually load the identity's value into the fields; either the one from the
1896 // messge, where appropriate, or the one from the sticky identity. What's in
1897 // mId might have changed meanwhile, thus the save value
1898 slotIdentityChanged( idToApply );
1899
1900 const KPIM::Identity & ident = im->identityForUoid( mIdentity->currentIdentity() );
1901
1902 // check for the presence of a DNT header, indicating that MDN's were
1903 // requested
1904 TQString mdnAddr = newMsg->headerField("Disposition-Notification-To");
1905 mRequestMDNAction->setChecked( ( !mdnAddr.isEmpty() &&
1906 im->thatIsMe( mdnAddr ) ) ||
1907 GlobalSettings::self()->requestMDN() );
1908
1909 // check for presence of a priority header, indicating urgent mail:
1910 mUrgentAction->setChecked( newMsg->isUrgent() );
1911
1912 if (!ident.isXFaceEnabled() || ident.xface().isEmpty())
1913 mMsg->removeHeaderField("X-Face");
1914 else
1915 {
1916 TQString xface = ident.xface();
1917 if (!xface.isEmpty())
1918 {
1919 int numNL = ( xface.length() - 1 ) / 70;
1920 for ( int i = numNL; i > 0; --i )
1921 xface.insert( i*70, "\n\t" );
1922 mMsg->setHeaderField("X-Face", xface);
1923 }
1924 }
1925
1926 // enable/disable encryption if the message was/wasn't encrypted
1927 switch ( mMsg->encryptionState() ) {
1928 case KMMsgFullyEncrypted: // fall through
1929 case KMMsgPartiallyEncrypted:
1930 mLastEncryptActionState = true;
1931 break;
1932 case KMMsgNotEncrypted:
1933 mLastEncryptActionState = false;
1934 break;
1935 default: // nothing
1936 break;
1937 }
1938
1939 // enable/disable signing if the message was/wasn't signed
1940 switch ( mMsg->signatureState() ) {
1941 case KMMsgFullySigned: // fall through
1942 case KMMsgPartiallySigned:
1943 mLastSignActionState = true;
1944 break;
1945 case KMMsgNotSigned:
1946 mLastSignActionState = false;
1947 break;
1948 default: // nothing
1949 break;
1950 }
1951
1952 // if these headers are present, the state of the message should be overruled
1953 if ( mMsg->headers().FindField( "X-KMail-SignatureActionEnabled" ) )
1954 mLastSignActionState = (mMsg->headerField( "X-KMail-SignatureActionEnabled" ) == "true");
1955 if ( mMsg->headers().FindField( "X-KMail-EncryptActionEnabled" ) )
1956 mLastEncryptActionState = (mMsg->headerField( "X-KMail-EncryptActionEnabled" ) == "true");
1957 if ( mMsg->headers().FindField( "X-KMail-CryptoMessageFormat" ) )
1958 mCryptoModuleAction->setCurrentItem( format2cb( static_cast<Kleo::CryptoMessageFormat>(
1959 mMsg->headerField( "X-KMail-CryptoMessageFormat" ).toInt() ) ) );
1960
1961 mLastIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
1962 mLastIdentityHasEncryptionKey = !ident.pgpEncryptionKey().isEmpty() || !ident.smimeEncryptionKey().isEmpty();
1963
1964 if ( Kleo::CryptoBackendFactory::instance()->openpgp() || Kleo::CryptoBackendFactory::instance()->smime() ) {
1965 const bool canOpenPGPSign = Kleo::CryptoBackendFactory::instance()->openpgp()
1966 && !ident.pgpSigningKey().isEmpty();
1967 const bool canSMIMESign = Kleo::CryptoBackendFactory::instance()->smime()
1968 && !ident.smimeSigningKey().isEmpty();
1969
1970 setEncryption( mLastEncryptActionState );
1971 setSigning( ( canOpenPGPSign || canSMIMESign ) && mLastSignActionState );
1972 }
1973 slotUpdateSignatureAndEncrypionStateIndicators();
1974
1975 // "Attach my public key" is only possible if the user uses OpenPGP
1976 // support and he specified his key:
1977 mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
1978 !ident.pgpEncryptionKey().isEmpty() );
1979
1980 TQString transport = newMsg->headerField("X-KMail-Transport");
1981 const bool stickyTransport = mBtnTransport->isChecked() && !mIgnoreStickyFields;
1982 if (!stickyTransport && !transport.isEmpty()) {
1983 setTransport( transport );
1984 }
1985
1986 // If we are using the default transport, and the originating account name of the original message matches the name of a valid transport, use setTransport() to set it
1987 // See Bug 1239
1988 if (transport.isEmpty() && !mMsg->originatingAccountName().isEmpty()) {
1989 TQString transportCandidate = mMsg->originatingAccountName();
1990 bool transportFound = false;
1991 for ( int i = 0; i < mTransport->count(); ++i ) {
1992 if ( mTransport->text(i) == transportCandidate ) {
1993 transportFound = true;
1994 setTransport(transportCandidate);
1995 break;
1996 }
1997 }
1998 }
1999
2000 if (!mBtnFcc->isChecked())
2001 {
2002 if (!mMsg->fcc().isEmpty())
2003 setFcc(mMsg->fcc());
2004 else
2005 setFcc(ident.fcc());
2006 }
2007
2008 const bool stickyDictionary = mBtnDictionary->isChecked() && !mIgnoreStickyFields;
2009 if ( !stickyDictionary ) {
2010 mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
2011 }
2012
2013 partNode * root = partNode::fromMessage( mMsg );
2014
2015 KMail::ObjectTreeParser otp; // all defaults are ok
2016 otp.parseObjectTree( root );
2017
2018 KMail::AttachmentCollector ac;
2019 ac.collectAttachmentsFrom( root );
2020
2021 for ( std::vector<partNode*>::const_iterator it = ac.attachments().begin() ; it != ac.attachments().end() ; ++it )
2022 addAttach( new KMMessagePart( (*it)->msgPart() ) );
2023
2024 mEditor->setText( otp.textualContent() );
2025 mCharset = otp.textualContentCharset();
2026 if ( partNode * n = root->findType( DwMime::kTypeText, DwMime::kSubtypeHtml ) )
2027 if ( partNode * p = n->parentNode() )
2028 if ( p->hasType( DwMime::kTypeMultipart ) &&
2029 p->hasSubType( DwMime::kSubtypeAlternative ) )
2030 if ( mMsg->headerField( "X-KMail-Markup" ) == "true" ) {
2031 toggleMarkup( true );
2032
2033 // get cte decoded body part
2034 mCharset = n->msgPart().charset();
2035 TQCString bodyDecoded = n->msgPart().bodyDecoded();
2036
2037 // respect html part charset
2038 const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
2039 if ( codec ) {
2040 mEditor->setText( codec->toUnicode( bodyDecoded ) );
2041 } else {
2042 mEditor->setText( TQString::fromLocal8Bit( bodyDecoded ) );
2043 }
2044 }
2045
2046 if ( mCharset.isEmpty() )
2047 mCharset = mMsg->charset();
2048 if ( mCharset.isEmpty() )
2049 mCharset = mDefCharset;
2050 setCharset( mCharset );
2051
2052 /* Handle the special case of non-mime mails */
2053 if ( mMsg->numBodyParts() == 0 && otp.textualContent().isEmpty() ) {
2054 mCharset=mMsg->charset();
2055 if ( mCharset.isEmpty() || mCharset == "default" )
2056 mCharset = mDefCharset;
2057
2058 TQCString bodyDecoded = mMsg->bodyDecoded();
2059
2060 if( allowDecryption )
2061 decryptOrStripOffCleartextSignature( bodyDecoded );
2062
2063 const TQTextCodec *codec = KMMsgBase::codecForName(mCharset);
2064 if (codec) {
2065 mEditor->setText(codec->toUnicode(bodyDecoded));
2066 } else
2067 mEditor->setText(TQString::fromLocal8Bit(bodyDecoded));
2068 }
2069#ifdef BROKEN_FOR_OPAQUE_SIGNED_OR_ENCRYPTED_MAILS
2070 const int num = mMsg->numBodyParts();
2071 kdDebug(5006) << "KMComposeWin::setMsg() mMsg->numBodyParts="
2072 << mMsg->numBodyParts() << endl;
2073
2074 if ( num > 0 ) {
2075 KMMessagePart bodyPart;
2076 int firstAttachment = 0;
2077
2078 mMsg->bodyPart(1, &bodyPart);
2079 if ( bodyPart.typeStr().lower() == "text" &&
2080 bodyPart.subtypeStr().lower() == "html" ) {
2081 // check whether we are inside a mp/al body part
2082 partNode *root = partNode::fromMessage( mMsg );
2083 partNode *node = root->findType( DwMime::kTypeText,
2084 DwMime::kSubtypeHtml );
2085 if ( node && node->parentNode() &&
2086 node->parentNode()->hasType( DwMime::kTypeMultipart ) &&
2087 node->parentNode()->hasSubType( DwMime::kSubtypeAlternative ) ) {
2088 // we have a mp/al body part with a text and an html body
2089 kdDebug(5006) << "KMComposeWin::setMsg() : text/html found" << endl;
2090 firstAttachment = 2;
2091 if ( mMsg->headerField( "X-KMail-Markup" ) == "true" )
2092 toggleMarkup( true );
2093 }
2094 delete root; root = 0;
2095 }
2096 if ( firstAttachment == 0 ) {
2097 mMsg->bodyPart(0, &bodyPart);
2098 if ( bodyPart.typeStr().lower() == "text" ) {
2099 // we have a mp/mx body with a text body
2100 kdDebug(5006) << "KMComposeWin::setMsg() : text/* found" << endl;
2101 firstAttachment = 1;
2102 }
2103 }
2104
2105 if ( firstAttachment != 0 ) // there's text to show
2106 {
2107 mCharset = bodyPart.charset();
2108 if ( mCharset.isEmpty() || mCharset == "default" )
2109 mCharset = mDefCharset;
2110
2111 TQCString bodyDecoded = bodyPart.bodyDecoded();
2112
2113 if( allowDecryption )
2114 decryptOrStripOffCleartextSignature( bodyDecoded );
2115
2116 // As nobody seems to know the purpose of the following line and
2117 // as it breaks word wrapping of long lines if drafts with attachments
2118 // are opened for editting in the composer (cf. Bug#41102) I comment it
2119 // out. Ingo, 2002-04-21
2120 //verifyWordWrapLengthIsAdequate(bodyDecoded);
2121
2122 const TQTextCodec *codec = KMMsgBase::codecForName(mCharset);
2123 if (codec)
2124 mEditor->setText(codec->toUnicode(bodyDecoded));
2125 else
2126 mEditor->setText(TQString::fromLocal8Bit(bodyDecoded));
2127 //mEditor->insertLine("\n", -1); <-- why ?
2128 } else mEditor->setText("");
2129 for( int i = firstAttachment; i < num; ++i )
2130 {
2131 KMMessagePart *msgPart = new KMMessagePart;
2132 mMsg->bodyPart(i, msgPart);
2133 TQCString mimeType = msgPart->typeStr().lower() + '/'
2134 + msgPart->subtypeStr().lower();
2135 // don't add the detached signature as attachment when editting a
2136 // PGP/MIME signed message
2137 if( mimeType != "application/pgp-signature" ) {
2138 addAttach(msgPart);
2139 }
2140 }
2141 } else{
2142 mCharset=mMsg->charset();
2143 if ( mCharset.isEmpty() || mCharset == "default" )
2144 mCharset = mDefCharset;
2145
2146 TQCString bodyDecoded = mMsg->bodyDecoded();
2147
2148 if( allowDecryption )
2149 decryptOrStripOffCleartextSignature( bodyDecoded );
2150
2151 const TQTextCodec *codec = KMMsgBase::codecForName(mCharset);
2152 if (codec) {
2153 mEditor->setText(codec->toUnicode(bodyDecoded));
2154 } else
2155 mEditor->setText(TQString::fromLocal8Bit(bodyDecoded));
2156 }
2157
2158 setCharset(mCharset);
2159#endif // BROKEN_FOR_OPAQUE_SIGNED_OR_ENCRYPTED_MAILS
2160
2161 if( (GlobalSettings::self()->autoTextSignature()=="auto") && mayAutoSign ) {
2162 //
2163 // Espen 2000-05-16
2164 // Delay the signature appending. It may start a fileseletor.
2165 // Not user friendy if this modal fileseletor opens before the
2166 // composer.
2167 //
2168 //TQTimer::singleShot( 200, this, TQ_SLOT(slotAppendSignature()) );
2169 if ( GlobalSettings::self()->prependSignature() ) {
2170 TQTimer::singleShot( 0, this, TQ_SLOT(slotPrependSignature()) );
2171 } else {
2172 TQTimer::singleShot( 0, this, TQ_SLOT(slotAppendSignature()) );
2173 }
2174 }
2175
2176 if ( mMsg->getCursorPos() > 0 ) {
2177 // The message has a cursor position explicitly set, so avoid
2178 // changing it when appending the signature.
2179 mPreserveUserCursorPosition = true;
2180 }
2181 setModified( isModified );
2182
2183 // do this even for new messages
2184 mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() );
2185
2186 // honor "keep reply in this folder" setting even when the identity is changed later on
2187 mPreventFccOverwrite = ( !newMsg->fcc().isEmpty() && ident.fcc() != newMsg->fcc() );
2188}
2189
2190
2191//-----------------------------------------------------------------------------
2192void KMComposeWin::setFcc( const TQString &idString )
2193{
2194 // check if the sent-mail folder still exists
2195 if ( ! idString.isEmpty() && kmkernel->findFolderById( idString ) ) {
2196 mFcc->setFolder( idString );
2197 } else {
2198 mFcc->setFolder( kmkernel->sentFolder() );
2199 }
2200}
2201
2202
2203//-----------------------------------------------------------------------------
2204bool KMComposeWin::isModified() const
2205{
2206 return ( mEditor->isModified() ||
2207 mEdtFrom->edited() ||
2208 ( mEdtReplyTo && mEdtReplyTo->edited() ) ||
2209 ( mEdtTo && mEdtTo->edited() ) ||
2210 ( mEdtCc && mEdtCc->edited() ) ||
2211 ( mEdtBcc && mEdtBcc->edited() ) ||
2212 ( mRecipientsEditor && mRecipientsEditor->isModified() ) ||
2213 mEdtSubject->edited() ||
2214 mAtmModified ||
2215 ( mTransport->lineEdit() && mTransport->lineEdit()->edited() ) );
2216}
2217
2218
2219//-----------------------------------------------------------------------------
2220void KMComposeWin::setModified( bool modified )
2221{
2222 mEditor->setModified( modified );
2223 if ( !modified ) {
2224 mEdtFrom->setEdited( false );
2225 if ( mEdtReplyTo ) mEdtReplyTo->setEdited( false );
2226 if ( mEdtTo ) mEdtTo->setEdited( false );
2227 if ( mEdtCc ) mEdtCc->setEdited( false );
2228 if ( mEdtBcc ) mEdtBcc->setEdited( false );
2229 if ( mRecipientsEditor ) mRecipientsEditor->clearModified();
2230 mEdtSubject->setEdited( false );
2231 mAtmModified = false ;
2232 if ( mTransport->lineEdit() )
2233 mTransport->lineEdit()->setEdited( false );
2234 }
2235}
2236
2237
2238//-----------------------------------------------------------------------------
2239bool KMComposeWin::queryClose ()
2240{
2241 if ( !mEditor->checkExternalEditorFinished() )
2242 return false;
2243 if ( kmkernel->shuttingDown() || tdeApp->sessionSaving() )
2244 return true;
2245 if ( mComposer && mComposer->isPerformingSignOperation() ) // since the non-gpg-agent gpg plugin gets a passphrase using TQDialog::exec()
2246 return false; // the user can try to close the window, which destroys mComposer mid-call.
2247
2248 if ( isModified() ) {
2249 bool istemplate = ( mFolder!=0 && mFolder->isTemplates() );
2250 const TQString savebut = ( istemplate ?
2251 i18n("Re&save as Template") :
2252 i18n("&Save as Draft") );
2253 const TQString savetext = ( istemplate ?
2254 i18n("Resave this message in the Templates folder. "
2255 "It can then be used at a later time.") :
2256 i18n("Save this message in the Drafts folder. "
2257 "It can then be edited and sent at a later time.") );
2258
2259 const int rc = KMessageBox::warningYesNoCancel( this,
2260 i18n("Do you want to save the message for later or discard it?"),
2261 i18n("Close Composer"),
2262 KGuiItem(savebut, "document-save", TQString(), savetext),
2263 KStdGuiItem::discard() );
2264 if ( rc == KMessageBox::Cancel )
2265 return false;
2266 else if ( rc == KMessageBox::Yes ) {
2267 // doSend will close the window. Just return false from this method
2268 if ( istemplate ) {
2269 slotSaveTemplate();
2270 } else {
2271 slotSaveDraft();
2272 }
2273 return false;
2274 }
2275 }
2276 cleanupAutoSave();
2277 return true;
2278}
2279
2280//-----------------------------------------------------------------------------
2281bool KMComposeWin::userForgotAttachment()
2282{
2283 bool checkForForgottenAttachments =
2284 mCheckForForgottenAttachments && GlobalSettings::self()->showForgottenAttachmentWarning();
2285
2286 if ( !checkForForgottenAttachments || ( mAtmList.count() > 0 ) )
2287 return false;
2288
2289
2290 TQStringList attachWordsList = GlobalSettings::self()->attachmentKeywords();
2291
2292 if ( attachWordsList.isEmpty() ) {
2293 // default value (FIXME: this is duplicated in configuredialog.cpp)
2294 attachWordsList << TQString::fromLatin1("attachment")
2295 << TQString::fromLatin1("attached");
2296 if ( TQString::fromLatin1("attachment") != i18n("attachment") )
2297 attachWordsList << i18n("attachment");
2298 if ( TQString::fromLatin1("attached") != i18n("attached") )
2299 attachWordsList << i18n("attached");
2300 }
2301
2302 TQRegExp rx ( TQString::fromLatin1("\\b") +
2303 attachWordsList.join("\\b|\\b") +
2304 TQString::fromLatin1("\\b") );
2305 rx.setCaseSensitive( false );
2306
2307 bool gotMatch = false;
2308
2309 // check whether the subject contains one of the attachment key words
2310 // unless the message is a reply or a forwarded message
2311 TQString subj = subject();
2312 gotMatch = ( KMMessage::stripOffPrefixes( subj ) == subj )
2313 && ( rx.search( subj ) >= 0 );
2314
2315 if ( !gotMatch ) {
2316 // check whether the non-quoted text contains one of the attachment key
2317 // words
2318 TQRegExp quotationRx ("^([ \\t]*([|>:}#]|[A-Za-z]+>))+");
2319 for ( int i = 0; i < mEditor->numLines(); ++i ) {
2320 TQString line = mEditor->textLine( i );
2321 gotMatch = ( quotationRx.search( line ) < 0 )
2322 && ( rx.search( line ) >= 0 );
2323 if ( gotMatch )
2324 break;
2325 }
2326 }
2327
2328 if ( !gotMatch )
2329 return false;
2330
2331 int rc = KMessageBox::warningYesNoCancel( this,
2332 i18n("The message you have composed seems to refer to an "
2333 "attached file but you have not attached anything.\n"
2334 "Do you want to attach a file to your message?"),
2335 i18n("File Attachment Reminder"),
2336 i18n("&Attach File..."),
2337 i18n("&Send as Is") );
2338 if ( rc == KMessageBox::Cancel )
2339 return true;
2340 if ( rc == KMessageBox::Yes ) {
2341 slotAttachFile();
2342 //preceed with editing
2343 return true;
2344 }
2345 return false;
2346}
2347
2348//-----------------------------------------------------------------------------
2349void KMComposeWin::applyChanges( bool dontSignNorEncrypt, bool dontDisable )
2350{
2351 kdDebug(5006) << "entering KMComposeWin::applyChanges" << endl;
2352
2353 if(!mMsg || mComposer) {
2354 kdDebug(5006) << "KMComposeWin::applyChanges() : mMsg == 0!\n" << endl;
2355 emit applyChangesDone( false );
2356 return;
2357 }
2358
2359 // Make new job and execute it
2360 mComposer = new MessageComposer( this );
2361 connect( mComposer, TQ_SIGNAL( done( bool ) ),
2362 this, TQ_SLOT( slotComposerDone( bool ) ) );
2363
2364 // TODO: Add a cancel button for the following operations?
2365 // Disable any input to the window, so that we have a snapshot of the
2366 // composed stuff
2367 if ( !dontDisable ) setEnabled( false );
2368 // apply the current state to the composer and let it do it's thing
2369 mComposer->setDisableBreaking( mDisableBreaking ); // FIXME
2370 mComposer->applyChanges( dontSignNorEncrypt );
2371}
2372
2373void KMComposeWin::slotComposerDone( bool rc )
2374{
2375 deleteAll( mComposedMessages );
2376 mComposedMessages = mComposer->composedMessageList();
2377 emit applyChangesDone( rc );
2378 delete mComposer;
2379 mComposer = 0;
2380
2381 // re-enable the composewin, the messsage composition is now done
2382 setEnabled( true );
2383}
2384
2385const KPIM::Identity & KMComposeWin::identity() const {
2386 return kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
2387}
2388
2389uint KMComposeWin::identityUid() const {
2390 return mIdentity->currentIdentity();
2391}
2392
2393Kleo::CryptoMessageFormat KMComposeWin::cryptoMessageFormat() const {
2394 if ( !mCryptoModuleAction )
2395 return Kleo::AutoFormat;
2396 return cb2format( mCryptoModuleAction->currentItem() );
2397}
2398
2399bool KMComposeWin::encryptToSelf() const {
2400// return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
2401 TDEConfigGroup group( KMKernel::config(), "Composer" );
2402 return group.readBoolEntry( "crypto-encrypt-to-self", true );
2403}
2404
2405bool KMComposeWin::queryExit ()
2406{
2407 return true;
2408}
2409
2410//-----------------------------------------------------------------------------
2411bool KMComposeWin::addAttach(const KURL aUrl)
2412{
2413 if ( !aUrl.isValid() ) {
2414 KMessageBox::sorry( this, i18n( "<qt><p>KMail could not recognize the location of the attachment (%1);</p>"
2415 "<p>you have to specify the full path if you wish to attach a file.</p></qt>" )
2416 .arg( aUrl.prettyURL() ) );
2417 return false;
2418 }
2419
2420 const int maxAttachmentSize = GlobalSettings::maximumAttachmentSize();
2421 const uint maximumAttachmentSizeInByte = maxAttachmentSize*1024*1024;
2422 if ( aUrl.isLocalFile() && TQFileInfo( aUrl.pathOrURL() ).size() > maximumAttachmentSizeInByte ) {
2423 KMessageBox::sorry( this, i18n( "<qt><p>Your administrator has disallowed attaching files bigger than %1 MB.</p>" ).arg( maxAttachmentSize ) );
2424 return false;
2425 }
2426
2427 TDEIO::TransferJob *job = TDEIO::get(aUrl);
2428 TDEIO::Scheduler::scheduleJob( job );
2429 atmLoadData ld;
2430 ld.url = aUrl;
2431 ld.data = TQByteArray();
2432 ld.insert = false;
2433 if( !aUrl.fileEncoding().isEmpty() )
2434 ld.encoding = aUrl.fileEncoding().latin1();
2435
2436 mMapAtmLoadData.insert(job, ld);
2437 mAttachJobs[job] = aUrl;
2438 connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
2439 this, TQ_SLOT(slotAttachFileResult(TDEIO::Job *)));
2440 connect(job, TQ_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
2441 this, TQ_SLOT(slotAttachFileData(TDEIO::Job *, const TQByteArray &)));
2442 return true;
2443}
2444
2445
2446//-----------------------------------------------------------------------------
2447void KMComposeWin::addAttach(const KMMessagePart* msgPart)
2448{
2449 mAtmList.append(msgPart);
2450
2451 // show the attachment listbox if it does not up to now
2452 if (mAtmList.count()==1)
2453 {
2454 mAtmListView->resize(mAtmListView->width(), 50);
2455 mAtmListView->show();
2456 resize(size());
2457 }
2458
2459 // add a line in the attachment listbox
2460 KMAtmListViewItem *lvi = new KMAtmListViewItem( mAtmListView );
2461 msgPartToItem(msgPart, lvi);
2462 mAtmItemList.append(lvi);
2463
2464 // the Attach file job has finished, so the possibly present tmp dir can be deleted now.
2465 if ( mTempDir != 0 ) {
2466 delete mTempDir;
2467 mTempDir = 0;
2468 }
2469
2470 connect( lvi, TQ_SIGNAL( compress( int ) ),
2471 this, TQ_SLOT( compressAttach( int ) ) );
2472 connect( lvi, TQ_SIGNAL( uncompress( int ) ),
2473 this, TQ_SLOT( uncompressAttach( int ) ) );
2474
2475 slotUpdateAttachActions();
2476}
2477
2478
2479//-----------------------------------------------------------------------------
2480void KMComposeWin::slotUpdateAttachActions()
2481{
2482 int selectedCount = 0;
2483 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it ) {
2484 if ( (*it)->isSelected() ) {
2485 ++selectedCount;
2486 }
2487 }
2488
2489 mAttachRemoveAction->setEnabled( selectedCount >= 1 );
2490 mAttachSaveAction->setEnabled( selectedCount == 1 );
2491 mAttachPropertiesAction->setEnabled( selectedCount == 1 );
2492}
2493
2494
2495//-----------------------------------------------------------------------------
2496
2497TQString KMComposeWin::prettyMimeType( const TQString& type )
2498{
2499 TQString t = type.lower();
2500 KServiceType::Ptr st = KServiceType::serviceType( t );
2501 return st ? st->comment() : t;
2502}
2503
2504void KMComposeWin::msgPartToItem(const KMMessagePart* msgPart,
2505 KMAtmListViewItem *lvi, bool loadDefaults)
2506{
2507 assert(msgPart != 0);
2508
2509 if (!msgPart->fileName().isEmpty())
2510 lvi->setText(0, msgPart->fileName());
2511 else
2512 lvi->setText(0, msgPart->name());
2513 lvi->setText(1, TDEIO::convertSize( msgPart->decodedSize()));
2514 lvi->setText(2, msgPart->contentTransferEncodingStr());
2515 lvi->setText(3, prettyMimeType(msgPart->typeStr() + "/" + msgPart->subtypeStr()));
2516 lvi->setAttachmentSize(msgPart->decodedSize());
2517
2518 if ( loadDefaults ) {
2519 if( canSignEncryptAttachments() ) {
2520 lvi->enableCryptoCBs( true );
2521 lvi->setEncrypt( mEncryptAction->isChecked() );
2522 lvi->setSign( mSignAction->isChecked() );
2523 } else {
2524 lvi->enableCryptoCBs( false );
2525 }
2526 }
2527}
2528
2529
2530//-----------------------------------------------------------------------------
2531void KMComposeWin::removeAttach(const TQString &aUrl)
2532{
2533 int idx;
2534 KMMessagePart* msgPart;
2535 for(idx=0,msgPart=mAtmList.first(); msgPart;
2536 msgPart=mAtmList.next(),idx++) {
2537 if (msgPart->name() == aUrl) {
2538 removeAttach(idx);
2539 return;
2540 }
2541 }
2542}
2543
2544
2545//-----------------------------------------------------------------------------
2546void KMComposeWin::removeAttach(int idx)
2547{
2548 mAtmModified = true;
2549
2550 KMAtmListViewItem *item = static_cast<KMAtmListViewItem*>( mAtmItemList.at( idx ) );
2551 if ( item->itemBelow() )
2552 mAtmSelectNew = item->itemBelow();
2553 else if ( item->itemAbove() )
2554 mAtmSelectNew = item->itemAbove();
2555
2556 mAtmList.remove(idx);
2557 delete mAtmItemList.take(idx);
2558
2559 if( mAtmList.isEmpty() )
2560 {
2561 mAtmListView->hide();
2562 mAtmListView->setMinimumSize(0, 0);
2563 resize(size());
2564 }
2565}
2566
2567
2568//-----------------------------------------------------------------------------
2569bool KMComposeWin::encryptFlagOfAttachment(int idx)
2570{
2571 return (int)(mAtmItemList.count()) > idx
2572 ? static_cast<KMAtmListViewItem*>( mAtmItemList.at( idx ) )->isEncrypt()
2573 : false;
2574}
2575
2576
2577//-----------------------------------------------------------------------------
2578bool KMComposeWin::signFlagOfAttachment(int idx)
2579{
2580 return (int)(mAtmItemList.count()) > idx
2581 ? ((KMAtmListViewItem*)(mAtmItemList.at( idx )))->isSign()
2582 : false;
2583}
2584
2585
2586//-----------------------------------------------------------------------------
2587void KMComposeWin::addrBookSelInto()
2588{
2589 if ( mClassicalRecipients ) {
2590 if ( GlobalSettings::self()->addresseeSelectorType() ==
2591 GlobalSettings::EnumAddresseeSelectorType::New ) {
2592 addrBookSelIntoNew();
2593 } else {
2594 addrBookSelIntoOld();
2595 }
2596 } else {
2597 kdWarning() << "To be implemented: call recipients picker." << endl;
2598 }
2599}
2600
2601void KMComposeWin::addrBookSelIntoOld()
2602{
2603 AddressesDialog dlg( this );
2604 TQString txt;
2605 TQStringList lst;
2606
2607 txt = to();
2608 if ( !txt.isEmpty() ) {
2609 lst = KPIM::splitEmailAddrList( txt );
2610 dlg.setSelectedTo( lst );
2611 }
2612
2613 txt = mEdtCc->text();
2614 if ( !txt.isEmpty() ) {
2615 lst = KPIM::splitEmailAddrList( txt );
2616 dlg.setSelectedCC( lst );
2617 }
2618
2619 txt = mEdtBcc->text();
2620 if ( !txt.isEmpty() ) {
2621 lst = KPIM::splitEmailAddrList( txt );
2622 dlg.setSelectedBCC( lst );
2623 }
2624
2625 dlg.setRecentAddresses( RecentAddresses::self( KMKernel::config() )->tdeabcAddresses() );
2626
2627 if (dlg.exec()==TQDialog::Rejected) return;
2628
2629 mEdtTo->setText( dlg.to().join(", ") );
2630 mEdtTo->setEdited( true );
2631
2632 mEdtCc->setText( dlg.cc().join(", ") );
2633 mEdtCc->setEdited( true );
2634
2635 mEdtBcc->setText( dlg.bcc().join(", ") );
2636 mEdtBcc->setEdited( true );
2637
2638 //Make sure BCC field is shown if needed
2639 if ( !mEdtBcc->text().isEmpty() ) {
2640 mShowHeaders |= HDR_BCC;
2641 rethinkFields( false );
2642 }
2643}
2644
2645void KMComposeWin::addrBookSelIntoNew()
2646{
2647 AddresseeEmailSelection selection;
2648
2649 AddresseeSelectorDialog dlg( &selection );
2650
2651 TQString txt;
2652 TQStringList lst;
2653
2654 txt = to();
2655 if ( !txt.isEmpty() ) {
2656 lst = KPIM::splitEmailAddrList( txt );
2657 selection.setSelectedTo( lst );
2658 }
2659
2660 txt = mEdtCc->text();
2661 if ( !txt.isEmpty() ) {
2662 lst = KPIM::splitEmailAddrList( txt );
2663 selection.setSelectedCC( lst );
2664 }
2665
2666 txt = mEdtBcc->text();
2667 if ( !txt.isEmpty() ) {
2668 lst = KPIM::splitEmailAddrList( txt );
2669 selection.setSelectedBCC( lst );
2670 }
2671
2672 if (dlg.exec()==TQDialog::Rejected) return;
2673
2674 TQStringList list = selection.to() + selection.toDistributionLists();
2675 mEdtTo->setText( list.join(", ") );
2676 mEdtTo->setEdited( true );
2677
2678 list = selection.cc() + selection.ccDistributionLists();
2679 mEdtCc->setText( list.join(", ") );
2680 mEdtCc->setEdited( true );
2681
2682 list = selection.bcc() + selection.bccDistributionLists();
2683 mEdtBcc->setText( list.join(", ") );
2684 mEdtBcc->setEdited( true );
2685
2686 //Make sure BCC field is shown if needed
2687 if ( !mEdtBcc->text().isEmpty() ) {
2688 mShowHeaders |= HDR_BCC;
2689 rethinkFields( false );
2690 }
2691}
2692
2693
2694//-----------------------------------------------------------------------------
2695void KMComposeWin::setCharset(const TQCString& aCharset, bool forceDefault)
2696{
2697 if ((forceDefault && GlobalSettings::self()->forceReplyCharset()) || aCharset.isEmpty())
2698 mCharset = mDefCharset;
2699 else
2700 mCharset = aCharset.lower();
2701
2702 if ( mCharset.isEmpty() || mCharset == "default" )
2703 mCharset = mDefCharset;
2704
2705 if (mAutoCharset)
2706 {
2707 mEncodingAction->setCurrentItem( 0 );
2708 return;
2709 }
2710
2711 TQStringList encodings = mEncodingAction->items();
2712 int i = 0;
2713 bool charsetFound = false;
2714 for ( TQStringList::Iterator it = encodings.begin(); it != encodings.end();
2715 ++it, i++ )
2716 {
2717 if (i > 0 && ((mCharset == "us-ascii" && i == 1) ||
2718 (i != 1 && TDEGlobal::charsets()->codecForName(
2719 TDEGlobal::charsets()->encodingForName(*it))
2720 == TDEGlobal::charsets()->codecForName(mCharset))))
2721 {
2722 mEncodingAction->setCurrentItem( i );
2723 slotSetCharset();
2724 charsetFound = true;
2725 break;
2726 }
2727 }
2728 if (!aCharset.isEmpty() && !charsetFound) setCharset("", true);
2729}
2730
2731
2732//-----------------------------------------------------------------------------
2733void KMComposeWin::slotAddrBook()
2734{
2735 KAddrBookExternal::openAddressBook(this);
2736}
2737
2738
2739//-----------------------------------------------------------------------------
2740void KMComposeWin::slotAddrBookFrom()
2741{
2742 addrBookSelInto();
2743}
2744
2745
2746//-----------------------------------------------------------------------------
2747void KMComposeWin::slotAddrBookReplyTo()
2748{
2749 addrBookSelInto();
2750}
2751
2752
2753//-----------------------------------------------------------------------------
2754void KMComposeWin::slotAddrBookTo()
2755{
2756 addrBookSelInto();
2757}
2758
2759//-----------------------------------------------------------------------------
2760void KMComposeWin::slotAttachFile()
2761{
2762 // Create File Dialog and return selected file(s)
2763 // We will not care about any permissions, existence or whatsoever in
2764 // this function.
2765
2766 // Handle the case where the last savedir is gone. kolab/issue4057
2767 TQString recent;
2768 KURL recentURL = KFileDialog::getStartURL( TQString(), recent );
2769 if ( !recentURL.url().isEmpty() &&
2770 !TDEIO::NetAccess::exists( recentURL, true, this ) ) {
2771 recentURL = KURL( TQDir::homeDirPath() );
2772 }
2773
2774 KFileDialog fdlg( recentURL.url(), TQString(), this, 0, true );
2775 fdlg.setOperationMode( KFileDialog::Other );
2776 fdlg.setCaption( i18n( "Attach File" ) );
2777 fdlg.okButton()->setGuiItem( KGuiItem( i18n( "&Attach" ),"document-open" ) );
2778 fdlg.setMode( KFile::Files );
2779 fdlg.exec();
2780 KURL::List files = fdlg.selectedURLs();
2781
2782 for (KURL::List::Iterator it = files.begin(); it != files.end(); ++it)
2783 addAttach(*it);
2784}
2785
2786
2787//-----------------------------------------------------------------------------
2788void KMComposeWin::slotAttachFileData(TDEIO::Job *job, const TQByteArray &data)
2789{
2790 TQMap<TDEIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
2791 assert(it != mMapAtmLoadData.end());
2792 TQBuffer buff((*it).data);
2793 buff.open(IO_WriteOnly | IO_Append);
2794 buff.writeBlock(data.data(), data.size());
2795 buff.close();
2796}
2797
2798
2799//-----------------------------------------------------------------------------
2800void KMComposeWin::slotAttachFileResult(TDEIO::Job *job)
2801{
2802 TQMap<TDEIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
2803 assert(it != mMapAtmLoadData.end());
2804 KURL attachURL;
2805 TQMap<TDEIO::Job*, KURL>::iterator jit = mAttachJobs.find(job);
2806 bool attachURLfound = (jit != mAttachJobs.end());
2807 if (attachURLfound)
2808 {
2809 attachURL = jit.data();
2810 mAttachJobs.remove(jit);
2811 }
2812 if (job->error())
2813 {
2814 mMapAtmLoadData.remove(it);
2815 job->showErrorDialog();
2816 if (attachURLfound)
2817 emit attachmentAdded(attachURL, false);
2818 return;
2819 }
2820 if ((*it).insert)
2821 {
2822 (*it).data.resize((*it).data.size() + 1);
2823 (*it).data[(*it).data.size() - 1] = '\0';
2824 if ( const TQTextCodec * codec = TDEGlobal::charsets()->codecForName((*it).encoding) )
2825 mEditor->insert( codec->toUnicode( (*it).data ) );
2826 else
2827 mEditor->insert( TQString::fromLocal8Bit( (*it).data ) );
2828 mMapAtmLoadData.remove(it);
2829 if (attachURLfound)
2830 emit attachmentAdded(attachURL, true);
2831 return;
2832 }
2833 TQCString partCharset;
2834 if ( !( *it ).url.fileEncoding().isEmpty() ) {
2835 partCharset = TQCString( ( *it ).url.fileEncoding().latin1() );
2836 } else {
2838 TDELocale *loc = TDEGlobal::locale();
2839 ed.setAutoDetectLanguage( EncodingDetector::scriptForLanguageCode ( loc->language() ) );
2840 ed.analyze( (*it).data );
2841 partCharset = ed.encoding();
2842 if ( partCharset.isEmpty() ) //shouldn't happen
2843 partCharset = mCharset;
2844 }
2845
2846 KMMessagePart* msgPart;
2847
2848 KCursorSaver busy(KBusyPtr::busy());
2849 TQString name( (*it).url.fileName() );
2850 // ask the job for the mime type of the file
2851 TQString mimeType = static_cast<TDEIO::MimetypeJob*>(job)->mimetype();
2852
2853 if ( name.isEmpty() ) {
2854 // URL ends with '/' (e.g. http://www.kde.org/)
2855 // guess a reasonable filename
2856 if( mimeType == "text/html" )
2857 name = "index.html";
2858 else {
2859 // try to determine a reasonable extension
2860 TQStringList patterns( KMimeType::mimeType( mimeType )->patterns() );
2861 TQString ext;
2862 if( !patterns.isEmpty() ) {
2863 ext = patterns[0];
2864 int i = ext.findRev( '.' );
2865 if( i == -1 )
2866 ext.prepend( '.' );
2867 else if( i > 0 )
2868 ext = ext.mid( i );
2869 }
2870 name = TQString("unknown") += ext;
2871 }
2872 }
2873
2874 name.truncate( 256 ); // is this needed?
2875
2876 TQCString encoding = KMMsgBase::autoDetectCharset(partCharset,
2878 if ( encoding.isEmpty() )
2879 encoding = "utf-8";
2880
2881 TQCString encName;
2882 if ( GlobalSettings::self()->outlookCompatibleAttachments() )
2883 encName = KMMsgBase::encodeRFC2047String( name, encoding );
2884 else
2885 encName = KMMsgBase::encodeRFC2231String( name, encoding );
2886 bool RFC2231encoded = false;
2887 if ( !GlobalSettings::self()->outlookCompatibleAttachments() )
2888 RFC2231encoded = name != TQString( encName );
2889
2890 // create message part
2891 msgPart = new KMMessagePart;
2892 msgPart->setName(name);
2893 TQValueList<int> allowedCTEs;
2894 if ( mimeType == "message/rfc822" ) {
2895 msgPart->setMessageBody( (*it).data );
2896 allowedCTEs << DwMime::kCte7bit;
2897 allowedCTEs << DwMime::kCte8bit;
2898 } else {
2899 msgPart->setBodyAndGuessCte((*it).data, allowedCTEs,
2900 !kmkernel->msgSender()->sendQuotedPrintable());
2901 kdDebug(5006) << "autodetected cte: " << msgPart->cteStr() << endl;
2902 }
2903 int slash = mimeType.find( '/' );
2904 if( slash == -1 )
2905 slash = mimeType.length();
2906 msgPart->setTypeStr( mimeType.left( slash ).latin1() );
2907 msgPart->setSubtypeStr( mimeType.mid( slash + 1 ).latin1() );
2908 msgPart->setContentDisposition(TQCString("attachment;\n\tfilename")
2909 + ( RFC2231encoded ? "*=" + encName : "=\"" + encName + '"' ) );
2910
2911 mMapAtmLoadData.remove(it);
2912
2913 if ( msgPart->typeStr().lower() == "text" ) {
2914 msgPart->setCharset(partCharset);
2915 }
2916
2917 // show message part dialog, if not configured away (default):
2918 TDEConfigGroup composer(KMKernel::config(), "Composer");
2919 if ( GlobalSettings::self()->showMessagePartDialogOnAttach() ) {
2920 const KCursorSaver saver( TQCursor::ArrowCursor );
2921 KMMsgPartDialogCompat dlg(mMainWidget);
2922 int encodings = 0;
2923 for ( TQValueListConstIterator<int> it = allowedCTEs.begin() ;
2924 it != allowedCTEs.end() ; ++it )
2925 switch ( *it ) {
2926 case DwMime::kCteBase64: encodings |= KMMsgPartDialog::Base64; break;
2927 case DwMime::kCteQp: encodings |= KMMsgPartDialog::QuotedPrintable; break;
2928 case DwMime::kCte7bit: encodings |= KMMsgPartDialog::SevenBit; break;
2929 case DwMime::kCte8bit: encodings |= KMMsgPartDialog::EightBit; break;
2930 default: ;
2931 }
2932 dlg.setShownEncodings( encodings );
2933 dlg.setMsgPart(msgPart);
2934 if (!dlg.exec()) {
2935 delete msgPart;
2936 msgPart = 0;
2937 if (attachURLfound)
2938 emit attachmentAdded(attachURL, false);
2939 return;
2940 }
2941 }
2942 mAtmModified = true;
2943
2944 // add the new attachment to the list
2945 addAttach(msgPart);
2946
2947 if (attachURLfound)
2948 emit attachmentAdded(attachURL, true);
2949}
2950
2951
2952//-----------------------------------------------------------------------------
2953void KMComposeWin::slotInsertFile()
2954{
2955 KFileDialog fdlg(TQString(), TQString(), this, 0, true);
2956 fdlg.setOperationMode( KFileDialog::Opening );
2957 fdlg.okButton()->setText(i18n("&Insert"));
2958 fdlg.setCaption(i18n("Insert File"));
2959 fdlg.toolBar()->insertCombo(KMMsgBase::supportedEncodings(false), 4711,
2960 false, 0, 0, 0);
2961 KComboBox *combo = fdlg.toolBar()->getCombo(4711);
2962 for (int i = 0; i < combo->count(); i++)
2963 if (TDEGlobal::charsets()->codecForName(TDEGlobal::charsets()->
2964 encodingForName(combo->text(i)))
2965 == TQTextCodec::codecForLocale()) combo->setCurrentItem(i);
2966 if (!fdlg.exec()) return;
2967
2968 KURL u = fdlg.selectedURL();
2969 mRecentAction->addURL(u);
2970 // Prevent race condition updating list when multiple composers are open
2971 {
2972 TDEConfig *config = KMKernel::config();
2973 TDEConfigGroupSaver saver( config, "Composer" );
2974 TQString encoding = TDEGlobal::charsets()->encodingForName(combo->currentText()).latin1();
2975 TQStringList urls = config->readListEntry( "recent-urls" );
2976 TQStringList encodings = config->readListEntry( "recent-encodings" );
2977 // Prevent config file from growing without bound
2978 // Would be nicer to get this constant from TDERecentFilesAction
2979 uint mMaxRecentFiles = 30;
2980 while (urls.count() > mMaxRecentFiles)
2981 urls.erase( urls.fromLast() );
2982 while (encodings.count() > mMaxRecentFiles)
2983 encodings.erase( encodings.fromLast() );
2984 // sanity check
2985 if (urls.count() != encodings.count()) {
2986 urls.clear();
2987 encodings.clear();
2988 }
2989 urls.prepend( u.prettyURL() );
2990 encodings.prepend( encoding );
2991 config->writeEntry( "recent-urls", urls );
2992 config->writeEntry( "recent-encodings", encodings );
2993 mRecentAction->saveEntries( config );
2994 }
2995 slotInsertRecentFile(u);
2996}
2997
2998
2999//-----------------------------------------------------------------------------
3000void KMComposeWin::slotInsertRecentFile(const KURL& u)
3001{
3002 if (u.fileName().isEmpty()) return;
3003
3004 TDEIO::Job *job = TDEIO::get(u);
3005 atmLoadData ld;
3006 ld.url = u;
3007 ld.data = TQByteArray();
3008 ld.insert = true;
3009 // Get the encoding previously used when inserting this file
3010 {
3011 TDEConfig *config = KMKernel::config();
3012 TDEConfigGroupSaver saver( config, "Composer" );
3013 TQStringList urls = config->readListEntry( "recent-urls" );
3014 TQStringList encodings = config->readListEntry( "recent-encodings" );
3015 int index = urls.findIndex( u.prettyURL() );
3016 if (index != -1) {
3017 TQString encoding = encodings[ index ];
3018 ld.encoding = encoding.latin1();
3019 }
3020 }
3021 mMapAtmLoadData.insert(job, ld);
3022 connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
3023 this, TQ_SLOT(slotAttachFileResult(TDEIO::Job *)));
3024 connect(job, TQ_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
3025 this, TQ_SLOT(slotAttachFileData(TDEIO::Job *, const TQByteArray &)));
3026}
3027
3028
3029//-----------------------------------------------------------------------------
3030void KMComposeWin::slotSetCharset()
3031{
3032 if (mEncodingAction->currentItem() == 0)
3033 {
3034 mAutoCharset = true;
3035 return;
3036 }
3037 mAutoCharset = false;
3038
3039 mCharset = TDEGlobal::charsets()->encodingForName( mEncodingAction->
3040 currentText() ).latin1();
3041}
3042
3043
3044//-----------------------------------------------------------------------------
3045void KMComposeWin::slotSelectCryptoModule( bool init )
3046{
3047 if ( !init ) {
3048 setModified( true );
3049 }
3050 if( canSignEncryptAttachments() ) {
3051 // if the encrypt/sign columns are hidden then show them
3052 if( 0 == mAtmListView->columnWidth( mAtmColEncrypt ) ) {
3053 // set/unset signing/encryption for all attachments according to the
3054 // state of the global sign/encrypt action
3055 if( !mAtmList.isEmpty() ) {
3056 for( KMAtmListViewItem* lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
3057 lvi;
3058 lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) ) {
3059 lvi->setSign( mSignAction->isChecked() );
3060 lvi->setEncrypt( mEncryptAction->isChecked() );
3061 }
3062 }
3063 int totalWidth = 0;
3064 // determine the total width of the columns
3065 for( int col=0; col < mAtmColEncrypt; col++ )
3066 totalWidth += mAtmListView->columnWidth( col );
3067 int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
3068 - mAtmSignColWidth;
3069 // reduce the width of all columns so that the encrypt and sign column
3070 // fit
3071 int usedWidth = 0;
3072 for( int col=0; col < mAtmColEncrypt-1; col++ ) {
3073 int newWidth = mAtmListView->columnWidth( col ) * reducedTotalWidth
3074 / totalWidth;
3075 mAtmListView->setColumnWidth( col, newWidth );
3076 usedWidth += newWidth;
3077 }
3078 // the last column before the encrypt column gets the remaining space
3079 // (because of rounding errors the width of this column isn't calculated
3080 // the same way as the width of the other columns)
3081 mAtmListView->setColumnWidth( mAtmColEncrypt-1,
3082 reducedTotalWidth - usedWidth );
3083 mAtmListView->setColumnWidth( mAtmColEncrypt, mAtmEncryptColWidth );
3084 mAtmListView->setColumnWidth( mAtmColSign, mAtmSignColWidth );
3085 for( KMAtmListViewItem* lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
3086 lvi;
3087 lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) ) {
3088 lvi->enableCryptoCBs( true );
3089 }
3090 }
3091 } else {
3092 // if the encrypt/sign columns are visible then hide them
3093 if( 0 != mAtmListView->columnWidth( mAtmColEncrypt ) ) {
3094 mAtmEncryptColWidth = mAtmListView->columnWidth( mAtmColEncrypt );
3095 mAtmSignColWidth = mAtmListView->columnWidth( mAtmColSign );
3096 int totalWidth = 0;
3097 // determine the total width of the columns
3098 for( int col=0; col < mAtmListView->columns(); col++ )
3099 totalWidth += mAtmListView->columnWidth( col );
3100 int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
3101 - mAtmSignColWidth;
3102 // increase the width of all columns so that the visible columns take
3103 // up the whole space
3104 int usedWidth = 0;
3105 for( int col=0; col < mAtmColEncrypt-1; col++ ) {
3106 int newWidth = mAtmListView->columnWidth( col ) * totalWidth
3107 / reducedTotalWidth;
3108 mAtmListView->setColumnWidth( col, newWidth );
3109 usedWidth += newWidth;
3110 }
3111 // the last column before the encrypt column gets the remaining space
3112 // (because of rounding errors the width of this column isn't calculated
3113 // the same way as the width of the other columns)
3114 mAtmListView->setColumnWidth( mAtmColEncrypt-1, totalWidth - usedWidth );
3115 mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
3116 mAtmListView->setColumnWidth( mAtmColSign, 0 );
3117 for( KMAtmListViewItem* lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
3118 lvi;
3119 lvi = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) ) {
3120 lvi->enableCryptoCBs( false );
3121 }
3122 }
3123 }
3124}
3125
3126static void showExportError( TQWidget * w, const GpgME::Error & err ) {
3127 assert( err );
3128 const TQString msg = i18n("<qt><p>An error occurred while trying to export "
3129 "the key from the backend:</p>"
3130 "<p><b>%1</b></p></qt>")
3131 .arg( TQString::fromLocal8Bit( err.asString() ) );
3132 KMessageBox::error( w, msg, i18n("Key Export Failed") );
3133}
3134
3135
3136//-----------------------------------------------------------------------------
3137void KMComposeWin::slotInsertMyPublicKey()
3138{
3139 // get PGP user id for the chosen identity
3140 mFingerprint =
3141 kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() ).pgpEncryptionKey();
3142 if ( !mFingerprint.isEmpty() )
3143 startPublicKeyExport();
3144}
3145
3146void KMComposeWin::startPublicKeyExport() {
3147 if ( mFingerprint.isEmpty() || !Kleo::CryptoBackendFactory::instance()->openpgp() )
3148 return;
3149 Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->openpgp()->publicKeyExportJob( true );
3150 assert( job );
3151
3152 connect( job, TQ_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
3153 this, TQ_SLOT(slotPublicKeyExportResult(const GpgME::Error&,const TQByteArray&)) );
3154
3155 const GpgME::Error err = job->start( mFingerprint );
3156 if ( err )
3157 showExportError( this, err );
3158 else
3159 (void)new Kleo::ProgressDialog( job, i18n("Exporting key..."), this );
3160}
3161
3162void KMComposeWin::slotPublicKeyExportResult( const GpgME::Error & err, const TQByteArray & keydata ) {
3163 if ( err ) {
3164 showExportError( this, err );
3165 return;
3166 }
3167
3168 // create message part
3169 KMMessagePart * msgPart = new KMMessagePart();
3170 msgPart->setName( i18n("OpenPGP key 0x%1").arg( mFingerprint ) );
3171 msgPart->setTypeStr("application");
3172 msgPart->setSubtypeStr("pgp-keys");
3173 TQValueList<int> dummy;
3174 msgPart->setBodyAndGuessCte(keydata, dummy, false);
3175 msgPart->setContentDisposition( "attachment;\n\tfilename=0x" + TQCString( mFingerprint.latin1() ) + ".asc" );
3176
3177 // add the new attachment to the list
3178 addAttach(msgPart);
3179 rethinkFields(); //work around initial-size bug in TQt-1.32
3180}
3181
3182//-----------------------------------------------------------------------------
3183void KMComposeWin::slotInsertPublicKey()
3184{
3185 Kleo::KeySelectionDialog dlg( i18n("Attach Public OpenPGP Key"),
3186 i18n("Select the public key which should "
3187 "be attached."),
3188 std::vector<GpgME::Key>(),
3189 Kleo::KeySelectionDialog::PublicKeys|Kleo::KeySelectionDialog::OpenPGPKeys,
3190 false /* no multi selection */,
3191 false /* no remember choice box */,
3192 this, "attach public key selection dialog" );
3193 if ( dlg.exec() != TQDialog::Accepted )
3194 return;
3195
3196 mFingerprint = dlg.fingerprint();
3197 startPublicKeyExport();
3198}
3199
3200
3201//-----------------------------------------------------------------------------
3202void KMComposeWin::slotAttachPopupMenu(TQListViewItem *, const TQPoint &, int)
3203{
3204 if (!mAttachMenu)
3205 {
3206 mAttachMenu = new TQPopupMenu(this);
3207
3208 mOpenId = mAttachMenu->insertItem(i18n("to open", "Open"), this,
3209 TQ_SLOT(slotAttachOpen()));
3210 mOpenWithId = mAttachMenu->insertItem(i18n("Open With..."), this,
3211 TQ_SLOT(slotAttachOpenWith()));
3212 mViewId = mAttachMenu->insertItem(i18n("to view", "View"), this,
3213 TQ_SLOT(slotAttachView()));
3214 mEditId = mAttachMenu->insertItem( i18n("Edit"), this, TQ_SLOT(slotAttachEdit()) );
3215 mEditWithId = mAttachMenu->insertItem( i18n("Edit With..."), this,
3216 TQ_SLOT(slotAttachEditWith()) );
3217 mRemoveId = mAttachMenu->insertItem(i18n("Remove"), this, TQ_SLOT(slotAttachRemove()));
3218 mSaveAsId = mAttachMenu->insertItem( SmallIconSet("document-save-as"), i18n("Save As..."), this,
3219 TQ_SLOT( slotAttachSave() ) );
3220 mPropertiesId = mAttachMenu->insertItem( i18n("Properties"), this,
3221 TQ_SLOT( slotAttachProperties() ) );
3222 mAttachMenu->insertSeparator();
3223 mAttachMenu->insertItem(i18n("Add Attachment..."), this, TQ_SLOT(slotAttachFile()));
3224 }
3225
3226 int selectedCount = 0;
3227 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it ) {
3228 if ( (*it)->isSelected() ) {
3229 ++selectedCount;
3230 }
3231 }
3232
3233 mAttachMenu->setItemEnabled( mOpenId, selectedCount > 0 );
3234 mAttachMenu->setItemEnabled( mOpenWithId, selectedCount > 0 );
3235 mAttachMenu->setItemEnabled( mViewId, selectedCount > 0 );
3236 mAttachMenu->setItemEnabled( mEditId, selectedCount == 1 );
3237 mAttachMenu->setItemEnabled( mEditWithId, selectedCount == 1 );
3238 mAttachMenu->setItemEnabled( mRemoveId, selectedCount > 0 );
3239 mAttachMenu->setItemEnabled( mSaveAsId, selectedCount == 1 );
3240 mAttachMenu->setItemEnabled( mPropertiesId, selectedCount == 1 );
3241
3242 mAttachMenu->popup(TQCursor::pos());
3243}
3244
3245//-----------------------------------------------------------------------------
3246int KMComposeWin::currentAttachmentNum()
3247{
3248 int i = 0;
3249 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it, ++i )
3250 if ( *it == mAtmListView->currentItem() )
3251 return i;
3252 return -1;
3253}
3254
3255//-----------------------------------------------------------------------------
3256void KMComposeWin::slotAttachProperties()
3257{
3258 int idx = currentAttachmentNum();
3259
3260 if (idx < 0) return;
3261
3262 KMMessagePart* msgPart = mAtmList.at(idx);
3263
3264 KMMsgPartDialogCompat dlg(mMainWidget);
3265 dlg.setMsgPart(msgPart);
3266 KMAtmListViewItem* listItem = (KMAtmListViewItem*)(mAtmItemList.at(idx));
3267 if( canSignEncryptAttachments() && listItem ) {
3268 dlg.setCanSign( true );
3269 dlg.setCanEncrypt( true );
3270 dlg.setSigned( listItem->isSign() );
3271 dlg.setEncrypted( listItem->isEncrypt() );
3272 } else {
3273 dlg.setCanSign( false );
3274 dlg.setCanEncrypt( false );
3275 }
3276 if (dlg.exec())
3277 {
3278 mAtmModified = true;
3279 // values may have changed, so recreate the listbox line
3280 if( listItem ) {
3281 msgPartToItem(msgPart, listItem);
3282 if( canSignEncryptAttachments() ) {
3283 listItem->setSign( dlg.isSigned() );
3284 listItem->setEncrypt( dlg.isEncrypted() );
3285 }
3286 }
3287 }
3288}
3289
3290//-----------------------------------------------------------------------------
3291void KMComposeWin::compressAttach( int idx )
3292{
3293 if (idx < 0) return;
3294
3295 unsigned int i;
3296 for ( i = 0; i < mAtmItemList.count(); ++i )
3297 if ( mAtmItemList.at( i )->itemPos() == idx )
3298 break;
3299
3300 if ( i > mAtmItemList.count() )
3301 return;
3302
3303 KMMessagePart* msgPart;
3304 msgPart = mAtmList.at( i );
3305 TQByteArray array;
3306 TQBuffer dev( array );
3307 KZip zip( &dev );
3308 TQByteArray decoded = msgPart->bodyDecodedBinary();
3309 if ( ! zip.open( IO_WriteOnly ) ) {
3310 KMessageBox::sorry(0, i18n("KMail could not compress the file.") );
3311 static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setCompress( false );
3312 return;
3313 }
3314
3315 zip.setCompression( KZip::DeflateCompression );
3316 if ( ! zip.writeFile( msgPart->name(), "", "", decoded.size(),
3317 decoded.data() ) ) {
3318 KMessageBox::sorry(0, i18n("KMail could not compress the file.") );
3319 static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setCompress( false );
3320 return;
3321 }
3322 zip.close();
3323 if ( array.size() >= decoded.size() ) {
3324 if ( KMessageBox::questionYesNo( this, i18n("The compressed file is larger "
3325 "than the original. Do you want to keep the original one?" ), TQString(), i18n("Keep"), i18n("Compress") )
3326 == KMessageBox::Yes ) {
3327 static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setCompress( false );
3328 return;
3329 }
3330 }
3331 static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setUncompressedCodec(
3332 msgPart->cteStr() );
3333
3334 msgPart->setCteStr( "base64" );
3335 msgPart->setBodyEncodedBinary( array );
3336 TQString name = msgPart->name() + ".zip";
3337
3338 msgPart->setName( name );
3339
3340 TQCString cDisp = "attachment;";
3341 TQCString encoding = KMMsgBase::autoDetectCharset( msgPart->charset(),
3343 kdDebug(5006) << "encoding: " << encoding << endl;
3344 if ( encoding.isEmpty() ) encoding = "utf-8";
3345 kdDebug(5006) << "encoding after: " << encoding << endl;
3346 TQCString encName;
3347 if ( GlobalSettings::self()->outlookCompatibleAttachments() )
3348 encName = KMMsgBase::encodeRFC2047String( name, encoding );
3349 else
3350 encName = KMMsgBase::encodeRFC2231String( name, encoding );
3351
3352 cDisp += "\n\tfilename";
3353 if ( name != TQString( encName ) )
3354 cDisp += "*=" + encName;
3355 else
3356 cDisp += "=\"" + encName + '"';
3357 msgPart->setContentDisposition( cDisp );
3358
3359 static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->setUncompressedMimeType(
3360 msgPart->typeStr(), msgPart->subtypeStr() );
3361 msgPart->setTypeStr( "application" );
3362 msgPart->setSubtypeStr( "x-zip" );
3363
3364 KMAtmListViewItem* listItem = static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) );
3365 msgPartToItem( msgPart, listItem, false );
3366}
3367
3368//-----------------------------------------------------------------------------
3369
3370void KMComposeWin::uncompressAttach( int idx )
3371{
3372 if (idx < 0) return;
3373
3374 unsigned int i;
3375 for ( i = 0; i < mAtmItemList.count(); ++i )
3376 if ( mAtmItemList.at( i )->itemPos() == idx )
3377 break;
3378
3379 if ( i > mAtmItemList.count() )
3380 return;
3381
3382 KMMessagePart* msgPart;
3383 msgPart = mAtmList.at( i );
3384
3385 TQBuffer dev( msgPart->bodyDecodedBinary() );
3386 KZip zip( &dev );
3387 TQByteArray decoded;
3388
3389 decoded = msgPart->bodyDecodedBinary();
3390 if ( ! zip.open( IO_ReadOnly ) ) {
3391 KMessageBox::sorry(0, i18n("KMail could not uncompress the file.") );
3392 static_cast<KMAtmListViewItem *>( mAtmItemList.at( i ) )->setCompress( true );
3393 return;
3394 }
3395 const KArchiveDirectory *dir = zip.directory();
3396
3397 KZipFileEntry *entry;
3398 if ( dir->entries().count() != 1 ) {
3399 KMessageBox::sorry(0, i18n("KMail could not uncompress the file.") );
3400 static_cast<KMAtmListViewItem *>( mAtmItemList.at( i ) )->setCompress( true );
3401 return;
3402 }
3403 entry = (KZipFileEntry*)dir->entry( dir->entries()[0] );
3404
3405 msgPart->setCteStr(
3406 static_cast<KMAtmListViewItem*>( mAtmItemList.at(i) )->uncompressedCodec() );
3407
3408 msgPart->setBodyEncodedBinary( entry->data() );
3409 TQString name = entry->name();
3410 msgPart->setName( name );
3411
3412 zip.close();
3413
3414 TQCString cDisp = "attachment;";
3415 TQCString encoding = KMMsgBase::autoDetectCharset( msgPart->charset(),
3417 if ( encoding.isEmpty() ) encoding = "utf-8";
3418
3419 TQCString encName;
3420 if ( GlobalSettings::self()->outlookCompatibleAttachments() )
3421 encName = KMMsgBase::encodeRFC2047String( name, encoding );
3422 else
3423 encName = KMMsgBase::encodeRFC2231String( name, encoding );
3424
3425 cDisp += "\n\tfilename";
3426 if ( name != TQString( encName ) )
3427 cDisp += "*=" + encName;
3428 else
3429 cDisp += "=\"" + encName + '"';
3430 msgPart->setContentDisposition( cDisp );
3431
3432 TQCString type, subtype;
3433 static_cast<KMAtmListViewItem*>( mAtmItemList.at( i ) )->uncompressedMimeType( type,
3434 subtype );
3435
3436 msgPart->setTypeStr( type );
3437 msgPart->setSubtypeStr( subtype );
3438
3439 KMAtmListViewItem* listItem = static_cast<KMAtmListViewItem*>(mAtmItemList.at( i ));
3440 msgPartToItem( msgPart, listItem, false );
3441}
3442
3443
3444//-----------------------------------------------------------------------------
3445void KMComposeWin::slotAttachView()
3446{
3447 int i = 0;
3448 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
3449 if ( (*it)->isSelected() ) {
3450 viewAttach( i );
3451 }
3452 }
3453}
3454//-----------------------------------------------------------------------------
3455void KMComposeWin::slotAttachOpen()
3456{
3457 int i = 0;
3458 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
3459 if ( (*it)->isSelected() ) {
3460 openAttach( i, false );
3461 }
3462 }
3463}
3464
3465//-----------------------------------------------------------------------------
3466void KMComposeWin::slotAttachOpenWith()
3467{
3468 int i = 0;
3469 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
3470 if ( (*it)->isSelected() ) {
3471 openAttach( i, true );
3472 }
3473 }
3474}
3475
3476void KMComposeWin::slotAttachEdit()
3477{
3478 int i = 0;
3479 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
3480 if ( (*it)->isSelected() ) {
3481 editAttach( i, false );
3482 }
3483 }
3484}
3485
3486void KMComposeWin::slotAttachEditWith()
3487{
3488 int i = 0;
3489 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
3490 if ( (*it)->isSelected() ) {
3491 editAttach( i, true );
3492 }
3493 }
3494}
3495
3496//-----------------------------------------------------------------------------
3497bool KMComposeWin::inlineSigningEncryptionSelected() {
3498 if ( !mSignAction->isChecked() && !mEncryptAction->isChecked() )
3499 return false;
3500 return cryptoMessageFormat() == Kleo::InlineOpenPGPFormat;
3501}
3502
3503//-----------------------------------------------------------------------------
3504void KMComposeWin::viewAttach( int index )
3505{
3506 TQString pname;
3507 KMMessagePart* msgPart;
3508 msgPart = mAtmList.at(index);
3509 pname = msgPart->name().stripWhiteSpace();
3510 if (pname.isEmpty()) pname=msgPart->contentDescription();
3511 if (pname.isEmpty()) pname="unnamed";
3512
3513 KTempFile* atmTempFile = new KTempFile();
3514 mAtmTempList.append( atmTempFile );
3515 atmTempFile->setAutoDelete( true );
3516 KPIM::kByteArrayToFile(msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
3517 false);
3518 KMReaderMainWin *win = new KMReaderMainWin(msgPart, false,
3519 atmTempFile->name(), pname, mCharset );
3520 win->show();
3521}
3522
3523//-----------------------------------------------------------------------------
3524void KMComposeWin::openAttach( int index, bool with )
3525{
3526 KMMessagePart* msgPart = mAtmList.at(index);
3527 const TQString contentTypeStr =
3528 ( msgPart->typeStr() + '/' + msgPart->subtypeStr() ).lower();
3529
3530 KMimeType::Ptr mimetype;
3531 mimetype = KMimeType::mimeType( contentTypeStr );
3532
3533 KTempFile* atmTempFile = new KTempFile();
3534 mAtmTempList.append( atmTempFile );
3535 const bool autoDelete = true;
3536 atmTempFile->setAutoDelete( autoDelete );
3537
3538 KURL url;
3539 url.setPath( atmTempFile->name() );
3540
3541 KPIM::kByteArrayToFile( msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
3542 false );
3543 if ( ::chmod( TQFile::encodeName( atmTempFile->name() ), S_IRUSR ) != 0) {
3544 TQFile::remove(url.path());
3545 return;
3546 }
3547
3548 KService::Ptr offer =
3549 KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
3550
3551 if ( with || !offer || mimetype->name() == "application/octet-stream" ) {
3552 if ( ( !KRun::displayOpenWithDialog( url, autoDelete ) ) && autoDelete ) {
3553 TQFile::remove(url.path());
3554 }
3555 }
3556 else {
3557 if ( ( !KRun::run( *offer, url, autoDelete ) ) && autoDelete ) {
3558 TQFile::remove( url.path() );
3559 }
3560 }
3561}
3562
3563void KMComposeWin::editAttach(int index, bool openWith)
3564{
3565 KMMessagePart* msgPart = mAtmList.at(index);
3566 const TQString contentTypeStr =
3567 ( msgPart->typeStr() + '/' + msgPart->subtypeStr() ).lower();
3568
3569 KTempFile* atmTempFile = new KTempFile();
3570 mAtmTempList.append( atmTempFile );
3571 atmTempFile->setAutoDelete( true );
3572 atmTempFile->file()->writeBlock( msgPart->bodyDecodedBinary() );
3573 atmTempFile->file()->flush();
3574
3575
3576 KMail::EditorWatcher *watcher =
3577 new KMail::EditorWatcher( KURL( atmTempFile->name() ), contentTypeStr, openWith,
3578 this, this );
3579 connect( watcher, TQ_SIGNAL(editDone(KMail::EditorWatcher*)), TQ_SLOT(slotEditDone(KMail::EditorWatcher*)) );
3580 if ( watcher->start() ) {
3581 mEditorMap.insert( watcher, msgPart );
3582 mEditorTempFiles.insert( watcher, atmTempFile );
3583 }
3584}
3585
3586//-----------------------------------------------------------------------------
3587void KMComposeWin::slotAttachSave()
3588{
3589 KMMessagePart* msgPart;
3590 TQString fileName, pname;
3591 int idx = currentAttachmentNum();
3592
3593 if (idx < 0) return;
3594
3595 msgPart = mAtmList.at(idx);
3596 pname = msgPart->name();
3597 if (pname.isEmpty()) pname="unnamed";
3598
3599 KURL url = KFileDialog::getSaveURL(pname, TQString(), 0, i18n("Save Attachment As"));
3600
3601 if( url.isEmpty() )
3602 return;
3603
3604 kmkernel->byteArrayToRemoteFile(msgPart->bodyDecodedBinary(), url);
3605}
3606
3607
3608//-----------------------------------------------------------------------------
3609void KMComposeWin::slotAttachRemove()
3610{
3611 mAtmSelectNew = 0;
3612 bool attachmentRemoved = false;
3613 int i = 0;
3614 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ) {
3615 if ( (*it)->isSelected() ) {
3616 removeAttach( i );
3617 attachmentRemoved = true;
3618 }
3619 else {
3620 ++it;
3621 ++i;
3622 }
3623 }
3624
3625 if ( attachmentRemoved ) {
3626 setModified( true );
3627 slotUpdateAttachActions();
3628 if ( mAtmSelectNew ) {
3629 mAtmListView->setSelected( mAtmSelectNew, true );
3630 mAtmListView->setCurrentItem( mAtmSelectNew );
3631 }
3632 }
3633}
3634
3635//-----------------------------------------------------------------------------
3636void KMComposeWin::slotFind()
3637{
3638 mEditor->search();
3639}
3640
3641void KMComposeWin::slotSearchAgain()
3642{
3643 mEditor->repeatSearch();
3644}
3645
3646//-----------------------------------------------------------------------------
3647void KMComposeWin::slotReplace()
3648{
3649 mEditor->replace();
3650}
3651
3652//-----------------------------------------------------------------------------
3653void KMComposeWin::slotUpdateFont()
3654{
3655 kdDebug() << "KMComposeWin::slotUpdateFont " << endl;
3656 if ( ! mFixedFontAction ) {
3657 return;
3658 }
3659 mEditor->setFont( mFixedFontAction->isChecked() ? mFixedFont : mBodyFont );
3660}
3661
3662TQString KMComposeWin::quotePrefixName() const
3663{
3664 if ( !msg() )
3665 return TQString();
3666
3667 int languageNr = GlobalSettings::self()->replyCurrentLanguage();
3668 ReplyPhrases replyPhrases( TQString::number(languageNr) );
3669 replyPhrases.readConfig();
3670 TQString quotePrefix = msg()->formatString(
3671 replyPhrases.indentPrefix() );
3672
3673 quotePrefix = msg()->formatString(quotePrefix);
3674 return quotePrefix;
3675}
3676
3677void KMComposeWin::slotPasteClipboardAsQuotation()
3678{
3679 if( mEditor->hasFocus() && msg() )
3680 {
3681 TQString s = TQApplication::clipboard()->text();
3682 if (!s.isEmpty())
3683 mEditor->insert(addQuotesToText(s));
3684 }
3685}
3686
3687void KMComposeWin::slotPasteClipboardAsAttachment()
3688{
3689 KURL url( TQApplication::clipboard()->text( TQClipboard::Clipboard ) );
3690 if ( url.isValid() ) {
3691 addAttach(TQApplication::clipboard()->text( TQClipboard::Clipboard ) );
3692 return;
3693 }
3694
3695 TQMimeSource *mimeSource = TQApplication::clipboard()->data();
3696 if ( TQImageDrag::canDecode(mimeSource) ) {
3697 slotAttachPNGImageData(mimeSource->encodedData("image/png"));
3698 }
3699 else {
3700 bool ok;
3701 TQString attName = KInputDialog::getText( "KMail", i18n("Name of the attachment:"), TQString(), &ok, this );
3702 if ( !ok )
3703 return;
3704 KMMessagePart *msgPart = new KMMessagePart;
3705 msgPart->setName(attName);
3706 TQValueList<int> dummy;
3707 msgPart->setBodyAndGuessCte(TQCString(TQApplication::clipboard()->text().latin1()), dummy,
3708 kmkernel->msgSender()->sendQuotedPrintable());
3709 addAttach(msgPart);
3710 }
3711}
3712
3713void KMComposeWin::slotAddQuotes()
3714{
3715 if( mEditor->hasFocus() && msg() )
3716 {
3717 // TODO: I think this is backwards.
3718 // i.e, if no region is marked then add quotes to every line
3719 // else add quotes only on the lines that are marked.
3720
3721 if ( mEditor->hasMarkedText() ) {
3722 TQString s = mEditor->markedText();
3723 if(!s.isEmpty())
3724 mEditor->insert(addQuotesToText(s));
3725 } else {
3726 int l = mEditor->currentLine();
3727 int c = mEditor->currentColumn();
3728 TQString s = mEditor->textLine(l);
3729 s.prepend(quotePrefixName());
3730 mEditor->insertLine(s,l);
3731 mEditor->removeLine(l+1);
3732 mEditor->setCursorPosition(l,c+2);
3733 }
3734 }
3735}
3736
3737TQString KMComposeWin::addQuotesToText(const TQString &inputText)
3738{
3739 TQString answer = TQString( inputText );
3740 TQString indentStr = quotePrefixName();
3741 answer.replace( '\n', '\n' + indentStr);
3742 answer.prepend( indentStr );
3743 answer += '\n';
3744 return KMMessage::smartQuote( answer, GlobalSettings::self()->lineWrapWidth() );
3745}
3746
3747TQString KMComposeWin::removeQuotesFromText(const TQString &inputText)
3748{
3749 TQString s = inputText;
3750
3751 // remove first leading quote
3752 TQString quotePrefix = '^' + quotePrefixName();
3753 TQRegExp rx(quotePrefix);
3754 s.remove(rx);
3755
3756 // now remove all remaining leading quotes
3757 quotePrefix = '\n' + quotePrefixName();
3758 rx = quotePrefix;
3759 s.replace(rx, "\n");
3760
3761 return s;
3762}
3763
3764void KMComposeWin::slotRemoveQuotes()
3765{
3766 if( mEditor->hasFocus() && msg() )
3767 {
3768 // TODO: I think this is backwards.
3769 // i.e, if no region is marked then remove quotes from every line
3770 // else remove quotes only on the lines that are marked.
3771
3772 if ( mEditor->hasMarkedText() ) {
3773 TQString s = mEditor->markedText();
3774 mEditor->insert(removeQuotesFromText(s));
3775 } else {
3776 int l = mEditor->currentLine();
3777 int c = mEditor->currentColumn();
3778 TQString s = mEditor->textLine(l);
3779 mEditor->insertLine(removeQuotesFromText(s),l);
3780 mEditor->removeLine(l+1);
3781 mEditor->setCursorPosition(l,c-2);
3782 }
3783 }
3784}
3785
3786//-----------------------------------------------------------------------------
3787void KMComposeWin::slotUndo()
3788{
3789 TQWidget* fw = focusWidget();
3790 if (!fw) return;
3791
3792 if ( ::tqt_cast<KEdit*>(fw) )
3793 static_cast<TQTextEdit*>(fw)->undo();
3794 else if (::tqt_cast<TQLineEdit*>(fw))
3795 static_cast<TQLineEdit*>(fw)->undo();
3796}
3797
3798void KMComposeWin::slotRedo()
3799{
3800 TQWidget* fw = focusWidget();
3801 if (!fw) return;
3802
3803 if (::tqt_cast<KEdit*>(fw))
3804 static_cast<KEdit*>(fw)->redo();
3805 else if (::tqt_cast<TQLineEdit*>(fw))
3806 static_cast<TQLineEdit*>(fw)->redo();
3807}
3808
3809//-----------------------------------------------------------------------------
3810void KMComposeWin::slotCut()
3811{
3812 TQWidget* fw = focusWidget();
3813 if (!fw) return;
3814
3815 if (::tqt_cast<KEdit*>(fw))
3816 static_cast<KEdit*>(fw)->cut();
3817 else if (::tqt_cast<TQLineEdit*>(fw))
3818 static_cast<TQLineEdit*>(fw)->cut();
3819}
3820
3821
3822//-----------------------------------------------------------------------------
3823void KMComposeWin::slotCopy()
3824{
3825 TQWidget* fw = focusWidget();
3826 if (!fw) return;
3827
3828#ifdef KeyPress
3829#undef KeyPress
3830#endif
3831
3832 TQKeyEvent k(TQEvent::KeyPress, Key_C, 0, ControlButton);
3833 tdeApp->notify(fw, &k);
3834}
3835
3836
3837//-----------------------------------------------------------------------------
3838void KMComposeWin::slotPasteClipboard()
3839{
3840 paste( TQClipboard::Clipboard );
3841}
3842
3843void KMComposeWin::paste( TQClipboard::Mode mode )
3844{
3845 TQWidget* fw = focusWidget();
3846 if (!fw) return;
3847
3848 TQMimeSource *mimeSource = TQApplication::clipboard()->data( mode );
3849 if ( mimeSource->provides("image/png") ) {
3850 slotAttachPNGImageData(mimeSource->encodedData("image/png"));
3851 } else if ( KURLDrag::canDecode( mimeSource ) ) {
3852 KURL::List urlList;
3853 if( KURLDrag::decode( mimeSource, urlList ) ) {
3854 const TQString asText = i18n("Add as Text");
3855 const TQString asAttachment = i18n("Add as Attachment");
3856 const TQString text = i18n("Please select whether you want to insert the content as text into the editor, "
3857 "or append the referenced file as an attachment.");
3858 const TQString caption = i18n("Paste as text or attachment?");
3859
3860 int id = KMessageBox::questionYesNoCancel( this, text, caption,
3861 KGuiItem( asText ), KGuiItem( asAttachment) );
3862 switch ( id) {
3863 case KMessageBox::Yes:
3864 for ( KURL::List::Iterator it = urlList.begin();
3865 it != urlList.end(); ++it ) {
3866 mEditor->insert( (*it).url() );
3867 }
3868 break;
3869 case KMessageBox::No:
3870 for ( KURL::List::Iterator it = urlList.begin();
3871 it != urlList.end(); ++it ) {
3872 addAttach( *it );
3873 }
3874 break;
3875 }
3876 }
3877 } else if ( TQTextDrag::canDecode( mimeSource ) ) {
3878 TQString s;
3879 if ( TQTextDrag::decode( mimeSource, s ) )
3880 mEditor->insert( s );
3881 }
3882}
3883
3884
3885//-----------------------------------------------------------------------------
3886void KMComposeWin::slotMarkAll()
3887{
3888 TQWidget* fw = focusWidget();
3889 if (!fw) return;
3890
3891 if (::tqt_cast<TQLineEdit*>(fw))
3892 static_cast<TQLineEdit*>(fw)->selectAll();
3893 else if (::tqt_cast<KEdit*>(fw))
3894 static_cast<KEdit*>(fw)->selectAll();
3895}
3896
3897
3898//-----------------------------------------------------------------------------
3899void KMComposeWin::slotClose()
3900{
3901 close(false);
3902}
3903
3904
3905//-----------------------------------------------------------------------------
3906void KMComposeWin::slotNewComposer()
3907{
3908 KMComposeWin* win;
3909 KMMessage* msg = new KMMessage;
3910
3911 msg->initHeader();
3912 win = new KMComposeWin(msg);
3913 win->show();
3914}
3915
3916
3917//-----------------------------------------------------------------------------
3918void KMComposeWin::slotNewMailReader()
3919{
3920 KMMainWin *kmmwin = new KMMainWin(0);
3921 kmmwin->show();
3922 //d->resize(d->size());
3923}
3924
3925
3926//-----------------------------------------------------------------------------
3927void KMComposeWin::slotUpdWinTitle(const TQString& text)
3928{
3929 TQString s( text );
3930 // Remove characters that show badly in most window decorations:
3931 // newlines tend to become boxes.
3932 if (text.isEmpty())
3933 setCaption("("+i18n("unnamed")+")");
3934 else setCaption( s.replace( TQChar('\n'), ' ' ) );
3935}
3936
3937
3938//-----------------------------------------------------------------------------
3939void KMComposeWin::slotEncryptToggled(bool on)
3940{
3941 setEncryption( on, true /* set by the user */ );
3942 slotUpdateSignatureAndEncrypionStateIndicators();
3943}
3944
3945
3946//-----------------------------------------------------------------------------
3947void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
3948{
3949 bool wasModified = isModified();
3950 if ( setByUser )
3951 setModified( true );
3952 if ( !mEncryptAction->isEnabled() )
3953 encrypt = false;
3954 // check if the user wants to encrypt messages to himself and if he defined
3955 // an encryption key for the current identity
3956 else if ( encrypt && encryptToSelf() && !mLastIdentityHasEncryptionKey ) {
3957 if ( setByUser ) {
3958 KMessageBox::sorry( this,
3959 i18n("<qt><p>You have requested that messages be "
3960 "encrypted to yourself, but the currently selected "
3961 "identity does not define an (OpenPGP or S/MIME) "
3962 "encryption key to use for this.</p>"
3963 "<p>Please select the key(s) to use "
3964 "in the identity configuration.</p>"
3965 "</qt>"),
3966 i18n("Undefined Encryption Key") );
3967 setModified( wasModified );
3968 }
3969 encrypt = false;
3970 }
3971
3972 // make sure the mEncryptAction is in the right state
3973 mEncryptAction->setChecked( encrypt );
3974
3975 // show the appropriate icon
3976 if ( encrypt )
3977 mEncryptAction->setIcon("encrypted");
3978 else
3979 mEncryptAction->setIcon("decrypted");
3980
3981 // mark the attachments for (no) encryption
3982 if ( canSignEncryptAttachments() ) {
3983 for ( KMAtmListViewItem* entry =
3984 static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
3985 entry;
3986 entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
3987 entry->setEncrypt( encrypt );
3988 }
3989}
3990
3991
3992//-----------------------------------------------------------------------------
3993void KMComposeWin::slotSignToggled(bool on)
3994{
3995 setSigning( on, true /* set by the user */ );
3996 slotUpdateSignatureAndEncrypionStateIndicators();
3997}
3998
3999
4000//-----------------------------------------------------------------------------
4001void KMComposeWin::setSigning( bool sign, bool setByUser )
4002{
4003 bool wasModified = isModified();
4004 if ( setByUser )
4005 setModified( true );
4006 if ( !mSignAction->isEnabled() )
4007 sign = false;
4008
4009 // check if the user defined a signing key for the current identity
4010 if ( sign && !mLastIdentityHasSigningKey ) {
4011 if ( setByUser ) {
4012 KMessageBox::sorry( this,
4013 i18n("<qt><p>In order to be able to sign "
4014 "this message you first have to "
4015 "define the (OpenPGP or S/MIME) signing key "
4016 "to use.</p>"
4017 "<p>Please select the key to use "
4018 "in the identity configuration.</p>"
4019 "</qt>"),
4020 i18n("Undefined Signing Key") );
4021 setModified( wasModified );
4022 }
4023 sign = false;
4024 }
4025
4026 // make sure the mSignAction is in the right state
4027 mSignAction->setChecked( sign );
4028
4029 // mark the attachments for (no) signing
4030 if ( canSignEncryptAttachments() ) {
4031 for ( KMAtmListViewItem* entry =
4032 static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
4033 entry;
4034 entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
4035 entry->setSign( sign );
4036 }
4037}
4038
4039
4040//-----------------------------------------------------------------------------
4041void KMComposeWin::slotWordWrapToggled(bool on)
4042{
4043 if (on)
4044 {
4045 mEditor->setWordWrap( TQTextEdit::FixedColumnWidth );
4046 mEditor->setWrapColumnOrWidth( GlobalSettings::self()->lineWrapWidth() );
4047 }
4048 else
4049 {
4050 mEditor->setWordWrap( TQTextEdit::WidgetWidth );
4051 }
4052}
4053
4054
4055void KMComposeWin::disableWordWrap()
4056{
4057 mEditor->setWordWrap( TQTextEdit::NoWrap );
4058}
4059
4060void KMComposeWin::disableRecipientNumberCheck()
4061{
4062 mCheckForRecipients = false;
4063}
4064
4065void KMComposeWin::disableForgottenAttachmentsCheck()
4066{
4067 mCheckForForgottenAttachments = false;
4068}
4069
4070void KMComposeWin::ignoreStickyFields()
4071{
4072 mIgnoreStickyFields = true;
4073 mBtnTransport->setChecked( false );
4074 mBtnDictionary->setChecked( false );
4075 mBtnIdentity->setChecked( false );
4076 mBtnTransport->setEnabled( false );
4077 mBtnDictionary->setEnabled( false );
4078 mBtnIdentity->setEnabled( false );
4079}
4080
4081//-----------------------------------------------------------------------------
4082void KMComposeWin::slotPrint()
4083{
4084 mMessageWasModified = isModified();
4085 connect( this, TQ_SIGNAL( applyChangesDone( bool ) ),
4086 this, TQ_SLOT( slotContinuePrint( bool ) ) );
4087 applyChanges( true );
4088}
4089
4090void KMComposeWin::slotContinuePrint( bool rc )
4091{
4092 disconnect( this, TQ_SIGNAL( applyChangesDone( bool ) ),
4093 this, TQ_SLOT( slotContinuePrint( bool ) ) );
4094
4095 if( rc ) {
4096 if ( mComposedMessages.isEmpty() ) {
4097 kdDebug(5006) << "Composing the message failed." << endl;
4098 return;
4099 }
4100 KMCommand *command = new KMPrintCommand( this, mComposedMessages.first() );
4101 command->start();
4102 setModified( mMessageWasModified );
4103 }
4104}
4105
4106//----------------------------------------------------------------------------
4107bool KMComposeWin::validateAddresses( TQWidget * parent, const TQString & addresses )
4108{
4109 TQString brokenAddress;
4110 KPIM::EmailParseResult errorCode = KMMessage::isValidEmailAddressList( KMMessage::expandAliases( addresses ), brokenAddress );
4111 if ( !( errorCode == KPIM::AddressOk || errorCode == KPIM::AddressEmpty ) ) {
4112 TQString errorMsg( "<qt><p><b>" + brokenAddress +
4113 "</b></p><p>" + KPIM::emailParseResultToString( errorCode ) +
4114 "</p></qt>" );
4115 KMessageBox::sorry( parent, errorMsg, i18n("Invalid Email Address") );
4116 return false;
4117 }
4118 return true;
4119}
4120
4121//----------------------------------------------------------------------------
4122void KMComposeWin::doSend( KMail::MessageSender::SendMethod method,
4123 KMComposeWin::SaveIn saveIn )
4124{
4125 if ( method != KMail::MessageSender::SendLater && kmkernel->isOffline() ) {
4126 KMessageBox::information( this,
4127 i18n("KMail is currently in offline mode,"
4128 "your messages will be kept in the outbox until you go online."),
4129 i18n("Online/Offline"), "kmailIsOffline" );
4130 mSendMethod = KMail::MessageSender::SendLater;
4131 } else {
4132 mSendMethod = method;
4133 }
4134 mSaveIn = saveIn;
4135
4136 if ( saveIn == KMComposeWin::None ) {
4137 if ( KPIM::getFirstEmailAddress( from() ).isEmpty() ) {
4138 if ( !( mShowHeaders & HDR_FROM ) ) {
4139 mShowHeaders |= HDR_FROM;
4140 rethinkFields( false );
4141 }
4142 mEdtFrom->setFocus();
4143 KMessageBox::sorry( this,
4144 i18n("You must enter your email address in the "
4145 "From: field. You should also set your email "
4146 "address for all identities, so that you do "
4147 "not have to enter it for each message.") );
4148 return;
4149 }
4150 if ( to().isEmpty() )
4151 {
4152 if ( cc().isEmpty() && bcc().isEmpty()) {
4153 if ( mEdtTo ) mEdtTo->setFocus();
4154 KMessageBox::information( this,
4155 i18n("You must specify at least one receiver,"
4156 "either in the To: field or as CC or as BCC.") );
4157 return;
4158 }
4159 else {
4160 if ( mEdtTo ) mEdtTo->setFocus();
4161 int rc =
4162 KMessageBox::questionYesNo( this,
4163 i18n("To field is missing."
4164 "Send message anyway?"),
4165 i18n("No To: specified") );
4166 if ( rc == KMessageBox::No ){
4167 return;
4168 }
4169 }
4170 }
4171
4172 // Validate the To:, CC: and BCC fields
4173 if ( !validateAddresses( this, to().stripWhiteSpace() ) ) {
4174 return;
4175 }
4176
4177 if ( !validateAddresses( this, cc().stripWhiteSpace() ) ) {
4178 return;
4179 }
4180
4181 if ( !validateAddresses( this, bcc().stripWhiteSpace() ) ) {
4182 return;
4183 }
4184
4185 if (subject().isEmpty())
4186 {
4187 mEdtSubject->setFocus();
4188 int rc =
4189 KMessageBox::questionYesNo( this,
4190 i18n("You did not specify a subject. "
4191 "Send message anyway?"),
4192 i18n("No Subject Specified"),
4193 i18n("S&end as Is"),
4194 i18n("&Specify the Subject"),
4195 "no_subject_specified" );
4196 if( rc == KMessageBox::No )
4197 {
4198 return;
4199 }
4200 }
4201
4202 if ( userForgotAttachment() )
4203 return;
4204 }
4205
4206 KCursorSaver busy(KBusyPtr::busy());
4207 mMsg->setDateToday();
4208
4209 // If a user sets up their outgoing messages preferences wrong and then
4210 // sends mail that gets 'stuck' in their outbox, they should be able to
4211 // rectify the problem by editing their outgoing preferences and
4212 // resending.
4213 // Hence this following conditional
4214 TQString hf = mMsg->headerField("X-KMail-Transport");
4215 if ((mTransport->currentText() != mTransport->text(0)) ||
4216 (!hf.isEmpty() && (hf != mTransport->text(0))))
4217 mMsg->setHeaderField("X-KMail-Transport", mTransport->currentText());
4218
4219 mDisableBreaking = ( saveIn != KMComposeWin::None );
4220
4221 const bool neverEncrypt = ( mDisableBreaking && GlobalSettings::self()->neverEncryptDrafts() )
4222 || mSigningAndEncryptionExplicitlyDisabled;
4223 connect( this, TQ_SIGNAL( applyChangesDone( bool ) ),
4224 TQ_SLOT( slotContinueDoSend( bool ) ) );
4225
4226 if ( mEditor->textFormat() == TQt::RichText )
4227 mMsg->setHeaderField( "X-KMail-Markup", "true" );
4228 else
4229 mMsg->removeHeaderField( "X-KMail-Markup" );
4230 if ( mEditor->textFormat() == TQt::RichText && inlineSigningEncryptionSelected() ) {
4231 TQString keepBtnText = mEncryptAction->isChecked() ?
4232 mSignAction->isChecked() ? i18n( "&Keep markup, do not sign/encrypt" )
4233 : i18n( "&Keep markup, do not encrypt" )
4234 : i18n( "&Keep markup, do not sign" );
4235 TQString yesBtnText = mEncryptAction->isChecked() ?
4236 mSignAction->isChecked() ? i18n("Sign/Encrypt (delete markup)")
4237 : i18n( "Encrypt (delete markup)" )
4238 : i18n( "Sign (delete markup)" );
4239 int ret = KMessageBox::warningYesNoCancel(this,
4240 i18n("<qt><p>Inline signing/encrypting of HTML messages is not possible;</p>"
4241 "<p>do you want to delete your markup?</p></qt>"),
4242 i18n("Sign/Encrypt Message?"),
4243 KGuiItem( yesBtnText ),
4244 KGuiItem( keepBtnText ) );
4245 if ( KMessageBox::Cancel == ret )
4246 return;
4247 if ( KMessageBox::No == ret ) {
4248 mEncryptAction->setChecked(false);
4249 mSignAction->setChecked(false);
4250 }
4251 else {
4252 toggleMarkup(false);
4253 }
4254 }
4255
4256 if (neverEncrypt && saveIn != KMComposeWin::None ) {
4257 // we can't use the state of the mail itself, to remember the
4258 // signing and encryption state, so let's add a header instead
4259 mMsg->setHeaderField( "X-KMail-SignatureActionEnabled", mSignAction->isChecked()? "true":"false" );
4260 mMsg->setHeaderField( "X-KMail-EncryptActionEnabled", mEncryptAction->isChecked()? "true":"false" );
4261 mMsg->setHeaderField( "X-KMail-CryptoMessageFormat", TQString::number( cryptoMessageFormat() ) );
4262 } else {
4263 mMsg->removeHeaderField( "X-KMail-SignatureActionEnabled" );
4264 mMsg->removeHeaderField( "X-KMail-EncryptActionEnabled" );
4265 mMsg->removeHeaderField( "X-KMail-CryptoMessageFormat" );
4266 }
4267
4268
4269 kdDebug(5006) << "KMComposeWin::doSend() - calling applyChanges()"
4270 << endl;
4271 applyChanges( neverEncrypt );
4272}
4273
4274bool KMComposeWin::saveDraftOrTemplate( const TQString &folderName,
4275 KMMessage *msg )
4276{
4277 KMFolder *theFolder = 0, *imapTheFolder = 0;
4278 // get the draftsFolder
4279 if ( !folderName.isEmpty() ) {
4280 theFolder = kmkernel->folderMgr()->findIdString( folderName );
4281 if ( theFolder == 0 )
4282 // This is *NOT* supposed to be "imapDraftsFolder", because a
4283 // dIMAP folder works like a normal folder
4284 theFolder = kmkernel->dimapFolderMgr()->findIdString( folderName );
4285 if ( theFolder == 0 )
4286 imapTheFolder = kmkernel->imapFolderMgr()->findIdString( folderName );
4287 if ( !theFolder && !imapTheFolder ) {
4288 const KPIM::Identity & id = kmkernel->identityManager()
4289 ->identityForUoidOrDefault( msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
4290 KMessageBox::information( 0,
4291 i18n("The custom drafts or templates folder for "
4292 "identify \"%1\" does not exist (anymore); "
4293 "therefore, the default drafts or templates "
4294 "folder will be used.")
4295 .arg( id.identityName() ) );
4296 }
4297 }
4298 if ( imapTheFolder && imapTheFolder->noContent() )
4299 imapTheFolder = 0;
4300
4301 bool didOpen = false;
4302 if ( theFolder == 0 ) {
4303 theFolder = ( mSaveIn==KMComposeWin::Drafts ?
4304 kmkernel->draftsFolder() : kmkernel->templatesFolder() );
4305 } else {
4306 //XXX this looks really, really fishy
4307 theFolder->open( "composer" );
4308 didOpen = true;
4309 }
4310 kdDebug(5006) << k_funcinfo << "theFolder=" << theFolder->name() << endl;
4311 if ( imapTheFolder )
4312 kdDebug(5006) << k_funcinfo << "imapTheFolder=" << imapTheFolder->name() << endl;
4313
4314 bool sentOk = !( theFolder->addMsg( msg ) );
4315
4316 // Ensure the message is correctly and fully parsed
4317 theFolder->unGetMsg( theFolder->count() - 1 );
4318 msg = theFolder->getMsg( theFolder->count() - 1 );
4319 // Does that assignment needs to be propagated out to the caller?
4320 // Assuming the send is OK, the iterator is set to 0 immediately afterwards.
4321 if ( imapTheFolder ) {
4322 // move the message to the imap-folder and highlight it
4323 imapTheFolder->moveMsg( msg );
4324 (static_cast<KMFolderImap*>( imapTheFolder->storage() ))->getFolder();
4325 }
4326
4327 if ( didOpen )
4328 theFolder->close( "composer" );
4329 return sentOk;
4330}
4331
4332void KMComposeWin::slotContinueDoSend( bool sentOk )
4333{
4334 kdDebug(5006) << "KMComposeWin::slotContinueDoSend( " << sentOk << " )"
4335 << endl;
4336 disconnect( this, TQ_SIGNAL( applyChangesDone( bool ) ),
4337 this, TQ_SLOT( slotContinueDoSend( bool ) ) );
4338
4339 if ( !sentOk ) {
4340 mDisableBreaking = false;
4341 return;
4342 }
4343
4344 for ( TQValueVector<KMMessage*>::iterator it = mComposedMessages.begin() ; it != mComposedMessages.end() ; ++it ) {
4345
4346 // remove fields that contain no data (e.g. an empty Cc: or Bcc:)
4347 (*it)->cleanupHeader();
4348
4349 // needed for imap
4350 (*it)->setComplete( true );
4351
4352 if ( mSaveIn==KMComposeWin::Drafts ) {
4353 sentOk = saveDraftOrTemplate( (*it)->drafts(), (*it) );
4354 } else if ( mSaveIn==KMComposeWin::Templates ) {
4355 sentOk = saveDraftOrTemplate( (*it)->templates(), (*it) );
4356 } else {
4357 (*it)->setTo( KMMessage::expandAliases( to() ));
4358 (*it)->setCc( KMMessage::expandAliases( cc() ));
4359 if( !mComposer->originalBCC().isEmpty() )
4360 (*it)->setBcc( KMMessage::expandAliases( mComposer->originalBCC() ));
4361 TQString recips = (*it)->headerField( "X-KMail-Recipients" );
4362 if( !recips.isEmpty() ) {
4363 (*it)->setHeaderField( "X-KMail-Recipients", KMMessage::expandAliases( recips ), KMMessage::Address );
4364 }
4365 (*it)->cleanupHeader();
4366 sentOk = kmkernel->msgSender()->send((*it), mSendMethod);
4367 }
4368
4369 if (!sentOk)
4370 return;
4371
4372 *it = 0; // don't kill it later...
4373 }
4374
4375 RecentAddresses::self( KMKernel::config() )->add( bcc() );
4376 RecentAddresses::self( KMKernel::config() )->add( cc() );
4377 RecentAddresses::self( KMKernel::config() )->add( to() );
4378
4379 setModified( false );
4380 mAutoDeleteMsg = false;
4381 mFolder = 0;
4382 cleanupAutoSave();
4383 close();
4384 return;
4385}
4386
4387bool KMComposeWin::checkTransport() const
4388{
4389 if ( KMail::TransportManager::transportNames().isEmpty() ) {
4390 KMessageBox::information( mMainWidget,
4391 i18n("Please create an account for sending and try again.") );
4392 return false;
4393 }
4394 return true;
4395
4396}
4397
4398//----------------------------------------------------------------------------
4399void KMComposeWin::slotSendLater()
4400{
4401 if ( !checkTransport() )
4402 return;
4403 if ( !checkRecipientNumber() )
4404 return;
4405 if ( mEditor->checkExternalEditorFinished() )
4406 doSend( KMail::MessageSender::SendLater );
4407}
4408
4409
4410//----------------------------------------------------------------------------
4411void KMComposeWin::slotSaveDraft() {
4412 if ( mEditor->checkExternalEditorFinished() )
4413 doSend( KMail::MessageSender::SendLater, KMComposeWin::Drafts );
4414}
4415
4416//----------------------------------------------------------------------------
4417void KMComposeWin::slotSaveTemplate() {
4418 if ( mEditor->checkExternalEditorFinished() )
4419 doSend( KMail::MessageSender::SendLater, KMComposeWin::Templates );
4420}
4421
4422//----------------------------------------------------------------------------
4423void KMComposeWin::slotSendNowVia( int item )
4424{
4425 TQStringList availTransports= KMail::TransportManager::transportNames();
4426 TQString customTransport = availTransports[ item ];
4427
4428 mTransport->setCurrentText( customTransport );
4429 slotSendNow();
4430}
4431
4432//----------------------------------------------------------------------------
4433void KMComposeWin::slotSendLaterVia( int item )
4434{
4435 TQStringList availTransports= KMail::TransportManager::transportNames();
4436 TQString customTransport = availTransports[ item ];
4437
4438 mTransport->setCurrentText( customTransport );
4439 slotSendLater();
4440}
4441
4442
4443//----------------------------------------------------------------------------
4444void KMComposeWin::slotSendNow() {
4445 if ( !mEditor->checkExternalEditorFinished() )
4446 return;
4447 if ( !checkTransport() )
4448 return;
4449 if ( !checkRecipientNumber() )
4450 return;
4451 if ( GlobalSettings::self()->confirmBeforeSend() )
4452 {
4453 int rc = KMessageBox::warningYesNoCancel( mMainWidget,
4454 i18n("About to send email..."),
4455 i18n("Send Confirmation"),
4456 i18n("&Send Now"),
4457 i18n("Send &Later") );
4458
4459 if ( rc == KMessageBox::Yes )
4460 doSend( KMail::MessageSender::SendImmediate );
4461 else if ( rc == KMessageBox::No )
4462 doSend( KMail::MessageSender::SendLater );
4463 }
4464 else
4465 doSend( KMail::MessageSender::SendImmediate );
4466}
4467
4468
4469//----------------------------------------------------------------------------
4470bool KMComposeWin::checkRecipientNumber() const
4471{
4472 uint thresHold = GlobalSettings::self()->recipientThreshold();
4473 if ( mCheckForRecipients &&
4474 GlobalSettings::self()->tooManyRecipients() &&
4475 mRecipientsEditor->recipients().count() > thresHold ) {
4476 if ( KMessageBox::questionYesNo( mMainWidget,
4477 i18n("You are trying to send the mail to more than %1 recipients. Send message anyway?").arg(thresHold),
4478 i18n("Too many receipients"),
4479 i18n("&Send as Is"),
4480 i18n("&Edit Recipients")) == KMessageBox::No ) {
4481 return false;
4482 }
4483 }
4484 return true;
4485}
4486
4487
4488//----------------------------------------------------------------------------
4489void KMComposeWin::slotAppendSignature()
4490{
4491 insertSignature();
4492}
4493
4494//----------------------------------------------------------------------------
4495void KMComposeWin::slotPrependSignature()
4496{
4497 insertSignature( Prepend );
4498}
4499
4500//----------------------------------------------------------------------------
4501void KMComposeWin::slotInsertSignatureAtCursor()
4502{
4503 insertSignature( AtCursor );
4504}
4505
4506//----------------------------------------------------------------------------
4507void KMComposeWin::insertSignature( SignaturePlacement placement )
4508{
4509 bool mod = mEditor->isModified();
4510
4511 const KPIM::Identity &ident =
4512 kmkernel->identityManager()->
4513 identityForUoidOrDefault( mIdentity->currentIdentity() );
4514
4515 mOldSigText = GlobalSettings::self()->prependSignature()? ident.signature().rawText() : ident.signatureText();
4516
4517 if( !mOldSigText.isEmpty() )
4518 {
4519 mEditor->sync();
4520 int paragraph, index;
4521 mEditor->getCursorPosition( &paragraph, &index );
4522 index = mEditor->indexOfCurrentLineStart( paragraph, index );
4523
4524 switch( placement ) {
4525 case Append:
4526 mEditor->setText( mEditor->text() + mOldSigText );
4527 break;
4528 case Prepend:
4529 mOldSigText = "\n\n" + mOldSigText + "\n";
4530 mEditor->insertAt( mOldSigText, paragraph, index );
4531 break;
4532 case AtCursor:
4533
4534 // If there is text in the same line, add a newline so that the stuff in
4535 // the current line moves after the signature. Also remove a leading newline, it is not
4536 // needed here.
4537 if ( mEditor->paragraphLength( paragraph ) > 0 )
4538 mOldSigText = mOldSigText + "\n";
4539 if ( mOldSigText.startsWith( "\n" ) )
4540 mOldSigText = mOldSigText.remove( 0, 1 );
4541
4542 // If we are inserting into a wordwrapped line, add a newline at the start to make
4543 // the text edit hard-wrap the line here
4544 if ( index != 0 )
4545 mOldSigText = "\n" + mOldSigText;
4546
4547 mEditor->insertAt( mOldSigText, paragraph, index );
4548 break;
4549 }
4550 mEditor->update();
4551 mEditor->setModified(mod);
4552
4553 if ( mPreserveUserCursorPosition ) {
4554 mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() );
4555 // Only keep the cursor from the mMsg *once* based on the
4556 // preserve-cursor-position setting; this handles the case where
4557 // the message comes from a template with a specific cursor
4558 // position set and the signature is appended automatically.
4559 mPreserveUserCursorPosition = false;
4560 } else {
4561 // for append and prepend, move the cursor to 0,0, for insertAt,
4562 // keep it in the same row, but move to first column
4563 if ( index == 0 ) {
4564 mEditor->setCursorPosition( paragraph, 0 );
4565 } else {
4566 // For word-wrapped lines, we have created a new paragraph, so change to that one
4567 mEditor->setCursorPosition( paragraph + 1, 0 );
4568 }
4569 if ( placement == Prepend || placement == Append )
4570 mEditor->setContentsPos( 0, 0 );
4571 }
4572 mEditor->sync();
4573 }
4574}
4575
4576//-----------------------------------------------------------------------------
4577void KMComposeWin::slotHelp()
4578{
4579 tdeApp->invokeHelp();
4580}
4581
4582//-----------------------------------------------------------------------------
4583void KMComposeWin::slotCleanSpace()
4584{
4585 // Originally we simply used the KEdit::cleanWhiteSpace() method,
4586 // but that code doesn't handle quoted-lines or signatures, so instead
4587 // we now simply use regexp's to squeeze sequences of tabs and spaces
4588 // into a single space, and make sure all our lines are single-spaced.
4589 //
4590 // Yes, extra space in a quote string is squeezed.
4591 // Signatures are respected (i.e. not cleaned).
4592
4593 TQString s;
4594 if ( mEditor->hasMarkedText() ) {
4595 s = mEditor->markedText();
4596 if( s.isEmpty() )
4597 return;
4598 } else {
4599 s = mEditor->text();
4600 }
4601
4602 // Remove the signature for now.
4603 TQString sig;
4604 bool restore = false;
4605 const KPIM::Identity & ident =
4606 kmkernel->identityManager()->identityForUoid( mId );
4607 if ( !ident.isNull() ) {
4608 sig = ident.signatureText();
4609 if( !sig.isEmpty() ) {
4610 if( s.endsWith( sig ) ) {
4611 s.truncate( s.length() - sig.length() );
4612 restore = true;
4613 }
4614 }
4615 }
4616
4617 // Squeeze tabs and spaces
4618 TQRegExp squeeze( "[\t ]+" );
4619 s.replace( squeeze, TQChar( ' ' ) );
4620
4621 // Remove trailing whitespace
4622 TQRegExp trailing( "\\s+$" );
4623 s.replace( trailing, TQChar( '\n' ) );
4624
4625 // Single space lines
4626 TQRegExp singleSpace( "[\n]{2,}" );
4627 s.replace( singleSpace, TQChar( '\n' ) );
4628
4629 // Restore the signature
4630 if ( restore )
4631 s.append( sig );
4632
4633 // Put the new text in place.
4634 // The lines below do not clear the undo history, but unfortuately cause
4635 // the side-effect that you need to press Ctrl-Z twice (first Ctrl-Z will
4636 // show cleared text area) to get back the original, pre-cleaned text.
4637 // If you use mEditor->setText( s ) then the undo history is cleared so
4638 // that isn't a good solution either.
4639 // TODO: is TQt4 better at handling the undo history??
4640 if ( !mEditor->hasMarkedText() )
4641 mEditor->clear();
4642 mEditor->insert( s );
4643}
4644
4645//-----------------------------------------------------------------------------
4646void KMComposeWin::slotToggleMarkup()
4647{
4648 if ( markupAction->isChecked() ) {
4649 mHtmlMarkup = true;
4650 toolBar("htmlToolBar")->show();
4651 // markup will be toggled as soon as markup is actually used
4652 fontChanged( mEditor->currentFont() ); // set buttons in correct position
4653 mSaveFont = mEditor->currentFont();
4654 }
4655 else
4656 toggleMarkup(false);
4657
4658}
4659//-----------------------------------------------------------------------------
4660void KMComposeWin::toggleMarkup(bool markup)
4661{
4662 if ( markup ) {
4663 if ( !mUseHTMLEditor ) {
4664 kdDebug(5006) << "setting RichText editor" << endl;
4665 mUseHTMLEditor = true; // set it directly to true. setColor hits another toggleMarkup
4666 mHtmlMarkup = true;
4667
4668 // set all highlighted text caused by spelling back to black
4669 int paraFrom, indexFrom, paraTo, indexTo;
4670 mEditor->getSelection ( &paraFrom, &indexFrom, &paraTo, &indexTo);
4671 mEditor->selectAll();
4672 // save the buttonstates because setColor calls fontChanged
4673 bool _bold = textBoldAction->isChecked();
4674 bool _italic = textItalicAction->isChecked();
4675 mEditor->setColor(TQColor(0,0,0));
4676 textBoldAction->setChecked(_bold);
4677 textItalicAction->setChecked(_italic);
4678 mEditor->setSelection ( paraFrom, indexFrom, paraTo, indexTo);
4679
4680 mEditor->setTextFormat(TQt::RichText);
4681 mEditor->setModified(true);
4682 markupAction->setChecked(true);
4683 toolBar( "htmlToolBar" )->show();
4684 mEditor->deleteAutoSpellChecking();
4685 mAutoSpellCheckingAction->setChecked(false);
4686 slotAutoSpellCheckingToggled(false);
4687 }
4688 } else { // markup is to be turned off
4689 kdDebug(5006) << "setting PlainText editor" << endl;
4690 mHtmlMarkup = false;
4691 toolBar("htmlToolBar")->hide();
4692 if ( mUseHTMLEditor ) { // it was turned on
4693 mUseHTMLEditor = false;
4694 mEditor->setTextFormat(TQt::PlainText);
4695 TQString text = mEditor->text();
4696 mEditor->setText(text); // otherwise the text still looks formatted
4697 mEditor->setModified(true);
4698 slotAutoSpellCheckingToggled(true);
4699 }
4700 }
4701}
4702
4703void KMComposeWin::htmlToolBarVisibilityChanged( bool visible )
4704{
4705 // disable markup if the user hides the HTML toolbar
4706 if ( !visible ) {
4707 markupAction->setChecked( false );
4708 toggleMarkup( false );
4709 }
4710}
4711
4712void KMComposeWin::slotSubjectTextSpellChecked()
4713{
4714 mSubjectTextWasSpellChecked = true;
4715}
4716
4717//-----------------------------------------------------------------------------
4718void KMComposeWin::slotAutoSpellCheckingToggled( bool on )
4719{
4720 if ( mEditor->autoSpellChecking(on) == -1 ) {
4721 mAutoSpellCheckingAction->setChecked(false); // set it to false again
4722 }
4723
4724 TQString temp;
4725 if ( on )
4726 temp = i18n( "Spellcheck: on" );
4727 else
4728 temp = i18n( "Spellcheck: off" );
4729 statusBar()->changeItem( temp, 3 );
4730}
4731//-----------------------------------------------------------------------------
4732void KMComposeWin::slotSpellcheck()
4733{
4734 if (mSpellCheckInProgress) return;
4735 mSubjectTextWasSpellChecked = false;
4736 mSpellCheckInProgress=true;
4737 /*
4738 connect (mEditor, TQ_SIGNAL (spellcheck_progress (unsigned)),
4739 this, TQ_SLOT (spell_progress (unsigned)));
4740 */
4741
4742 mEditor->spellcheck();
4743}
4744//-----------------------------------------------------------------------------
4745void KMComposeWin::slotUpdateSignatureActions()
4746{
4747 //Check if an identity has signature or not and turn on/off actions in the
4748 //edit menu accordingly.
4749 const KPIM::Identity & ident =
4750 kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
4751 TQString sig = ident.signatureText();
4752
4753 if ( sig.isEmpty() ) {
4754 mAppendSignatureAction->setEnabled( false );
4755 mPrependSignatureAction->setEnabled( false );
4756 mInsertSignatureAction->setEnabled( false );
4757 }
4758 else {
4759 mAppendSignatureAction->setEnabled( true );
4760 mPrependSignatureAction->setEnabled( true );
4761 mInsertSignatureAction->setEnabled( true );
4762 }
4763}
4764
4765void KMComposeWin::polish()
4766{
4767 // Ensure the html toolbar is appropriately shown/hidden
4768 markupAction->setChecked(mHtmlMarkup);
4769 if (mHtmlMarkup)
4770 toolBar("htmlToolBar")->show();
4771 else
4772 toolBar("htmlToolBar")->hide();
4773 KMail::Composer::polish();
4774}
4775
4776//-----------------------------------------------------------------------------
4777void KMComposeWin::slotSpellcheckDone(int result)
4778{
4779 kdDebug(5006) << "spell check complete: result = " << result << endl;
4780 mSpellCheckInProgress=false;
4781
4782 switch( result )
4783 {
4784 case KS_CANCEL:
4785 statusBar()->changeItem(i18n(" Spell check canceled."),0);
4786 break;
4787 case KS_STOP:
4788 statusBar()->changeItem(i18n(" Spell check stopped."),0);
4789 break;
4790 default:
4791 statusBar()->changeItem(i18n(" Spell check complete."),0);
4792 break;
4793 }
4794 TQTimer::singleShot( 2000, this, TQ_SLOT(slotSpellcheckDoneClearStatus()) );
4795}
4796
4797void KMComposeWin::slotSpellcheckDoneClearStatus()
4798{
4799 statusBar()->changeItem("", 0);
4800}
4801
4802
4803//-----------------------------------------------------------------------------
4804void KMComposeWin::slotIdentityChanged( uint uoid )
4805{
4806 const KPIM::Identity & ident =
4807 kmkernel->identityManager()->identityForUoid( uoid );
4808 if( ident.isNull() ) return;
4809
4810 //Turn on/off signature actions if identity has no signature.
4811 slotUpdateSignatureActions();
4812
4813 if( !ident.fullEmailAddr().isNull() )
4814 mEdtFrom->setText(ident.fullEmailAddr());
4815 // make sure the From field is shown if it does not contain a valid email address
4816 if ( KPIM::getFirstEmailAddress( from() ).isEmpty() )
4817 mShowHeaders |= HDR_FROM;
4818 if ( mEdtReplyTo ) mEdtReplyTo->setText(ident.replyToAddr());
4819
4820 if ( mRecipientsEditor ) {
4821 // remove BCC of old identity and add BCC of new identity (if they differ)
4822 const KPIM::Identity & oldIdentity =
4823 kmkernel->identityManager()->identityForUoidOrDefault( mId );
4824 if ( oldIdentity.bcc() != ident.bcc() ) {
4825 mRecipientsEditor->removeRecipient( oldIdentity.bcc(), Recipient::Bcc );
4826 mRecipientsEditor->addRecipient( ident.bcc(), Recipient::Bcc );
4827 mRecipientsEditor->setFocusBottom();
4828 }
4829 }
4830
4831 // don't overwrite the BCC field under certain circomstances
4832 // NOT edited and preset BCC from the identity
4833 if( mEdtBcc && !mEdtBcc->edited() && !ident.bcc().isEmpty() ) {
4834 // BCC NOT empty AND contains a diff adress then the preset BCC
4835 // of the new identity
4836 if( !mEdtBcc->text().isEmpty() && mEdtBcc->text() != ident.bcc() && !mEdtBcc->edited() ) {
4837 mEdtBcc->setText( ident.bcc() );
4838 } else {
4839 // user type into the editbox an address that != to the preset bcc
4840 // of the identity, we assume that since the user typed it
4841 // they want to keep it
4842 if ( mEdtBcc->text() != ident.bcc() && !mEdtBcc->text().isEmpty() ) {
4843 TQString temp_string( mEdtBcc->text() + TQString::fromLatin1(",") + ident.bcc() );
4844 mEdtBcc->setText( temp_string );
4845 } else {
4846 // if the user typed the same address as the preset BCC
4847 // from the identity we will overwrite it to avoid duplicates.
4848 mEdtBcc->setText( ident.bcc() );
4849 }
4850 }
4851 }
4852 // user edited the bcc box and has a preset bcc in the identity
4853 // we will append whatever the user typed to the preset address
4854 // allowing the user to keep all addresses
4855 if( mEdtBcc && mEdtBcc->edited() && !ident.bcc().isEmpty() ) {
4856 if( !mEdtBcc->text().isEmpty() ) {
4857 TQString temp_string ( mEdtBcc->text() + TQString::fromLatin1(",") + ident.bcc() );
4858 mEdtBcc->setText( temp_string );
4859 } else {
4860 mEdtBcc->setText( ident.bcc() );
4861 }
4862 }
4863 // user typed nothing and the identity does not have a preset bcc
4864 // we then reset the value to get rid of any previous
4865 // values if the user changed identity mid way through.
4866 if( mEdtBcc && !mEdtBcc->edited() && ident.bcc().isEmpty() ) {
4867 mEdtBcc->setText( ident.bcc() );
4868 }
4869 // make sure the BCC field is shown because else it's ignored
4870 if ( !ident.bcc().isEmpty() ) {
4871 mShowHeaders |= HDR_BCC;
4872 }
4873
4874 if ( ident.organization().isEmpty() )
4875 mMsg->removeHeaderField("Organization");
4876 else
4877 mMsg->setHeaderField("Organization", ident.organization());
4878
4879 if (!ident.isXFaceEnabled() || ident.xface().isEmpty())
4880 mMsg->removeHeaderField("X-Face");
4881 else
4882 {
4883 TQString xface = ident.xface();
4884 if (!xface.isEmpty())
4885 {
4886 int numNL = ( xface.length() - 1 ) / 70;
4887 for ( int i = numNL; i > 0; --i )
4888 xface.insert( i*70, "\n\t" );
4889 mMsg->setHeaderField("X-Face", xface);
4890 }
4891 }
4892
4893 if ( !mBtnTransport->isChecked() && !mIgnoreStickyFields ) {
4894 TQString transp = ident.transport();
4895 if ( transp.isEmpty() )
4896 {
4897 mMsg->removeHeaderField("X-KMail-Transport");
4898 transp = GlobalSettings::self()->defaultTransport();
4899 }
4900 else
4901 mMsg->setHeaderField("X-KMail-Transport", transp);
4902 setTransport( transp );
4903 }
4904
4905 if ( !mBtnDictionary->isChecked() && !mIgnoreStickyFields ) {
4906 mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
4907 }
4908
4909 if ( !mBtnFcc->isChecked() && !mPreventFccOverwrite ) {
4910 setFcc( ident.fcc() );
4911 }
4912
4913 TQString edtText = mEditor->text();
4914
4915 if ( mOldSigText.isEmpty() ) {
4916 const KPIM::Identity &id =
4917 kmkernel->
4918 identityManager()->
4919 identityForUoidOrDefault( mMsg->headerField( "X-KMail-Identity" ).
4920 stripWhiteSpace().toUInt() );
4921 mOldSigText = GlobalSettings::self()->prependSignature() ? id.signature().rawText() : id.signatureText();
4922 }
4923
4924
4925 if ( !GlobalSettings::prependSignature() ) {
4926 // try to truncate the old sig
4927 // First remove any trailing whitespace
4928 while ( !edtText.isEmpty() && edtText[edtText.length()-1].isSpace() )
4929 edtText.truncate( edtText.length() - 1 );
4930 // From the sig too, just in case
4931 while ( !mOldSigText.isEmpty() && mOldSigText[mOldSigText.length()-1].isSpace() )
4932 mOldSigText.truncate( mOldSigText.length() - 1 );
4933
4934 if ( edtText.endsWith( mOldSigText ) )
4935 edtText.truncate( edtText.length() - mOldSigText.length() );
4936
4937 // now append the new sig
4938 mOldSigText = ident.signatureText();
4939 if( ( !mOldSigText.isEmpty() ) &&
4940 ( GlobalSettings::self()->autoTextSignature() == "auto" ) ) {
4941 edtText.append( mOldSigText );
4942 }
4943 mEditor->setText( edtText );
4944 } else {
4945 const int pos = edtText.find( mOldSigText );
4946 if ( pos >= 0 && !mOldSigText.isEmpty() ) {
4947 const int oldLength = mOldSigText.length();
4948 mOldSigText = "\n\n"+ ident.signature().rawText() + "\n"; // see insertSignature()
4949 edtText = edtText.replace( pos, oldLength, mOldSigText );
4950 mEditor->setText( edtText );
4951 } else {
4952 insertSignature( Append );
4953 }
4954 }
4955
4956 // disable certain actions if there is no PGP user identity set
4957 // for this profile
4958 bool bNewIdentityHasSigningKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
4959 bool bNewIdentityHasEncryptionKey = !ident.pgpSigningKey().isEmpty() || !ident.smimeSigningKey().isEmpty();
4960 mAttachMPK->setEnabled( Kleo::CryptoBackendFactory::instance()->openpgp() &&
4961 !ident.pgpEncryptionKey().isEmpty() );
4962 // save the state of the sign and encrypt button
4963 if ( !bNewIdentityHasEncryptionKey && mLastIdentityHasEncryptionKey ) {
4964 mLastEncryptActionState = mEncryptAction->isChecked();
4965 setEncryption( false );
4966 }
4967 if ( !bNewIdentityHasSigningKey && mLastIdentityHasSigningKey ) {
4968 mLastSignActionState = mSignAction->isChecked();
4969 setSigning( false );
4970 }
4971 // restore the last state of the sign and encrypt button
4972 if ( bNewIdentityHasEncryptionKey && !mLastIdentityHasEncryptionKey )
4973 setEncryption( mLastEncryptActionState );
4974 if ( bNewIdentityHasSigningKey && !mLastIdentityHasSigningKey )
4975 setSigning( mLastSignActionState );
4976
4977 mLastIdentityHasSigningKey = bNewIdentityHasSigningKey;
4978 mLastIdentityHasEncryptionKey = bNewIdentityHasEncryptionKey;
4979
4980 setModified( true );
4981 mId = uoid;
4982
4983 // make sure the From and BCC fields are shown if necessary
4984 rethinkFields( false );
4985}
4986
4987//-----------------------------------------------------------------------------
4988void KMComposeWin::slotSpellcheckConfig()
4989{
4990 KDialogBase dlg(KDialogBase::Plain, i18n("Spellchecker"),
4991 KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok,
4992 this, 0, true, true );
4993 KWin twin;
4994 TQTabDialog qtd (this, "tabdialog", true);
4995 KSpellConfig mKSpellConfig (&qtd);
4996 mKSpellConfig.layout()->setMargin( KDialog::marginHint() );
4997
4998 qtd.addTab (&mKSpellConfig, i18n("Spellchecker"));
4999 qtd.setCancelButton ();
5000
5001 twin.setIcons (qtd.winId(), tdeApp->icon(), tdeApp->miniIcon());
5002 qtd.setCancelButton(KStdGuiItem::cancel().text());
5003 qtd.setOkButton(KStdGuiItem::ok().text());
5004
5005 if (qtd.exec())
5006 mKSpellConfig.writeGlobalSettings();
5007}
5008
5009//-----------------------------------------------------------------------------
5010void KMComposeWin::slotStatusMessage(const TQString &message)
5011{
5012 statusBar()->changeItem( message, 0 );
5013}
5014
5015void KMComposeWin::slotEditToolbars()
5016{
5017 saveMainWindowSettings(KMKernel::config(), "Composer");
5018 KEditToolbar dlg(guiFactory(), this);
5019
5020 connect( &dlg, TQ_SIGNAL(newToolbarConfig()),
5021 TQ_SLOT(slotUpdateToolbars()) );
5022
5023 dlg.exec();
5024}
5025
5026void KMComposeWin::slotUpdateToolbars()
5027{
5028 createGUI("kmcomposerui.rc");
5029 applyMainWindowSettings(KMKernel::config(), "Composer");
5030}
5031
5032void KMComposeWin::slotEditKeys()
5033{
5034 KKeyDialog::configure( actionCollection(),
5035 false /*don't allow one-letter shortcuts*/
5036 );
5037}
5038
5039void KMComposeWin::setReplyFocus( bool hasMessage )
5040{
5041 mEditor->setFocus();
5042 if ( hasMessage ) {
5043 if( mMsg->getCursorPos() ) {
5044 mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() );
5045 } else {
5046 mEditor->setCursorPosition( 1, 0 );
5047 }
5048 }
5049}
5050
5051void KMComposeWin::setFocusToSubject()
5052{
5053 mEdtSubject->setFocus();
5054}
5055
5056int KMComposeWin::autoSaveInterval() const
5057{
5058 return GlobalSettings::self()->autosaveInterval() * 1000 * 60;
5059}
5060
5061void KMComposeWin::initAutoSave()
5062{
5063 kdDebug(5006) << k_funcinfo << endl;
5064 // make sure the autosave folder exists
5065 KMFolderMaildir::createMaildirFolders( KMKernel::localDataPath() + "autosave" );
5066 if ( mAutoSaveFilename.isEmpty() ) {
5067 mAutoSaveFilename = KMFolderMaildir::constructValidFileName();
5068 }
5069
5070 updateAutoSave();
5071}
5072
5073void KMComposeWin::updateAutoSave()
5074{
5075 if ( autoSaveInterval() == 0 ) {
5076 delete mAutoSaveTimer; mAutoSaveTimer = 0;
5077 }
5078 else {
5079 if ( !mAutoSaveTimer ) {
5080 mAutoSaveTimer = new TQTimer( this, "mAutoSaveTimer" );
5081 connect( mAutoSaveTimer, TQ_SIGNAL( timeout() ),
5082 this, TQ_SLOT( autoSaveMessage() ) );
5083 }
5084 mAutoSaveTimer->start( autoSaveInterval() );
5085 }
5086}
5087
5088void KMComposeWin::setAutoSaveFilename( const TQString & filename )
5089{
5090 mAutoSaveFilename = filename;
5091}
5092
5093void KMComposeWin::cleanupAutoSave()
5094{
5095 delete mAutoSaveTimer; mAutoSaveTimer = 0;
5096 if ( !mAutoSaveFilename.isEmpty() ) {
5097 kdDebug(5006) << k_funcinfo << "deleting autosave file "
5098 << mAutoSaveFilename << endl;
5099 KMFolderMaildir::removeFile( KMKernel::localDataPath() + "autosave",
5100 mAutoSaveFilename );
5101 mAutoSaveFilename = TQString();
5102 }
5103}
5104
5105void KMComposeWin::slotCompletionModeChanged( TDEGlobalSettings::Completion mode)
5106{
5107 GlobalSettings::self()->setCompletionMode( (int) mode );
5108
5109 // sync all the lineedits to the same completion mode
5110 mEdtFrom->setCompletionMode( mode );
5111 mEdtReplyTo->setCompletionMode( mode );
5112 if ( mClassicalRecipients ) {
5113 mEdtTo->setCompletionMode( mode );
5114 mEdtCc->setCompletionMode( mode );
5115 mEdtBcc->setCompletionMode( mode );
5116 }else
5117 mRecipientsEditor->setCompletionMode( mode );
5118}
5119
5120void KMComposeWin::slotConfigChanged()
5121{
5122 readConfig( true /*reload*/);
5123 updateAutoSave();
5124 rethinkFields();
5125 slotWordWrapToggled( mWordWrapAction->isChecked() );
5126}
5127
5128/*
5129* checks if the drafts-folder has been deleted
5130* that is not nice so we set the system-drafts-folder
5131*/
5132void KMComposeWin::slotFolderRemoved(KMFolder* folder)
5133{
5134 // TODO: need to handle templates here?
5135 if ( (mFolder) && (folder->idString() == mFolder->idString()) )
5136 {
5137 mFolder = kmkernel->draftsFolder();
5138 kdDebug(5006) << "restoring drafts to " << mFolder->idString() << endl;
5139 }
5140 if (mMsg) mMsg->setParent(0);
5141}
5142
5143
5144void KMComposeWin::editorFocusChanged(bool gained)
5145{
5146 mPasteQuotation->setEnabled(gained);
5147}
5148
5149void KMComposeWin::slotSetAlwaysSend( bool bAlways )
5150{
5151 mAlwaysSend = bAlways;
5152}
5153
5154void KMComposeWin::slotListAction( const TQString& style )
5155{
5156 toggleMarkup(true);
5157 if ( style == i18n( "Standard" ) )
5158 mEditor->setParagType( TQStyleSheetItem::DisplayBlock, TQStyleSheetItem::ListDisc );
5159 else if ( style == i18n( "Bulleted List (Disc)" ) )
5160 mEditor->setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListDisc );
5161 else if ( style == i18n( "Bulleted List (Circle)" ) )
5162 mEditor->setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListCircle );
5163 else if ( style == i18n( "Bulleted List (Square)" ) )
5164 mEditor->setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListSquare );
5165 else if ( style == i18n( "Ordered List (Decimal)" ))
5166 mEditor->setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListDecimal );
5167 else if ( style == i18n( "Ordered List (Alpha lower)" ) )
5168 mEditor->setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListLowerAlpha );
5169 else if ( style == i18n( "Ordered List (Alpha upper)" ) )
5170 mEditor->setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListUpperAlpha );
5171 mEditor->viewport()->setFocus();
5172}
5173
5174void KMComposeWin::slotFontAction( const TQString& font)
5175{
5176 toggleMarkup(true);
5177 mEditor->TQTextEdit::setFamily( font );
5178 mEditor->viewport()->setFocus();
5179}
5180
5181void KMComposeWin::slotSizeAction( int size )
5182{
5183 toggleMarkup(true);
5184 mEditor->setPointSize( size );
5185 mEditor->viewport()->setFocus();
5186}
5187
5188void KMComposeWin::slotAlignLeft()
5189{
5190 toggleMarkup(true);
5191 mEditor->TQTextEdit::setAlignment( AlignLeft );
5192}
5193
5194void KMComposeWin::slotAlignCenter()
5195{
5196 toggleMarkup(true);
5197 mEditor->TQTextEdit::setAlignment( AlignHCenter );
5198}
5199
5200void KMComposeWin::slotAlignRight()
5201{
5202 toggleMarkup(true);
5203 mEditor->TQTextEdit::setAlignment( AlignRight );
5204}
5205
5206void KMComposeWin::slotTextBold()
5207{
5208 toggleMarkup(true);
5209 mEditor->TQTextEdit::setBold( textBoldAction->isChecked() );
5210}
5211
5212void KMComposeWin::slotTextItalic()
5213{
5214 toggleMarkup(true);
5215 mEditor->TQTextEdit::setItalic( textItalicAction->isChecked() );
5216}
5217
5218void KMComposeWin::slotTextUnder()
5219{
5220 toggleMarkup(true);
5221 mEditor->TQTextEdit::setUnderline( textUnderAction->isChecked() );
5222}
5223
5224void KMComposeWin::slotFormatReset()
5225{
5226 mEditor->setColor(mForeColor);
5227 mEditor->setCurrentFont( mSaveFont ); // fontChanged is called now
5228}
5229void KMComposeWin::slotTextColor()
5230{
5231 TQColor color = mEditor->color();
5232
5233 if ( KColorDialog::getColor( color, this ) ) {
5234 toggleMarkup(true);
5235 mEditor->setColor( color );
5236 }
5237}
5238
5239void KMComposeWin::fontChanged( const TQFont &f )
5240{
5241 TQFont fontTemp = f;
5242 fontTemp.setBold( true );
5243 fontTemp.setItalic( true );
5244 TQFontInfo fontInfo( fontTemp );
5245
5246 if ( fontInfo.bold() ) {
5247 textBoldAction->setChecked( f.bold() );
5248 textBoldAction->setEnabled( true ) ;
5249 } else {
5250 textBoldAction->setEnabled( false );
5251 }
5252
5253 if ( fontInfo.italic() ) {
5254 textItalicAction->setChecked( f.italic() );
5255 textItalicAction->setEnabled( true ) ;
5256 } else {
5257 textItalicAction->setEnabled( false );
5258 }
5259
5260 textUnderAction->setChecked( f.underline() );
5261
5262 fontAction->setFont( f.family() );
5263 fontSizeAction->setFontSize( f.pointSize() );
5264}
5265
5266void KMComposeWin::alignmentChanged( int a )
5267{
5268 //toggleMarkup();
5269 alignLeftAction->setChecked( ( a == AlignAuto ) || ( a & AlignLeft ) );
5270 alignCenterAction->setChecked( ( a & AlignHCenter ) );
5271 alignRightAction->setChecked( ( a & AlignRight ) );
5272}
5273
5274namespace {
5275 class TDEToggleActionResetter {
5276 TDEToggleAction * mAction;
5277 bool mOn;
5278 public:
5279 TDEToggleActionResetter( TDEToggleAction * action, bool on )
5280 : mAction( action ), mOn( on ) {}
5281 ~TDEToggleActionResetter() {
5282 if ( mAction )
5283 mAction->setChecked( mOn );
5284 }
5285 void disable() { mAction = 0; }
5286 };
5287}
5288
5289void KMComposeWin::slotEncryptChiasmusToggled( bool on ) {
5290 mEncryptWithChiasmus = false;
5291
5292 if ( !on )
5293 return;
5294
5295 TDEToggleActionResetter resetter( mEncryptChiasmusAction, false );
5296
5297 const Kleo::CryptoBackend::Protocol * chiasmus =
5298 Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
5299
5300 if ( !chiasmus ) {
5301 const TQString msg = Kleo::CryptoBackendFactory::instance()->knowsAboutProtocol( "Chiasmus" )
5302 ? i18n( "Please configure a Crypto Backend to use for "
5303 "Chiasmus encryption first.\n"
5304 "You can do this in the Crypto Backends tab of "
5305 "the configure dialog's Security page." )
5306 : i18n( "It looks as though libkleopatra was compiled without "
5307 "Chiasmus support. You might want to recompile "
5308 "libkleopatra with --enable-chiasmus.");
5309 KMessageBox::information( this, msg, i18n("No Chiasmus Backend Configured" ) );
5310 return;
5311 }
5312
5313 STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-obtain-keys", TQStringVariantMap() ) );
5314 if ( !job ) {
5315 const TQString msg = i18n( "Chiasmus backend does not offer the "
5316 "\"x-obtain-keys\" function. Please report this bug." );
5317 KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
5318 return;
5319 }
5320
5321 if ( job->exec() ) {
5322 job->showErrorDialog( this, i18n( "Chiasmus Backend Error" ) );
5323 return;
5324 }
5325
5326 const TQVariant result = job->property( "result" );
5327 if ( result.type() != TQVariant::StringList ) {
5328 const TQString msg = i18n( "Unexpected return value from Chiasmus backend: "
5329 "The \"x-obtain-keys\" function did not return a "
5330 "string list. Please report this bug." );
5331 KMessageBox::error( this, msg, i18n( "Chiasmus Backend Error" ) );
5332 return;
5333 }
5334
5335 const TQStringList keys = result.toStringList();
5336 if ( keys.empty() ) {
5337 const TQString msg = i18n( "No keys have been found. Please check that a "
5338 "valid key path has been set in the Chiasmus "
5339 "configuration." );
5340 KMessageBox::information( this, msg, i18n( "No Chiasmus Keys Found" ) );
5341 return;
5342 }
5343
5344 ChiasmusKeySelector selectorDlg( this, i18n( "Chiasmus Encryption Key Selection" ),
5345 keys, GlobalSettings::chiasmusKey(),
5346 GlobalSettings::chiasmusOptions() );
5347 if ( selectorDlg.exec() != TQDialog::Accepted )
5348 return;
5349
5350 GlobalSettings::setChiasmusOptions( selectorDlg.options() );
5351 GlobalSettings::setChiasmusKey( selectorDlg.key() );
5352 assert( !GlobalSettings::chiasmusKey().isEmpty() );
5353 mEncryptWithChiasmus = true;
5354 resetter.disable();
5355}
5356
5357void KMComposeWin::slotEditDone(KMail::EditorWatcher * watcher)
5358{
5359 kdDebug(5006) << k_funcinfo << endl;
5360 KMMessagePart *part = mEditorMap[ watcher ];
5361 KTempFile *tf = mEditorTempFiles[ watcher ];
5362 mEditorMap.remove( watcher );
5363 mEditorTempFiles.remove( watcher );
5364 if ( !watcher->fileChanged() )
5365 return;
5366
5367 tf->file()->reset();
5368 TQByteArray data = tf->file()->readAll();
5369 part->setBodyEncodedBinary( data );
5370}
5371
5372
5373void KMComposeWin::slotUpdateSignatureAndEncrypionStateIndicators()
5374{
5375 const bool showIndicatorsAlways = false; // FIXME config option?
5376 mSignatureStateIndicator->setText( mSignAction->isChecked()? i18n("Message will be signed") : i18n("Message will not be signed") );
5377 mEncryptionStateIndicator->setText( mEncryptAction->isChecked()? i18n("Message will be encrypted") : i18n("Message will not be encrypted") );
5378 if ( !showIndicatorsAlways ) {
5379 mSignatureStateIndicator->setShown( mSignAction->isChecked() );
5380 mEncryptionStateIndicator->setShown( mEncryptAction->isChecked() );
5381 }
5382}
5383
5384void KMComposeWin::slotAttachmentDragStarted()
5385{
5386 kdDebug(5006) << k_funcinfo << endl;
5387 int idx = 0;
5388 TQStringList filenames;
5389 for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ++it, ++idx ) {
5390 if ( (*it)->isSelected() ) {
5391 KMMessagePart* msgPart = mAtmList.at(idx);
5392 KTempDir * tempDir = new KTempDir(); // will be deleted on composer close
5393 tempDir->setAutoDelete( true );
5394 mTempDirs.insert( tempDir );
5395 const TQString fileName = tempDir->name() + "/" + msgPart->name();
5396 KPIM::kByteArrayToFile(msgPart->bodyDecodedBinary(),
5397 fileName,
5398 false, false, false);
5399 KURL url;
5400 url.setPath( fileName );
5401 filenames << url.path();
5402 }
5403 }
5404 if ( filenames.isEmpty() ) return;
5405
5406 TQUriDrag *drag = new TQUriDrag( mAtmListView );
5407 drag->setFileNames( filenames );
5408 drag->dragCopy();
5409}
5410
5411void KMComposeWin::recipientEditorSizeHintChanged()
5412{
5413 TQTimer::singleShot( 1, this, TQ_SLOT(setMaximumHeaderSize()) );
5414}
5415
5416void KMComposeWin::setMaximumHeaderSize()
5417{
5418 mHeadersArea->setMaximumHeight( mHeadersArea->sizeHint().height() );
5419}
5420
Provides encoding detection capabilities.
const char * encoding() const
Convenience method.
bool analyze(const char *data, int len)
Analyze text data.
sets a cursor and makes sure it's restored on destruction Create a KCursorSaver object when you want ...
Mail folder.
Definition kmfolder.h:69
TQString idString() const
Returns a string that can be used to identify this folder.
Definition kmfolder.cpp:705
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage
Definition kmfolder.cpp:326
int addMsg(KMMessage *msg, int *index_return=0)
Add the given message to the folder.
Definition kmfolder.cpp:390
void close(const char *owner, bool force=false)
Close folder.
Definition kmfolder.cpp:489
KMMessage * getMsg(int idx)
Read message at given index.
Definition kmfolder.cpp:321
int count(bool cache=false) const
Number of messages in this folder.
Definition kmfolder.cpp:445
int open(const char *owner)
Open folder for access.
Definition kmfolder.cpp:479
static TQString localDataPath()
Returns the full path of the user's local data directory for KMail.
This is a Mime Message.
Definition kmmessage.h:68
static KPIM::EmailParseResult isValidEmailAddressList(const TQString &aStr, TQString &brokenAddress)
Validate a list of email addresses, and also allow aliases and distribution lists to be expanded befo...
bool isUrgent() const
static TQString smartQuote(const TQString &msg, int maxLineLength)
Given argument msg add quoting characters and relayout for max width maxLength.
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
static TQString expandAliases(const TQString &recipients)
Expands aliases (distribution lists and nick names) and appends a domain part to all email addresses ...
const DwString & asDwString() const
Return the entire message contents in the DwString.
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
static TQCString defaultCharset()
Get the default message charset.
TQString fcc() const
Get or set the 'Fcc' header field.
void initHeader(uint identity=0)
Initialize header fields.
The attachment dialog with convenience backward compatible methods.
A combo box for selecting the dictionary used for spell checking.
Starts an editor for the given URL and emits an signal when editing has been finished.
static TQStringList transportNames()
Returns the list for transport names.
DCOP interface for mail composer window.
virtual void setBody(TQString body)=0
Set message body.
virtual void addAttachment(KURL url, TQString comment)=0
Add url as attachment with a user-defined comment.
virtual void send(int how)=0
Send message.
This is the widget which gets added to the right TreeToolView.
folderdiaquotatab.h
Definition aboutdata.cpp:40