kmail

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