kmail

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