kmail

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