kmail

kmreaderwin.cpp
1 // -*- mode: C++; c-file-style: "gnu" -*-
2 // kmreaderwin.cpp
3 // Author: Markus Wuebben <markus.wuebben@kde.org>
4 
5 // define this to copy all html that is written to the readerwindow to
6 // filehtmlwriter.out in the current working directory
7 //#define KMAIL_READER_HTML_DEBUG 1
8 
9 #include <config.h>
10 
11 #include "kmreaderwin.h"
12 
13 #include "globalsettings.h"
14 #include "kmversion.h"
15 #include "kmmainwidget.h"
16 #include "kmreadermainwin.h"
17 #include <libkdepim/kfileio.h>
18 #include "kmfolderindex.h"
19 #include "kmcommands.h"
20 #include "kmmsgpartdlg.h"
21 #include "mailsourceviewer.h"
22 using KMail::MailSourceViewer;
23 #include "partNode.h"
24 #include "kmmsgdict.h"
25 #include "messagesender.h"
26 #include "kcursorsaver.h"
27 #include "kmfolder.h"
28 #include "vcardviewer.h"
29 using KMail::VCardViewer;
30 #include "objecttreeparser.h"
31 using KMail::ObjectTreeParser;
32 #include "partmetadata.h"
33 using KMail::PartMetaData;
34 #include "attachmentstrategy.h"
35 using KMail::AttachmentStrategy;
36 #include "headerstrategy.h"
37 using KMail::HeaderStrategy;
38 #include "headerstyle.h"
39 using KMail::HeaderStyle;
40 #include "khtmlparthtmlwriter.h"
41 using KMail::HtmlWriter;
42 using KMail::KHtmlPartHtmlWriter;
43 #include "htmlstatusbar.h"
45 #include "folderjob.h"
46 using KMail::FolderJob;
47 #include "csshelper.h"
48 using KMail::CSSHelper;
49 #include "isubject.h"
50 using KMail::ISubject;
51 #include "urlhandlermanager.h"
53 #include "interfaces/observable.h"
54 #include "util.h"
55 #include "kmheaders.h"
56 
57 #include "broadcaststatus.h"
58 
59 #include <kmime_mdn.h>
60 using namespace KMime;
61 #ifdef KMAIL_READER_HTML_DEBUG
62 #include "filehtmlwriter.h"
63 using KMail::FileHtmlWriter;
64 #include "teehtmlwriter.h"
66 #endif
67 
68 #include <kasciistringtools.h>
69 #include <kstringhandler.h>
70 
71 #include <mimelib/mimepp.h>
72 #include <mimelib/body.h>
73 #include <mimelib/utility.h>
74 
75 #include <kleo/specialjob.h>
76 #include <kleo/cryptobackend.h>
77 #include <kleo/cryptobackendfactory.h>
78 
79 // KABC includes
80 #include <kabc/addressee.h>
81 #include <kabc/vcardconverter.h>
82 
83 // khtml headers
84 #include <khtml_part.h>
85 #include <khtmlview.h> // So that we can get rid of the frames
86 #include <dom/html_element.h>
87 #include <dom/html_block.h>
88 #include <dom/html_document.h>
89 #include <dom/dom_string.h>
90 #include <dom/dom_exception.h>
91 
92 #include <kapplication.h>
93 // for the click on attachment stuff (dnaber):
94 #include <kuserprofile.h>
95 #include <kcharsets.h>
96 #include <kpopupmenu.h>
97 #include <kstandarddirs.h> // Sven's : for access and getpid
98 #include <kcursor.h>
99 #include <kdebug.h>
100 #include <kfiledialog.h>
101 #include <klocale.h>
102 #include <kmessagebox.h>
103 #include <kglobalsettings.h>
104 #include <krun.h>
105 #include <ktempfile.h>
106 #include <kprocess.h>
107 #include <kdialog.h>
108 #include <kaction.h>
109 #include <kiconloader.h>
110 #include <kmdcodec.h>
111 #include <kasciistricmp.h>
112 #include <kurldrag.h>
113 
114 #include <tqclipboard.h>
115 #include <tqhbox.h>
116 #include <tqtextcodec.h>
117 #include <tqpaintdevicemetrics.h>
118 #include <tqlayout.h>
119 #include <tqlabel.h>
120 #include <tqsplitter.h>
121 #include <tqstyle.h>
122 
123 // X headers...
124 #undef Never
125 #undef Always
126 
127 #include <unistd.h>
128 #include <stdlib.h>
129 #include <sys/stat.h>
130 #include <errno.h>
131 #include <stdio.h>
132 #include <ctype.h>
133 #include <string.h>
134 
135 #ifdef HAVE_PATHS_H
136 #include <paths.h>
137 #endif
138 
139 class NewByteArray : public TQByteArray
140 {
141 public:
142  NewByteArray &appendNULL();
143  NewByteArray &operator+=( const char * );
144  NewByteArray &operator+=( const TQByteArray & );
145  NewByteArray &operator+=( const TQCString & );
146  TQByteArray& qByteArray();
147 };
148 
149 NewByteArray& NewByteArray::appendNULL()
150 {
151  TQByteArray::detach();
152  uint len1 = size();
153  if ( !TQByteArray::resize( len1 + 1 ) )
154  return *this;
155  *(data() + len1) = '\0';
156  return *this;
157 }
158 NewByteArray& NewByteArray::operator+=( const char * newData )
159 {
160  if ( !newData )
161  return *this;
162  TQByteArray::detach();
163  uint len1 = size();
164  uint len2 = tqstrlen( newData );
165  if ( !TQByteArray::resize( len1 + len2 ) )
166  return *this;
167  memcpy( data() + len1, newData, len2 );
168  return *this;
169 }
170 NewByteArray& NewByteArray::operator+=( const TQByteArray & newData )
171 {
172  if ( newData.isNull() )
173  return *this;
174  TQByteArray::detach();
175  uint len1 = size();
176  uint len2 = newData.size();
177  if ( !TQByteArray::resize( len1 + len2 ) )
178  return *this;
179  memcpy( data() + len1, newData.data(), len2 );
180  return *this;
181 }
182 NewByteArray& NewByteArray::operator+=( const TQCString & newData )
183 {
184  if ( newData.isEmpty() )
185  return *this;
186  TQByteArray::detach();
187  uint len1 = size();
188  uint len2 = newData.length(); // forget about the trailing 0x00 !
189  if ( !TQByteArray::resize( len1 + len2 ) )
190  return *this;
191  memcpy( data() + len1, newData.data(), len2 );
192  return *this;
193 }
194 TQByteArray& NewByteArray::qByteArray()
195 {
196  return *((TQByteArray*)this);
197 }
198 
199 // This function returns the complete data that were in this
200 // message parts - *after* all encryption has been removed that
201 // could be removed.
202 // - This is used to store the message in decrypted form.
203 void KMReaderWin::objectTreeToDecryptedMsg( partNode* node,
204  NewByteArray& resultingData,
205  KMMessage& theMessage,
206  bool weAreReplacingTheRootNode,
207  int recCount )
208 {
209  kdDebug(5006) << TQString("-------------------------------------------------" ) << endl;
210  kdDebug(5006) << TQString("KMReaderWin::objectTreeToDecryptedMsg( %1 ) START").arg( recCount ) << endl;
211  if( node ) {
212 
213  kdDebug(5006) << node->typeString() << '/' << node->subTypeString() << endl;
214 
215  partNode* curNode = node;
216  partNode* dataNode = curNode;
217  partNode * child = node->firstChild();
218  const bool bIsMultipart = node->type() == DwMime::kTypeMultipart ;
219  bool bKeepPartAsIs = false;
220 
221  switch( curNode->type() ){
222  case DwMime::kTypeMultipart: {
223  switch( curNode->subType() ){
224  case DwMime::kSubtypeSigned: {
225  bKeepPartAsIs = true;
226  }
227  break;
228  case DwMime::kSubtypeEncrypted: {
229  if ( child )
230  dataNode = child;
231  }
232  break;
233  }
234  }
235  break;
236  case DwMime::kTypeMessage: {
237  switch( curNode->subType() ){
238  case DwMime::kSubtypeRfc822: {
239  if ( child )
240  dataNode = child;
241  }
242  break;
243  }
244  }
245  break;
246  case DwMime::kTypeApplication: {
247  switch( curNode->subType() ){
248  case DwMime::kSubtypeOctetStream: {
249  if ( child )
250  dataNode = child;
251  }
252  break;
253  case DwMime::kSubtypePkcs7Signature: {
254  // note: subtype Pkcs7Signature specifies a signature part
255  // which we do NOT want to remove!
256  bKeepPartAsIs = true;
257  }
258  break;
259  case DwMime::kSubtypePkcs7Mime: {
260  // note: subtype Pkcs7Mime can also be signed
261  // and we do NOT want to remove the signature!
262  if ( child && curNode->encryptionState() != KMMsgNotEncrypted )
263  dataNode = child;
264  }
265  break;
266  }
267  }
268  break;
269  }
270 
271 
272  DwHeaders& rootHeaders( theMessage.headers() );
273  DwBodyPart * part = dataNode->dwPart() ? dataNode->dwPart() : 0;
274  DwHeaders * headers(
275  (part && part->hasHeaders())
276  ? &part->Headers()
277  : ( (weAreReplacingTheRootNode || !dataNode->parentNode())
278  ? &rootHeaders
279  : 0 ) );
280  if( dataNode == curNode ) {
281 kdDebug(5006) << "dataNode == curNode: Save curNode without replacing it." << endl;
282 
283  // A) Store the headers of this part IF curNode is not the root node
284  // AND we are not replacing a node that already *has* replaced
285  // the root node in previous recursion steps of this function...
286  if( headers ) {
287  if( dataNode->parentNode() && !weAreReplacingTheRootNode ) {
288 kdDebug(5006) << "dataNode is NOT replacing the root node: Store the headers." << endl;
289  resultingData += headers->AsString().c_str();
290  } else if( weAreReplacingTheRootNode && part && part->hasHeaders() ){
291 kdDebug(5006) << "dataNode replace the root node: Do NOT store the headers but change" << endl;
292 kdDebug(5006) << " the Message's headers accordingly." << endl;
293 kdDebug(5006) << " old Content-Type = " << rootHeaders.ContentType().AsString().c_str() << endl;
294 kdDebug(5006) << " new Content-Type = " << headers->ContentType( ).AsString().c_str() << endl;
295  rootHeaders.ContentType() = headers->ContentType();
296  theMessage.setContentTransferEncodingStr(
297  headers->HasContentTransferEncoding()
298  ? headers->ContentTransferEncoding().AsString().c_str()
299  : "" );
300  rootHeaders.ContentDescription() = headers->ContentDescription();
301  rootHeaders.ContentDisposition() = headers->ContentDisposition();
302  theMessage.setNeedsAssembly();
303  }
304  }
305 
306  if ( bKeepPartAsIs ) {
307  resultingData += dataNode->encodedBody();
308  } else {
309 
310  // B) Store the body of this part.
311  if( headers && bIsMultipart && dataNode->firstChild() ) {
312 kdDebug(5006) << "is valid Multipart, processing children:" << endl;
313  TQCString boundary = headers->ContentType().Boundary().c_str();
314  curNode = dataNode->firstChild();
315  // store children of multipart
316  while( curNode ) {
317 kdDebug(5006) << "--boundary" << endl;
318  if( resultingData.size() &&
319  ( '\n' != resultingData.at( resultingData.size()-1 ) ) )
320  resultingData += TQCString( "\n" );
321  resultingData += TQCString( "\n" );
322  resultingData += "--";
323  resultingData += boundary;
324  resultingData += "\n";
325  // note: We are processing a harmless multipart that is *not*
326  // to be replaced by one of it's children, therefor
327  // we set their doStoreHeaders to true.
328  objectTreeToDecryptedMsg( curNode,
329  resultingData,
330  theMessage,
331  false,
332  recCount + 1 );
333  curNode = curNode->nextSibling();
334  }
335 kdDebug(5006) << "--boundary--" << endl;
336  resultingData += "\n--";
337  resultingData += boundary;
338  resultingData += "--\n\n";
339 kdDebug(5006) << "Multipart processing children - DONE" << endl;
340  } else if( part ){
341  // store simple part
342 kdDebug(5006) << "is Simple part or invalid Multipart, storing body data .. DONE" << endl;
343  resultingData += part->Body().AsString().c_str();
344  }
345  }
346  } else {
347 kdDebug(5006) << "dataNode != curNode: Replace curNode by dataNode." << endl;
348  bool rootNodeReplaceFlag = weAreReplacingTheRootNode || !curNode->parentNode();
349  if( rootNodeReplaceFlag ) {
350 kdDebug(5006) << " Root node will be replaced." << endl;
351  } else {
352 kdDebug(5006) << " Root node will NOT be replaced." << endl;
353  }
354  // store special data to replace the current part
355  // (e.g. decrypted data or embedded RfC 822 data)
356  objectTreeToDecryptedMsg( dataNode,
357  resultingData,
358  theMessage,
359  rootNodeReplaceFlag,
360  recCount + 1 );
361  }
362  }
363  kdDebug(5006) << TQString("\nKMReaderWin::objectTreeToDecryptedMsg( %1 ) END").arg( recCount ) << endl;
364 }
365 
366 
367 /*
368  ===========================================================================
369 
370 
371  E N D O F T E M P O R A R Y M I M E C O D E
372 
373 
374  ===========================================================================
375 */
376 
377 
378 
379 
380 
381 
382 
383 
384 
385 
386 
387 void KMReaderWin::createWidgets() {
388  TQVBoxLayout * vlay = new TQVBoxLayout( this );
389  mSplitter = new TQSplitter( Qt::Vertical, this, "mSplitter" );
390  vlay->addWidget( mSplitter );
391  mMimePartTree = new KMMimePartTree( this, mSplitter, "mMimePartTree" );
392  mBox = new TQHBox( mSplitter, "mBox" );
393  setStyleDependantFrameWidth();
394  mBox->setFrameStyle( mMimePartTree->frameStyle() );
395  mColorBar = new HtmlStatusBar( mBox, "mColorBar" );
396  mViewer = new KHTMLPart( mBox, "mViewer" );
397  mSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
398  mSplitter->setResizeMode( mMimePartTree, TQSplitter::KeepSize );
399 }
400 
401 const int KMReaderWin::delay = 150;
402 
403 //-----------------------------------------------------------------------------
404 KMReaderWin::KMReaderWin(TQWidget *aParent,
405  TQWidget *mainWindow,
406  KActionCollection* actionCollection,
407  const char *aName,
408  int aFlags )
409  : TQWidget(aParent, aName, aFlags | TQt::WDestructiveClose),
410  mSerNumOfOriginalMessage( 0 ),
411  mNodeIdOffset( -1 ),
412  mAttachmentStrategy( 0 ),
413  mHeaderStrategy( 0 ),
414  mHeaderStyle( 0 ),
415  mUpdateReaderWinTimer( 0, "mUpdateReaderWinTimer" ),
416  mResizeTimer( 0, "mResizeTimer" ),
417  mDelayedMarkTimer( 0, "mDelayedMarkTimer" ),
418  mOldGlobalOverrideEncoding( "---" ), // init with dummy value
419  mCSSHelper( 0 ),
420  mRootNode( 0 ),
421  mMainWindow( mainWindow ),
422  mActionCollection( actionCollection ),
423  mMailToComposeAction( 0 ),
424  mMailToReplyAction( 0 ),
425  mMailToForwardAction( 0 ),
426  mAddAddrBookAction( 0 ),
427  mOpenAddrBookAction( 0 ),
428  mCopyAction( 0 ),
429  mCopyURLAction( 0 ),
430  mUrlOpenAction( 0 ),
431  mUrlSaveAsAction( 0 ),
432  mAddBookmarksAction( 0 ),
433  mStartIMChatAction( 0 ),
434  mSelectAllAction( 0 ),
435  mHeaderOnlyAttachmentsAction( 0 ),
436  mSelectEncodingAction( 0 ),
437  mToggleFixFontAction( 0 ),
438  mCanStartDrag( false ),
439  mHtmlWriter( 0 ),
440  mSavedRelativePosition( 0 ),
441  mDecrytMessageOverwrite( false ),
442  mShowSignatureDetails( false ),
443  mShowAttachmentQuicklist( true ),
444  mShowRawToltecMail( false )
445 {
446  mExternalWindow = (aParent == mainWindow );
447  mSplitterSizes << 180 << 100;
448  mMimeTreeMode = 1;
449  mMimeTreeAtBottom = true;
450  mAutoDelete = false;
451  mLastSerNum = 0;
452  mWaitingForSerNum = 0;
453  mMessage = 0;
454  mMsgDisplay = true;
455  mPrinting = false;
456  mShowColorbar = false;
457  mAtmUpdate = false;
458 
459  createWidgets();
460  createActions( actionCollection );
461  initHtmlWidget();
462  readConfig();
463 
464  mHtmlOverride = false;
465  mHtmlLoadExtOverride = false;
466 
467  mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin() - 1;
468 
469  connect( &mUpdateReaderWinTimer, TQT_SIGNAL(timeout()),
470  TQT_TQOBJECT(this), TQT_SLOT(updateReaderWin()) );
471  connect( &mResizeTimer, TQT_SIGNAL(timeout()),
472  TQT_TQOBJECT(this), TQT_SLOT(slotDelayedResize()) );
473  connect( &mDelayedMarkTimer, TQT_SIGNAL(timeout()),
474  TQT_TQOBJECT(this), TQT_SLOT(slotTouchMessage()) );
475 
476 }
477 
478 void KMReaderWin::createActions( KActionCollection * ac ) {
479  if ( !ac )
480  return;
481 
482  KRadioAction *raction = 0;
483 
484  // header style
485  KActionMenu *headerMenu =
486  new KActionMenu( i18n("View->", "&Headers"), ac, "view_headers" );
487  headerMenu->setToolTip( i18n("Choose display style of message headers") );
488 
489  connect( headerMenu, TQT_SIGNAL(activated()),
490  TQT_TQOBJECT(this), TQT_SLOT(slotCycleHeaderStyles()) );
491 
492  raction = new KRadioAction( i18n("View->headers->", "&Enterprise Headers"), 0,
493  TQT_TQOBJECT(this), TQT_SLOT(slotEnterpriseHeaders()),
494  ac, "view_headers_enterprise" );
495  raction->setToolTip( i18n("Show the list of headers in Enterprise style") );
496  raction->setExclusiveGroup( "view_headers_group" );
497  headerMenu->insert(raction);
498 
499  raction = new KRadioAction( i18n("View->headers->", "&Fancy Headers"), 0,
500  TQT_TQOBJECT(this), TQT_SLOT(slotFancyHeaders()),
501  ac, "view_headers_fancy" );
502  raction->setToolTip( i18n("Show the list of headers in a fancy format") );
503  raction->setExclusiveGroup( "view_headers_group" );
504  headerMenu->insert( raction );
505 
506  raction = new KRadioAction( i18n("View->headers->", "&Brief Headers"), 0,
507  TQT_TQOBJECT(this), TQT_SLOT(slotBriefHeaders()),
508  ac, "view_headers_brief" );
509  raction->setToolTip( i18n("Show brief list of message headers") );
510  raction->setExclusiveGroup( "view_headers_group" );
511  headerMenu->insert( raction );
512 
513  raction = new KRadioAction( i18n("View->headers->", "&Standard Headers"), 0,
514  TQT_TQOBJECT(this), TQT_SLOT(slotStandardHeaders()),
515  ac, "view_headers_standard" );
516  raction->setToolTip( i18n("Show standard list of message headers") );
517  raction->setExclusiveGroup( "view_headers_group" );
518  headerMenu->insert( raction );
519 
520  raction = new KRadioAction( i18n("View->headers->", "&Long Headers"), 0,
521  TQT_TQOBJECT(this), TQT_SLOT(slotLongHeaders()),
522  ac, "view_headers_long" );
523  raction->setToolTip( i18n("Show long list of message headers") );
524  raction->setExclusiveGroup( "view_headers_group" );
525  headerMenu->insert( raction );
526 
527  raction = new KRadioAction( i18n("View->headers->", "&All Headers"), 0,
528  TQT_TQOBJECT(this), TQT_SLOT(slotAllHeaders()),
529  ac, "view_headers_all" );
530  raction->setToolTip( i18n("Show all message headers") );
531  raction->setExclusiveGroup( "view_headers_group" );
532  headerMenu->insert( raction );
533 
534  // attachment style
535  KActionMenu *attachmentMenu =
536  new KActionMenu( i18n("View->", "&Attachments"), ac, "view_attachments" );
537  attachmentMenu->setToolTip( i18n("Choose display style of attachments") );
538  connect( attachmentMenu, TQT_SIGNAL(activated()),
539  TQT_TQOBJECT(this), TQT_SLOT(slotCycleAttachmentStrategy()) );
540 
541  raction = new KRadioAction( i18n("View->attachments->", "&As Icons"), 0,
542  TQT_TQOBJECT(this), TQT_SLOT(slotIconicAttachments()),
543  ac, "view_attachments_as_icons" );
544  raction->setToolTip( i18n("Show all attachments as icons. Click to see them.") );
545  raction->setExclusiveGroup( "view_attachments_group" );
546  attachmentMenu->insert( raction );
547 
548  raction = new KRadioAction( i18n("View->attachments->", "&Smart"), 0,
549  TQT_TQOBJECT(this), TQT_SLOT(slotSmartAttachments()),
550  ac, "view_attachments_smart" );
551  raction->setToolTip( i18n("Show attachments as suggested by sender.") );
552  raction->setExclusiveGroup( "view_attachments_group" );
553  attachmentMenu->insert( raction );
554 
555  raction = new KRadioAction( i18n("View->attachments->", "&Inline"), 0,
556  TQT_TQOBJECT(this), TQT_SLOT(slotInlineAttachments()),
557  ac, "view_attachments_inline" );
558  raction->setToolTip( i18n("Show all attachments inline (if possible)") );
559  raction->setExclusiveGroup( "view_attachments_group" );
560  attachmentMenu->insert( raction );
561 
562  raction = new KRadioAction( i18n("View->attachments->", "&Hide"), 0,
563  TQT_TQOBJECT(this), TQT_SLOT(slotHideAttachments()),
564  ac, "view_attachments_hide" );
565  raction->setToolTip( i18n("Do not show attachments in the message viewer") );
566  raction->setExclusiveGroup( "view_attachments_group" );
567  attachmentMenu->insert( raction );
568 
569  mHeaderOnlyAttachmentsAction = new KRadioAction( i18n( "View->attachments->", "In Header &Only" ), 0,
570  TQT_TQOBJECT(this), TQT_SLOT( slotHeaderOnlyAttachments() ),
571  ac, "view_attachments_headeronly" );
572  mHeaderOnlyAttachmentsAction->setToolTip( i18n( "Show Attachments only in the header of the mail" ) );
573  mHeaderOnlyAttachmentsAction->setExclusiveGroup( "view_attachments_group" );
574  attachmentMenu->insert( mHeaderOnlyAttachmentsAction );
575 
576  // Set Encoding submenu
577  mSelectEncodingAction = new KSelectAction( i18n( "&Set Encoding" ), "charset", 0,
578  TQT_TQOBJECT(this), TQT_SLOT( slotSetEncoding() ),
579  ac, "encoding" );
580  TQStringList encodings = KMMsgBase::supportedEncodings( false );
581  encodings.prepend( i18n( "Auto" ) );
582  mSelectEncodingAction->setItems( encodings );
583  mSelectEncodingAction->setCurrentItem( 0 );
584 
585  mMailToComposeAction = new KAction( i18n("New Message To..."), "mail_new",
586  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoCompose()), ac,
587  "mailto_compose" );
588  mMailToReplyAction = new KAction( i18n("Reply To..."), "mail_reply",
589  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoReply()), ac,
590  "mailto_reply" );
591  mMailToForwardAction = new KAction( i18n("Forward To..."), "mail_forward",
592  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoForward()), ac,
593  "mailto_forward" );
594  mAddAddrBookAction = new KAction( i18n("Add to Address Book"),
595  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoAddAddrBook()),
596  ac, "add_addr_book" );
597  mOpenAddrBookAction = new KAction( i18n("Open in Address Book"),
598  0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoOpenAddrBook()),
599  ac, "openin_addr_book" );
600  mCopyAction = KStdAction::copy( TQT_TQOBJECT(this), TQT_SLOT(slotCopySelectedText()), ac, "kmail_copy");
601  mSelectAllAction = new KAction( i18n("Select All Text"), CTRL+SHIFT+Key_A, TQT_TQOBJECT(this),
602  TQT_SLOT(selectAll()), ac, "mark_all_text" );
603  mCopyURLAction = new KAction( i18n("Copy Link Address"), 0, TQT_TQOBJECT(this),
604  TQT_SLOT(slotUrlCopy()), ac, "copy_url" );
605  mUrlOpenAction = new KAction( i18n("Open URL"), 0, TQT_TQOBJECT(this),
606  TQT_SLOT(slotUrlOpen()), ac, "open_url" );
607  mAddBookmarksAction = new KAction( i18n("Bookmark This Link"),
608  "bookmark_add",
609  0, TQT_TQOBJECT(this), TQT_SLOT(slotAddBookmarks()),
610  ac, "add_bookmarks" );
611  mUrlSaveAsAction = new KAction( i18n("Save Link As..."), 0, TQT_TQOBJECT(this),
612  TQT_SLOT(slotUrlSave()), ac, "saveas_url" );
613 
614  mToggleFixFontAction = new KToggleAction( i18n("Use Fi&xed Font"),
615  Key_X, TQT_TQOBJECT(this), TQT_SLOT(slotToggleFixedFont()),
616  ac, "toggle_fixedfont" );
617 
618  mStartIMChatAction = new KAction( i18n("Chat &With..."), 0, TQT_TQOBJECT(this),
619  TQT_SLOT(slotIMChat()), ac, "start_im_chat" );
620 }
621 
622 // little helper function
623 KRadioAction *KMReaderWin::actionForHeaderStyle( const HeaderStyle * style, const HeaderStrategy * strategy ) {
624  if ( !mActionCollection )
625  return 0;
626  const char * actionName = 0;
627  if ( style == HeaderStyle::enterprise() )
628  actionName = "view_headers_enterprise";
629  if ( style == HeaderStyle::fancy() )
630  actionName = "view_headers_fancy";
631  else if ( style == HeaderStyle::brief() )
632  actionName = "view_headers_brief";
633  else if ( style == HeaderStyle::plain() ) {
634  if ( strategy == HeaderStrategy::standard() )
635  actionName = "view_headers_standard";
636  else if ( strategy == HeaderStrategy::rich() )
637  actionName = "view_headers_long";
638  else if ( strategy == HeaderStrategy::all() )
639  actionName = "view_headers_all";
640  }
641  if ( actionName )
642  return static_cast<KRadioAction*>(mActionCollection->action(actionName));
643  else
644  return 0;
645 }
646 
647 KRadioAction *KMReaderWin::actionForAttachmentStrategy( const AttachmentStrategy * as ) {
648  if ( !mActionCollection )
649  return 0;
650  const char * actionName = 0;
651  if ( as == AttachmentStrategy::iconic() )
652  actionName = "view_attachments_as_icons";
653  else if ( as == AttachmentStrategy::smart() )
654  actionName = "view_attachments_smart";
655  else if ( as == AttachmentStrategy::inlined() )
656  actionName = "view_attachments_inline";
657  else if ( as == AttachmentStrategy::hidden() )
658  actionName = "view_attachments_hide";
659  else if ( as == AttachmentStrategy::headerOnly() )
660  actionName = "view_attachments_headeronly";
661 
662  if ( actionName )
663  return static_cast<KRadioAction*>(mActionCollection->action(actionName));
664  else
665  return 0;
666 }
667 
668 void KMReaderWin::slotEnterpriseHeaders() {
669  setHeaderStyleAndStrategy( HeaderStyle::enterprise(),
670  HeaderStrategy::rich() );
671  if( !mExternalWindow )
672  writeConfig();
673 }
674 
675 void KMReaderWin::slotFancyHeaders() {
676  setHeaderStyleAndStrategy( HeaderStyle::fancy(),
677  HeaderStrategy::rich() );
678  if( !mExternalWindow )
679  writeConfig();
680 }
681 
682 void KMReaderWin::slotBriefHeaders() {
683  setHeaderStyleAndStrategy( HeaderStyle::brief(),
684  HeaderStrategy::brief() );
685  if( !mExternalWindow )
686  writeConfig();
687 }
688 
689 void KMReaderWin::slotStandardHeaders() {
690  setHeaderStyleAndStrategy( HeaderStyle::plain(),
691  HeaderStrategy::standard());
692  writeConfig();
693 }
694 
695 void KMReaderWin::slotLongHeaders() {
696  setHeaderStyleAndStrategy( HeaderStyle::plain(),
697  HeaderStrategy::rich() );
698  if( !mExternalWindow )
699  writeConfig();
700 }
701 
702 void KMReaderWin::slotAllHeaders() {
703  setHeaderStyleAndStrategy( HeaderStyle::plain(),
704  HeaderStrategy::all() );
705  if( !mExternalWindow )
706  writeConfig();
707 }
708 
709 void KMReaderWin::slotLevelQuote( int l )
710 {
711  mLevelQuote = l;
713  update(true);
714 }
715 
716 void KMReaderWin::slotCycleHeaderStyles() {
717  const HeaderStrategy * strategy = headerStrategy();
718  const HeaderStyle * style = headerStyle();
719 
720  const char * actionName = 0;
721  if ( style == HeaderStyle::enterprise() ) {
722  slotFancyHeaders();
723  actionName = "view_headers_fancy";
724  }
725  if ( style == HeaderStyle::fancy() ) {
726  slotBriefHeaders();
727  actionName = "view_headers_brief";
728  } else if ( style == HeaderStyle::brief() ) {
729  slotStandardHeaders();
730  actionName = "view_headers_standard";
731  } else if ( style == HeaderStyle::plain() ) {
732  if ( strategy == HeaderStrategy::standard() ) {
733  slotLongHeaders();
734  actionName = "view_headers_long";
735  } else if ( strategy == HeaderStrategy::rich() ) {
736  slotAllHeaders();
737  actionName = "view_headers_all";
738  } else if ( strategy == HeaderStrategy::all() ) {
739  slotEnterpriseHeaders();
740  actionName = "view_headers_enterprise";
741  }
742  }
743 
744  if ( actionName )
745  static_cast<KRadioAction*>( mActionCollection->action( actionName ) )->setChecked( true );
746 }
747 
748 
749 void KMReaderWin::slotIconicAttachments() {
750  setAttachmentStrategy( AttachmentStrategy::iconic() );
751 }
752 
753 void KMReaderWin::slotSmartAttachments() {
754  setAttachmentStrategy( AttachmentStrategy::smart() );
755 }
756 
757 void KMReaderWin::slotInlineAttachments() {
758  setAttachmentStrategy( AttachmentStrategy::inlined() );
759 }
760 
761 void KMReaderWin::slotHideAttachments() {
762  setAttachmentStrategy( AttachmentStrategy::hidden() );
763 }
764 
765 void KMReaderWin::slotHeaderOnlyAttachments() {
766  setAttachmentStrategy( AttachmentStrategy::headerOnly() );
767 }
768 
769 void KMReaderWin::slotCycleAttachmentStrategy() {
770  setAttachmentStrategy( attachmentStrategy()->next() );
771  KRadioAction * action = actionForAttachmentStrategy( attachmentStrategy() );
772  assert( action );
773  action->setChecked( true );
774 }
775 
776 
777 //-----------------------------------------------------------------------------
778 KMReaderWin::~KMReaderWin()
779 {
780  clearBodyPartMementos();
781  delete mHtmlWriter; mHtmlWriter = 0;
782  delete mCSSHelper;
783  if (mAutoDelete) delete message();
784  delete mRootNode; mRootNode = 0;
785  removeTempFiles();
786 }
787 
788 
789 //-----------------------------------------------------------------------------
790 void KMReaderWin::slotMessageArrived( KMMessage *msg )
791 {
792  if (msg && ((KMMsgBase*)msg)->isMessage()) {
793  if ( msg->getMsgSerNum() == mWaitingForSerNum ) {
794  setMsg( msg, true );
795  } else {
796  //kdDebug( 5006 ) << "KMReaderWin::slotMessageArrived - ignoring update" << endl;
797  }
798  }
799 }
800 
801 //-----------------------------------------------------------------------------
803 {
804  if ( !mAtmUpdate ) {
805  // reparse the msg
806  //kdDebug(5006) << "KMReaderWin::update - message" << endl;
807  updateReaderWin();
808  return;
809  }
810 
811  if ( !mRootNode )
812  return;
813 
814  KMMessage* msg = static_cast<KMMessage*>( observable );
815  assert( msg != 0 );
816 
817  // find our partNode and update it
818  if ( !msg->lastUpdatedPart() ) {
819  kdDebug(5006) << "KMReaderWin::update - no updated part" << endl;
820  return;
821  }
822  partNode* node = mRootNode->findNodeForDwPart( msg->lastUpdatedPart() );
823  if ( !node ) {
824  kdDebug(5006) << "KMReaderWin::update - can't find node for part" << endl;
825  return;
826  }
827  node->setDwPart( msg->lastUpdatedPart() );
828 
829  // update the tmp file
830  // we have to set it writeable temporarily
831  ::chmod( TQFile::encodeName( mAtmCurrentName ), S_IRWXU );
832  TQByteArray data = node->msgPart().bodyDecodedBinary();
833  size_t size = data.size();
834  if ( node->msgPart().type() == DwMime::kTypeText && size) {
835  size = KMail::Util::crlf2lf( data.data(), size );
836  }
837  KPIM::kBytesToFile( data.data(), size, mAtmCurrentName, false, false, false );
838  ::chmod( TQFile::encodeName( mAtmCurrentName ), S_IRUSR );
839 
840  mAtmUpdate = false;
841 }
842 
843 //-----------------------------------------------------------------------------
845 {
846  for (TQStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end();
847  it++)
848  {
849  TQFile::remove(*it);
850  }
851  mTempFiles.clear();
852  for (TQStringList::Iterator it = mTempDirs.begin(); it != mTempDirs.end();
853  it++)
854  {
855  TQDir(*it).rmdir(*it);
856  }
857  mTempDirs.clear();
858 }
859 
860 
861 //-----------------------------------------------------------------------------
862 bool KMReaderWin::event(TQEvent *e)
863 {
864  if (e->type() == TQEvent::ApplicationPaletteChange)
865  {
866  delete mCSSHelper;
867  mCSSHelper = new KMail::CSSHelper( TQPaintDeviceMetrics( mViewer->view() ) );
868  if (message())
869  message()->readConfig();
870  update( true ); // Force update
871  return true;
872  }
873  return TQWidget::event(e);
874 }
875 
876 
877 //-----------------------------------------------------------------------------
879 {
880  const KConfigGroup mdnGroup( KMKernel::config(), "MDN" );
881  /*should be: const*/ KConfigGroup reader( KMKernel::config(), "Reader" );
882 
883  delete mCSSHelper;
884  mCSSHelper = new KMail::CSSHelper( TQPaintDeviceMetrics( mViewer->view() ) );
885 
886  mNoMDNsWhenEncrypted = mdnGroup.readBoolEntry( "not-send-when-encrypted", true );
887 
888  mUseFixedFont = reader.readBoolEntry( "useFixedFont", false );
889  if ( mToggleFixFontAction )
890  mToggleFixFontAction->setChecked( mUseFixedFont );
891 
892  mHtmlMail = reader.readBoolEntry( "htmlMail", false );
893  mHtmlLoadExternal = reader.readBoolEntry( "htmlLoadExternal", false );
894 
895  setHeaderStyleAndStrategy( HeaderStyle::create( reader.readEntry( "header-style", "fancy" ) ),
896  HeaderStrategy::create( reader.readEntry( "header-set-displayed", "rich" ) ) );
897  KRadioAction *raction = actionForHeaderStyle( headerStyle(), headerStrategy() );
898  if ( raction )
899  raction->setChecked( true );
900 
901  setAttachmentStrategy( AttachmentStrategy::create( reader.readEntry( "attachment-strategy", "smart" ) ) );
902  raction = actionForAttachmentStrategy( attachmentStrategy() );
903  if ( raction )
904  raction->setChecked( true );
905 
906  // if the user uses OpenPGP then the color bar defaults to enabled
907  // else it defaults to disabled
908  mShowColorbar = reader.readBoolEntry( "showColorbar", Kpgp::Module::getKpgp()->usePGP() );
909  // if the value defaults to enabled and KMail (with color bar) is used for
910  // the first time the config dialog doesn't know this if we don't save the
911  // value now
912  reader.writeEntry( "showColorbar", mShowColorbar );
913 
914  mMimeTreeAtBottom = reader.readEntry( "MimeTreeLocation", "bottom" ) != "top";
915  const TQString s = reader.readEntry( "MimeTreeMode", "smart" );
916  if ( s == "never" )
917  mMimeTreeMode = 0;
918  else if ( s == "always" )
919  mMimeTreeMode = 2;
920  else
921  mMimeTreeMode = 1;
922 
923  const int mimeH = reader.readNumEntry( "MimePaneHeight", 100 );
924  const int messageH = reader.readNumEntry( "MessagePaneHeight", 180 );
925  mSplitterSizes.clear();
926  if ( mMimeTreeAtBottom )
927  mSplitterSizes << messageH << mimeH;
928  else
929  mSplitterSizes << mimeH << messageH;
930 
931  adjustLayout();
932 
933  readGlobalOverrideCodec();
934 
935  if (message())
936  update();
938 }
939 
940 
941 void KMReaderWin::adjustLayout() {
942  if ( mMimeTreeAtBottom )
943  mSplitter->moveToLast( mMimePartTree );
944  else
945  mSplitter->moveToFirst( mMimePartTree );
946  mSplitter->setSizes( mSplitterSizes );
947 
948  if ( mMimeTreeMode == 2 && mMsgDisplay )
949  mMimePartTree->show();
950  else
951  mMimePartTree->hide();
952 
953  if ( mShowColorbar && mMsgDisplay )
954  mColorBar->show();
955  else
956  mColorBar->hide();
957 }
958 
959 
960 void KMReaderWin::saveSplitterSizes( KConfigBase & c ) const {
961  if ( !mSplitter || !mMimePartTree )
962  return;
963  if ( mMimePartTree->isHidden() )
964  return; // don't rely on TQSplitter maintaining sizes for hidden widgets.
965 
966  c.writeEntry( "MimePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 1 : 0 ] );
967  c.writeEntry( "MessagePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 0 : 1 ] );
968 }
969 
970 //-----------------------------------------------------------------------------
971 void KMReaderWin::writeConfig( bool sync ) const {
972  KConfigGroup reader( KMKernel::config(), "Reader" );
973 
974  reader.writeEntry( "useFixedFont", mUseFixedFont );
975  if ( headerStyle() )
976  reader.writeEntry( "header-style", headerStyle()->name() );
977  if ( headerStrategy() )
978  reader.writeEntry( "header-set-displayed", headerStrategy()->name() );
979  if ( attachmentStrategy() )
980  reader.writeEntry( "attachment-strategy", attachmentStrategy()->name() );
981 
982  saveSplitterSizes( reader );
983 
984  if ( sync )
985  kmkernel->slotRequestConfigSync();
986 }
987 
988 //-----------------------------------------------------------------------------
990 {
991  mViewer->widget()->setFocusPolicy(TQ_WheelFocus);
992  // Let's better be paranoid and disable plugins (it defaults to enabled):
993  mViewer->setPluginsEnabled(false);
994  mViewer->setJScriptEnabled(false); // just make this explicit
995  mViewer->setJavaEnabled(false); // just make this explicit
996  mViewer->setMetaRefreshEnabled(false);
997  mViewer->setURLCursor(KCursor::handCursor());
998  // Espen 2000-05-14: Getting rid of thick ugly frames
999  mViewer->view()->setLineWidth(0);
1000  // register our own event filter for shift-click
1001  mViewer->view()->viewport()->installEventFilter( this );
1002 
1003  if ( !htmlWriter() )
1004 #ifdef KMAIL_READER_HTML_DEBUG
1005  mHtmlWriter = new TeeHtmlWriter( new FileHtmlWriter( TQString() ),
1006  new KHtmlPartHtmlWriter( mViewer, 0 ) );
1007 #else
1008  mHtmlWriter = new KHtmlPartHtmlWriter( mViewer, 0 );
1009 #endif
1010 
1011  connect(mViewer->browserExtension(),
1012  TQT_SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),this,
1013  TQT_SLOT(slotUrlOpen(const KURL &)));
1014  connect(mViewer->browserExtension(),
1015  TQT_SIGNAL(createNewWindow(const KURL &, const KParts::URLArgs &)),this,
1016  TQT_SLOT(slotUrlOpen(const KURL &)));
1017  connect(mViewer,TQT_SIGNAL(popupMenu(const TQString &, const TQPoint &)),
1018  TQT_SLOT(slotUrlPopup(const TQString &, const TQPoint &)));
1019  connect( kmkernel->imProxy(), TQT_SIGNAL( sigContactPresenceChanged( const TQString & ) ),
1020  TQT_TQOBJECT(this), TQT_SLOT( contactStatusChanged( const TQString & ) ) );
1021  connect( kmkernel->imProxy(), TQT_SIGNAL( sigPresenceInfoExpired() ),
1022  TQT_TQOBJECT(this), TQT_SLOT( updateReaderWin() ) );
1023 }
1024 
1025 void KMReaderWin::contactStatusChanged( const TQString &uid)
1026 {
1027 // kdDebug( 5006 ) << k_funcinfo << " got a presence change for " << uid << endl;
1028  // get the list of nodes for this contact from the htmlView
1029  DOM::NodeList presenceNodes = mViewer->htmlDocument()
1030  .getElementsByName( DOM::DOMString( TQString::fromLatin1("presence-") + uid ) );
1031  for ( unsigned int i = 0; i < presenceNodes.length(); ++i ) {
1032  DOM::Node n = presenceNodes.item( i );
1033  kdDebug( 5006 ) << "name is " << n.nodeName().string() << endl;
1034  kdDebug( 5006 ) << "value of content was " << n.firstChild().nodeValue().string() << endl;
1035  TQString newPresence = kmkernel->imProxy()->presenceString( uid );
1036  if ( newPresence.isNull() ) // KHTML crashes if you setNodeValue( TQString() )
1037  newPresence = TQString::fromLatin1( "ENOIMRUNNING" );
1038  n.firstChild().setNodeValue( newPresence );
1039 // kdDebug( 5006 ) << "value of content is now " << n.firstChild().nodeValue().string() << endl;
1040  }
1041 // kdDebug( 5006 ) << "and we updated the above presence nodes" << uid << endl;
1042 }
1043 
1044 void KMReaderWin::setAttachmentStrategy( const AttachmentStrategy * strategy ) {
1045  mAttachmentStrategy = strategy ? strategy : AttachmentStrategy::smart();
1046  update( true );
1047 }
1048 
1050  const HeaderStrategy * strategy ) {
1051  mHeaderStyle = style ? style : HeaderStyle::fancy();
1052  mHeaderStrategy = strategy ? strategy : HeaderStrategy::rich();
1053  if ( mHeaderOnlyAttachmentsAction ) {
1054  const bool styleHasAttachmentQuickList = mHeaderStyle == HeaderStyle::fancy() ||
1055  mHeaderStyle == HeaderStyle::enterprise();
1056  mHeaderOnlyAttachmentsAction->setEnabled( styleHasAttachmentQuickList );
1057  if ( !styleHasAttachmentQuickList && mAttachmentStrategy == AttachmentStrategy::headerOnly() ) {
1058  // Style changed to something without an attachment quick list, need to change attachment
1059  // strategy
1060  setAttachmentStrategy( AttachmentStrategy::smart() );
1061  }
1062  }
1063  update( true );
1064 }
1065 
1066 //-----------------------------------------------------------------------------
1067 void KMReaderWin::setOverrideEncoding( const TQString & encoding )
1068 {
1069  if ( encoding == mOverrideEncoding )
1070  return;
1071 
1072  mOverrideEncoding = encoding;
1073  if ( mSelectEncodingAction ) {
1074  if ( encoding.isEmpty() ) {
1075  mSelectEncodingAction->setCurrentItem( 0 );
1076  }
1077  else {
1078  TQStringList encodings = mSelectEncodingAction->items();
1079  uint i = 0;
1080  for ( TQStringList::const_iterator it = encodings.begin(), end = encodings.end(); it != end; ++it, ++i ) {
1081  if ( KGlobal::charsets()->encodingForName( *it ) == encoding ) {
1082  mSelectEncodingAction->setCurrentItem( i );
1083  break;
1084  }
1085  }
1086  if ( i == encodings.size() ) {
1087  // the value of encoding is unknown => use Auto
1088  kdWarning(5006) << "Unknown override character encoding \"" << encoding
1089  << "\". Using Auto instead." << endl;
1090  mSelectEncodingAction->setCurrentItem( 0 );
1091  mOverrideEncoding = TQString();
1092  }
1093  }
1094  }
1095  update( true );
1096 }
1097 
1098 
1099 void KMReaderWin::setPrintFont( const TQFont& font )
1100 {
1101 
1102  mCSSHelper->setPrintFont( font );
1103 }
1104 
1105 //-----------------------------------------------------------------------------
1106 const TQTextCodec * KMReaderWin::overrideCodec() const
1107 {
1108  if ( mOverrideEncoding.isEmpty() || mOverrideEncoding == "Auto" ) // Auto
1109  return 0;
1110  else
1111  return KMMsgBase::codecForName( mOverrideEncoding.latin1() );
1112 }
1113 
1114 //-----------------------------------------------------------------------------
1115 void KMReaderWin::slotSetEncoding()
1116 {
1117  if ( mSelectEncodingAction->currentItem() == 0 ) // Auto
1118  mOverrideEncoding = TQString();
1119  else
1120  mOverrideEncoding = KGlobal::charsets()->encodingForName( mSelectEncodingAction->currentText() );
1121  update( true );
1122 }
1123 
1124 //-----------------------------------------------------------------------------
1125 void KMReaderWin::readGlobalOverrideCodec()
1126 {
1127  // if the global character encoding wasn't changed then there's nothing to do
1128  if ( GlobalSettings::self()->overrideCharacterEncoding() == mOldGlobalOverrideEncoding )
1129  return;
1130 
1131  setOverrideEncoding( GlobalSettings::self()->overrideCharacterEncoding() );
1132  mOldGlobalOverrideEncoding = GlobalSettings::self()->overrideCharacterEncoding();
1133 }
1134 
1135 //-----------------------------------------------------------------------------
1136 void KMReaderWin::setOriginalMsg( unsigned long serNumOfOriginalMessage, int nodeIdOffset )
1137 {
1138  mSerNumOfOriginalMessage = serNumOfOriginalMessage;
1139  mNodeIdOffset = nodeIdOffset;
1140 }
1141 
1142 //-----------------------------------------------------------------------------
1143 void KMReaderWin::setMsg( KMMessage* aMsg, bool force, bool updateOnly )
1144 {
1145  if ( aMsg ) {
1146  kdDebug(5006) << "(" << aMsg->getMsgSerNum() << ", last " << mLastSerNum << ") " << aMsg->subject() << " "
1147  << aMsg->fromStrip() << ", readyToShow " << (aMsg->readyToShow()) << endl;
1148  }
1149 
1150  // Reset message-transient state
1151  if ( aMsg && aMsg->getMsgSerNum() != mLastSerNum && !updateOnly ){
1152  mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin()-1;
1153  mShowRawToltecMail = !GlobalSettings::self()->showToltecReplacementText();
1154  clearBodyPartMementos();
1155  }
1156  if ( mPrinting )
1157  mLevelQuote = -1;
1158 
1159  bool complete = true;
1160  if ( aMsg &&
1161  !aMsg->readyToShow() &&
1162  (aMsg->getMsgSerNum() != mLastSerNum) &&
1163  !aMsg->isComplete() )
1164  complete = false;
1165 
1166  // If not forced and there is aMsg and aMsg is same as mMsg then return
1167  if (!force && aMsg && mLastSerNum != 0 && aMsg->getMsgSerNum() == mLastSerNum)
1168  return;
1169 
1170  // (de)register as observer
1171  if (aMsg && message())
1172  message()->detach( this );
1173  if (aMsg)
1174  aMsg->attach( this );
1175  mAtmUpdate = false;
1176 
1177  // connect to the updates if we have hancy headers
1178 
1179  mDelayedMarkTimer.stop();
1180 
1181  mMessage = 0;
1182  if ( !aMsg ) {
1183  mWaitingForSerNum = 0; // otherwise it has been set
1184  mLastSerNum = 0;
1185  } else {
1186  mLastSerNum = aMsg->getMsgSerNum();
1187  // Check if the serial number can be used to find the assoc KMMessage
1188  // If so, keep only the serial number (and not mMessage), to avoid a dangling mMessage
1189  // when going to another message in the mainwindow.
1190  // Otherwise, keep only mMessage, this is fine for standalone KMReaderMainWins since
1191  // we're working on a copy of the KMMessage, which we own.
1192  if (message() != aMsg) {
1193  mMessage = aMsg;
1194  mLastSerNum = 0;
1195  }
1196  }
1197 
1198  if (aMsg) {
1199  aMsg->setOverrideCodec( overrideCodec() );
1200  aMsg->setDecodeHTML( htmlMail() );
1201  // FIXME: workaround to disable DND for IMAP load-on-demand
1202  if ( !aMsg->isComplete() )
1203  mViewer->setDNDEnabled( false );
1204  else
1205  mViewer->setDNDEnabled( true );
1206  }
1207 
1208  // only display the msg if it is complete
1209  // otherwise we'll get flickering with progressively loaded messages
1210  if ( complete )
1211  {
1212  // Avoid flicker, somewhat of a cludge
1213  if (force) {
1214  // stop the timer to avoid calling updateReaderWin twice
1215  mUpdateReaderWinTimer.stop();
1216  updateReaderWin();
1217  }
1218  else if (mUpdateReaderWinTimer.isActive())
1219  mUpdateReaderWinTimer.changeInterval( delay );
1220  else
1221  mUpdateReaderWinTimer.start( 0, true );
1222  }
1223 
1224  if ( aMsg && (aMsg->isUnread() || aMsg->isNew()) && GlobalSettings::self()->delayedMarkAsRead() ) {
1225  if ( GlobalSettings::self()->delayedMarkTime() != 0 )
1226  mDelayedMarkTimer.start( GlobalSettings::self()->delayedMarkTime() * 1000, true );
1227  else
1228  slotTouchMessage();
1229  }
1230 }
1231 
1232 //-----------------------------------------------------------------------------
1234 {
1235  mUpdateReaderWinTimer.stop();
1236  clear();
1237  mDelayedMarkTimer.stop();
1238  mLastSerNum = 0;
1239  mWaitingForSerNum = 0;
1240  mMessage = 0;
1241 }
1242 
1243 // enter items for the "Important changes" list here:
1244 static const char * const kmailChanges[] = {
1245  ""
1246 };
1247 static const int numKMailChanges =
1248  sizeof kmailChanges / sizeof *kmailChanges;
1249 
1250 // enter items for the "new features" list here, so the main body of
1251 // the welcome page can be left untouched (probably much easier for
1252 // the translators). Note that the <li>...</li> tags are added
1253 // automatically below:
1254 static const char * const kmailNewFeatures[] = {
1255  I18N_NOOP("Full namespace support for IMAP"),
1256  I18N_NOOP("Offline mode"),
1257  I18N_NOOP("Sieve script management and editing"),
1258  I18N_NOOP("Account specific filtering"),
1259  I18N_NOOP("Filtering of incoming mail for online IMAP accounts"),
1260  I18N_NOOP("Online IMAP folders can be used when filtering into folders"),
1261  I18N_NOOP("Automatically delete older mails on POP servers")
1262 };
1263 static const int numKMailNewFeatures =
1264  sizeof kmailNewFeatures / sizeof *kmailNewFeatures;
1265 
1266 
1267 //-----------------------------------------------------------------------------
1268 //static
1270 {
1271  TQCString str;
1272  for ( int i = 0 ; i < numKMailChanges ; ++i )
1273  str += kmailChanges[i];
1274  for ( int i = 0 ; i < numKMailNewFeatures ; ++i )
1275  str += kmailNewFeatures[i];
1276  KMD5 md5( str );
1277  return md5.base64Digest();
1278 }
1279 
1280 //-----------------------------------------------------------------------------
1281 void KMReaderWin::displaySplashPage( const TQString &info )
1282 {
1283  mMsgDisplay = false;
1284  adjustLayout();
1285 
1286  TQString location = locate("data", "kmail/about/main.html");
1287  TQString content = KPIM::kFileToString(location);
1288  content = content.arg( locate( "data", "libkdepim/about/kde_infopage.css" ) );
1289  if ( kapp->reverseLayout() )
1290  content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libkdepim/about/kde_infopage_rtl.css" ) );
1291  else
1292  content = content.arg( "" );
1293 
1294  mViewer->begin(KURL( location ));
1295 
1296  TQString fontSize = TQString::number( pointsToPixel( mCSSHelper->bodyFont().pointSize() ) );
1297  TQString appTitle = i18n("KMail");
1298  TQString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
1299  TQString quickDescription = i18n("The email client for the K Desktop Environment.");
1300  mViewer->write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info));
1301  mViewer->end();
1302 }
1303 
1305 {
1306  TQString info =
1307  i18n( "<h2 style='margin-top: 0px;'>Retrieving Folder Contents</h2><p>Please wait . . .</p>&nbsp;" );
1308 
1309  displaySplashPage( info );
1310 }
1311 
1313 {
1314  TQString info =
1315  i18n( "<h2 style='margin-top: 0px;'>Offline</h2><p>KMail is currently in offline mode. "
1316  "Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p>&nbsp;" );
1317 
1318  displaySplashPage( info );
1319 }
1320 
1321 
1322 //-----------------------------------------------------------------------------
1324 {
1325  TQString info =
1326  i18n("%1: KMail version; %2: help:// URL; %3: homepage URL; "
1327  "%4: prior KMail version; %5: prior KDE version; "
1328  "%6: generated list of new features; "
1329  "%7: First-time user text (only shown on first start); "
1330  "%8: generated list of important changes; "
1331  "--- end of comment ---",
1332  "<h2 style='margin-top: 0px;'>Welcome to KMail %1</h2><p>KMail is the email client for the K "
1333  "Desktop Environment. It is designed to be fully compatible with "
1334  "Internet mailing standards including MIME, SMTP, POP3 and IMAP."
1335  "</p>\n"
1336  "<ul><li>KMail has many powerful features which are described in the "
1337  "<a href=\"%2\">documentation</a></li>\n"
1338  "<li>The <a href=\"%3\">KMail homepage</A> offers information about "
1339  "new versions of KMail</li></ul>\n"
1340  "%8\n" // important changes
1341  "<p>Some of the new features in this release of KMail include "
1342  "(compared to KMail %4, which is part of KDE %5):</p>\n"
1343  "<ul>\n%6</ul>\n"
1344  "%7\n"
1345  "<p>We hope that you will enjoy KMail.</p>\n"
1346  "<p>Thank you,</p>\n"
1347  "<p style='margin-bottom: 0px'>&nbsp; &nbsp; The KMail Team</p>")
1348  .arg(KMAIL_VERSION) // KMail version
1349  .arg("help:/kmail/index.html") // KMail help:// URL
1350  .arg("http://kontact.kde.org/kmail/") // KMail homepage URL
1351  .arg("1.8").arg("3.4"); // prior KMail and KDE version
1352 
1353  TQString featureItems;
1354  for ( int i = 0 ; i < numKMailNewFeatures ; i++ )
1355  featureItems += i18n("<li>%1</li>\n").arg( i18n( kmailNewFeatures[i] ) );
1356 
1357  info = info.arg( featureItems );
1358 
1359  if( kmkernel->firstStart() ) {
1360  info = info.arg( i18n("<p>Please take a moment to fill in the KMail "
1361  "configuration panel at Settings-&gt;Configure "
1362  "KMail.\n"
1363  "You need to create at least a default identity and "
1364  "an incoming as well as outgoing mail account."
1365  "</p>\n") );
1366  } else {
1367  info = info.arg( TQString() );
1368  }
1369 
1370  if ( ( numKMailChanges > 1 ) || ( numKMailChanges == 1 && strlen(kmailChanges[0]) > 0 ) ) {
1371  TQString changesText =
1372  i18n("<p><span style='font-size:125%; font-weight:bold;'>"
1373  "Important changes</span> (compared to KMail %1):</p>\n")
1374  .arg("1.8");
1375  changesText += "<ul>\n";
1376  for ( int i = 0 ; i < numKMailChanges ; i++ )
1377  changesText += i18n("<li>%1</li>\n").arg( i18n( kmailChanges[i] ) );
1378  changesText += "</ul>\n";
1379  info = info.arg( changesText );
1380  }
1381  else
1382  info = info.arg(""); // remove the %8
1383 
1384  displaySplashPage( info );
1385 }
1386 
1388  mMsgDisplay = true;
1389  adjustLayout();
1390 }
1391 
1392 
1393 //-----------------------------------------------------------------------------
1394 
1396 {
1397  if (!mMsgDisplay) return;
1398 
1399  mViewer->setOnlyLocalReferences(!htmlLoadExternal());
1400 
1401  htmlWriter()->reset();
1402 
1403  KMFolder* folder = 0;
1404  if (message(&folder))
1405  {
1406  if ( mShowColorbar )
1407  mColorBar->show();
1408  else
1409  mColorBar->hide();
1410  displayMessage();
1411  }
1412  else
1413  {
1414  mColorBar->hide();
1415  mMimePartTree->hide();
1416  mMimePartTree->clear();
1417  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
1418  htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) + "</body></html>" );
1419  htmlWriter()->end();
1420  }
1421 
1422  if (mSavedRelativePosition)
1423  {
1424  TQScrollView * scrollview = static_cast<TQScrollView *>(mViewer->widget());
1425  scrollview->setContentsPos( 0,
1426  tqRound( scrollview->contentsHeight() * mSavedRelativePosition ) );
1427  mSavedRelativePosition = 0;
1428  }
1429 }
1430 
1431 //-----------------------------------------------------------------------------
1432 int KMReaderWin::pointsToPixel(int pointSize) const
1433 {
1434  const TQPaintDeviceMetrics pdm(mViewer->view());
1435 
1436  return (pointSize * pdm.logicalDpiY() + 36) / 72;
1437 }
1438 
1439 //-----------------------------------------------------------------------------
1440 void KMReaderWin::showHideMimeTree( bool isPlainTextTopLevel ) {
1441  if ( mMimeTreeMode == 2 ||
1442  ( mMimeTreeMode == 1 && !isPlainTextTopLevel ) )
1443  mMimePartTree->show();
1444  else {
1445  // don't rely on TQSplitter maintaining sizes for hidden widgets:
1446  KConfigGroup reader( KMKernel::config(), "Reader" );
1447  saveSplitterSizes( reader );
1448  mMimePartTree->hide();
1449  }
1450 }
1451 
1453  KMMessage * msg = message();
1454 
1455  mMimePartTree->clear();
1456  showHideMimeTree( !msg || // treat no message as "text/plain"
1457  ( msg->type() == DwMime::kTypeText
1458  && msg->subtype() == DwMime::kSubtypePlain ) );
1459 
1460  if ( !msg )
1461  return;
1462 
1463  msg->setOverrideCodec( overrideCodec() );
1464 
1465  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
1466  htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
1467 
1468  if (!parent())
1469  setCaption(msg->subject());
1470 
1471  removeTempFiles();
1472 
1473  mColorBar->setNeutralMode();
1474 
1475  parseMsg(msg);
1476 
1477  if( mColorBar->isNeutral() )
1478  mColorBar->setNormalMode();
1479 
1480  htmlWriter()->queue("</body></html>");
1481  htmlWriter()->flush();
1482 
1483  TQTimer::singleShot( 1, TQT_TQOBJECT(this), TQT_SLOT(injectAttachments()) );
1484 }
1485 
1486 static bool message_was_saved_decrypted_before( const KMMessage * msg ) {
1487  if ( !msg )
1488  return false;
1489  //kdDebug(5006) << "msgId = " << msg->msgId() << endl;
1490  return msg->msgId().stripWhiteSpace().startsWith( "<DecryptedMsg." );
1491 }
1492 
1493 //-----------------------------------------------------------------------------
1495 {
1496  KMMessagePart msgPart;
1497  TQCString subtype, contDisp;
1498  TQByteArray str;
1499 
1500  assert(aMsg!=0);
1501 
1502  aMsg->setIsBeingParsed( true );
1503 
1504  if ( mRootNode && !mRootNode->processed() )
1505  {
1506  kdWarning() << "The root node is not yet processed! Danger!\n";
1507  return;
1508  } else
1509  delete mRootNode;
1510  mRootNode = partNode::fromMessage( aMsg, this );
1511  const TQCString mainCntTypeStr = mRootNode->typeString() + '/' + mRootNode->subTypeString();
1512 
1513  TQString cntDesc = aMsg->subject();
1514  if( cntDesc.isEmpty() )
1515  cntDesc = i18n("( body part )");
1516  KIO::filesize_t cntSize = aMsg->msgSize();
1517  TQString cntEnc;
1518  if( aMsg->contentTransferEncodingStr().isEmpty() )
1519  cntEnc = "7bit";
1520  else
1521  cntEnc = aMsg->contentTransferEncodingStr();
1522 
1523  // fill the MIME part tree viewer
1524  mRootNode->fillMimePartTree( 0,
1525  mMimePartTree,
1526  cntDesc,
1527  mainCntTypeStr,
1528  cntEnc,
1529  cntSize );
1530 
1531  partNode* vCardNode = mRootNode->findType( DwMime::kTypeText, DwMime::kSubtypeXVCard );
1532  bool hasVCard = false;
1533  if( vCardNode ) {
1534  // ### FIXME: We should only do this if the vCard belongs to the sender,
1535  // ### i.e. if the sender's email address is contained in the vCard.
1536  KABC::VCardConverter t;
1537 #if defined(KABC_VCARD_ENCODING_FIX)
1538  const TQByteArray vcard = vCardNode->msgPart().bodyDecodedBinary();
1539  if ( !t.parseVCardsRaw( vcard.data() ).empty() ) {
1540 #else
1541  const TQString vcard = vCardNode->msgPart().bodyToUnicode( overrideCodec() );
1542  if ( !t.parseVCards( vcard ).empty() ) {
1543 #endif
1544  hasVCard = true;
1545  writeMessagePartToTempFile( &vCardNode->msgPart(), vCardNode->nodeId() );
1546  }
1547  }
1548 
1549  if ( !mRootNode || !mRootNode->isToltecMessage() || mShowRawToltecMail ) {
1550  htmlWriter()->queue( writeMsgHeader(aMsg, hasVCard ? vCardNode : 0, true ) );
1551  }
1552 
1553  // show message content
1554  ObjectTreeParser otp( this );
1555  otp.setAllowAsync( true );
1556  otp.setShowRawToltecMail( mShowRawToltecMail );
1557  otp.parseObjectTree( mRootNode );
1558 
1559  // store encrypted/signed status information in the KMMessage
1560  // - this can only be done *after* calling parseObjectTree()
1561  KMMsgEncryptionState encryptionState = mRootNode->overallEncryptionState();
1562  KMMsgSignatureState signatureState = mRootNode->overallSignatureState();
1563  // Don't crash when switching message while GPG passphrase entry dialog is shown #53185
1564  if (aMsg != message()) {
1565  displayMessage();
1566  return;
1567  }
1568  aMsg->setEncryptionState( encryptionState );
1569  // Don't reset the signature state to "not signed" (e.g. if one canceled the
1570  // decryption of a signed messages which has already been decrypted before).
1571  if ( signatureState != KMMsgNotSigned ||
1572  aMsg->signatureState() == KMMsgSignatureStateUnknown ) {
1573  aMsg->setSignatureState( signatureState );
1574  }
1575 
1576  bool emitReplaceMsgByUnencryptedVersion = false;
1577  const KConfigGroup reader( KMKernel::config(), "Reader" );
1578  if ( reader.readBoolEntry( "store-displayed-messages-unencrypted", false ) ) {
1579 
1580  // Hack to make sure the S/MIME CryptPlugs follows the strict requirement
1581  // of german government:
1582  // --> All received encrypted messages *must* be stored in unencrypted form
1583  // after they have been decrypted once the user has read them.
1584  // ( "Aufhebung der Verschluesselung nach dem Lesen" )
1585  //
1586  // note: Since there is no configuration option for this, we do that for
1587  // all kinds of encryption now - *not* just for S/MIME.
1588  // This could be changed in the objectTreeToDecryptedMsg() function
1589  // by deciding when (or when not, resp.) to set the 'dataNode' to
1590  // something different than 'curNode'.
1591 
1592 
1593 kdDebug(5006) << "\n\n\nKMReaderWin::parseMsg() - special post-encryption handling:\n1." << endl;
1594 kdDebug(5006) << "(aMsg == msg) = " << (aMsg == message()) << endl;
1595 kdDebug(5006) << "aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() = " << (aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder()) << endl;
1596 kdDebug(5006) << "message_was_saved_decrypted_before( aMsg ) = " << message_was_saved_decrypted_before( aMsg ) << endl;
1597 kdDebug(5006) << "this->decryptMessage() = " << decryptMessage() << endl;
1598 kdDebug(5006) << "otp.hasPendingAsyncJobs() = " << otp.hasPendingAsyncJobs() << endl;
1599 kdDebug(5006) << " (KMMsgFullyEncrypted == encryptionState) = " << (KMMsgFullyEncrypted == encryptionState) << endl;
1600 kdDebug(5006) << "|| (KMMsgPartiallyEncrypted == encryptionState) = " << (KMMsgPartiallyEncrypted == encryptionState) << endl;
1601  // only proceed if we were called the normal way - not by
1602  // double click on the message (==not running in a separate window)
1603  if( (aMsg == message())
1604  // don't remove encryption in the outbox folder :)
1605  && ( aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() )
1606  // only proceed if this message was not saved encryptedly before
1607  && !message_was_saved_decrypted_before( aMsg )
1608  // only proceed if the message has actually been decrypted
1609  && decryptMessage()
1610  // only proceed if no pending async jobs are running:
1611  && !otp.hasPendingAsyncJobs()
1612  // only proceed if this message is (at least partially) encrypted
1613  && ( (KMMsgFullyEncrypted == encryptionState)
1614  || (KMMsgPartiallyEncrypted == encryptionState) ) ) {
1615 
1616 kdDebug(5006) << "KMReaderWin - calling objectTreeToDecryptedMsg()" << endl;
1617 
1618  NewByteArray decryptedData;
1619  // note: The following call may change the message's headers.
1620  objectTreeToDecryptedMsg( mRootNode, decryptedData, *aMsg );
1621  // add a \0 to the data
1622  decryptedData.appendNULL();
1623  TQCString resultString( decryptedData.data() );
1624 kdDebug(5006) << "KMReaderWin - resulting data:" << resultString << endl;
1625 
1626  if( !resultString.isEmpty() ) {
1627 kdDebug(5006) << "KMReaderWin - composing unencrypted message" << endl;
1628  // try this:
1629  aMsg->setBody( resultString );
1630  KMMessage* unencryptedMessage = new KMMessage( *aMsg );
1631  unencryptedMessage->setParent( 0 );
1632  // because this did not work:
1633  /*
1634  DwMessage dwMsg( aMsg->asDwString() );
1635  dwMsg.Body() = DwBody( DwString( resultString.data() ) );
1636  dwMsg.Body().Parse();
1637  KMMessage* unencryptedMessage = new KMMessage( &dwMsg );
1638  */
1639  //kdDebug(5006) << "KMReaderWin - resulting message:" << unencryptedMessage->asString() << endl;
1640  kdDebug(5006) << "KMReaderWin - attach unencrypted message to aMsg" << endl;
1641  aMsg->setUnencryptedMsg( unencryptedMessage );
1642  emitReplaceMsgByUnencryptedVersion = true;
1643  }
1644  }
1645  }
1646 
1647  // save current main Content-Type before deleting mRootNode
1648  const int rootNodeCntType = mRootNode ? mRootNode->type() : DwMime::kTypeText;
1649  const int rootNodeCntSubtype = mRootNode ? mRootNode->subType() : DwMime::kSubtypePlain;
1650 
1651  // store message id to avoid endless recursions
1652  setIdOfLastViewedMessage( aMsg->msgId() );
1653 
1654  if( emitReplaceMsgByUnencryptedVersion ) {
1655  kdDebug(5006) << "KMReaderWin - invoce saving in decrypted form:" << endl;
1657  } else {
1658  kdDebug(5006) << "KMReaderWin - finished parsing and displaying of message." << endl;
1659  showHideMimeTree( rootNodeCntType == DwMime::kTypeText &&
1660  rootNodeCntSubtype == DwMime::kSubtypePlain );
1661  }
1662 
1663  aMsg->setIsBeingParsed( false );
1664 }
1665 
1666 
1667 //-----------------------------------------------------------------------------
1668 TQString KMReaderWin::writeMsgHeader( KMMessage* aMsg, partNode *vCardNode, bool topLevel )
1669 {
1670  kdFatal( !headerStyle(), 5006 )
1671  << "trying to writeMsgHeader() without a header style set!" << endl;
1672  kdFatal( !headerStrategy(), 5006 )
1673  << "trying to writeMsgHeader() without a header strategy set!" << endl;
1674  TQString href;
1675  if ( vCardNode )
1676  href = vCardNode->asHREF( "body" );
1677 
1678  return headerStyle()->format( aMsg, headerStrategy(), href, mPrinting, topLevel );
1679 }
1680 
1681 
1682 
1683 //-----------------------------------------------------------------------------
1684 TQString KMReaderWin::writeMessagePartToTempFile( KMMessagePart* aMsgPart,
1685  int aPartNum )
1686 {
1687  TQString fileName = aMsgPart->fileName();
1688  if( fileName.isEmpty() )
1689  fileName = aMsgPart->name();
1690 
1691  //--- Sven's save attachments to /tmp start ---
1692  TQString fname = createTempDir( TQString::number( aPartNum ) );
1693  if ( fname.isEmpty() )
1694  return TQString();
1695 
1696  // strip off a leading path
1697  int slashPos = fileName.findRev( '/' );
1698  if( -1 != slashPos )
1699  fileName = fileName.mid( slashPos + 1 );
1700  if( fileName.isEmpty() )
1701  fileName = "unnamed";
1702  fname += "/" + fileName;
1703 
1704  TQByteArray data = aMsgPart->bodyDecodedBinary();
1705  size_t size = data.size();
1706  if ( aMsgPart->type() == DwMime::kTypeText && size) {
1707  // convert CRLF to LF before writing text attachments to disk
1708  size = KMail::Util::crlf2lf( data.data(), size );
1709  }
1710  if( !KPIM::kBytesToFile( data.data(), size, fname, false, false, false ) )
1711  return TQString();
1712 
1713  mTempFiles.append( fname );
1714  // make file read-only so that nobody gets the impression that he might
1715  // edit attached files (cf. bug #52813)
1716  ::chmod( TQFile::encodeName( fname ), S_IRUSR );
1717 
1718  return fname;
1719 }
1720 
1721 TQString KMReaderWin::createTempDir( const TQString &param )
1722 {
1723  KTempFile *tempFile = new KTempFile( TQString(), "." + param );
1724  tempFile->setAutoDelete( true );
1725  TQString fname = tempFile->name();
1726  delete tempFile;
1727 
1728  if( ::access( TQFile::encodeName( fname ), W_OK ) != 0 )
1729  // Not there or not writable
1730  if( ::mkdir( TQFile::encodeName( fname ), 0 ) != 0
1731  || ::chmod( TQFile::encodeName( fname ), S_IRWXU ) != 0 )
1732  return TQString(); //failed create
1733 
1734  assert( !fname.isNull() );
1735 
1736  mTempDirs.append( fname );
1737  return fname;
1738 }
1739 
1740 //-----------------------------------------------------------------------------
1741 void KMReaderWin::showVCard( KMMessagePart *msgPart )
1742 {
1743 #if defined(KABC_VCARD_ENCODING_FIX)
1744  const TQByteArray vCard = msgPart->bodyDecodedBinary();
1745 #else
1746  const TQString vCard = msgPart->bodyToUnicode( overrideCodec() );
1747 #endif
1748  VCardViewer *vcv = new VCardViewer( this, vCard, "vCardDialog" );
1749  vcv->show();
1750 }
1751 
1752 //-----------------------------------------------------------------------------
1754 {
1755  if (!message()) return;
1756  mViewer->view()->print();
1757 }
1758 
1759 
1760 //-----------------------------------------------------------------------------
1761 int KMReaderWin::msgPartFromUrl(const KURL &aUrl)
1762 {
1763  if (aUrl.isEmpty()) return -1;
1764  if (!aUrl.isLocalFile()) return -1;
1765 
1766  TQString path = aUrl.path();
1767  uint right = path.findRev('/');
1768  uint left = path.findRev('.', right);
1769 
1770  bool ok;
1771  int res = path.mid(left + 1, right - left - 1).toInt(&ok);
1772  return (ok) ? res : -1;
1773 }
1774 
1775 
1776 //-----------------------------------------------------------------------------
1777 void KMReaderWin::resizeEvent(TQResizeEvent *)
1778 {
1779  if( !mResizeTimer.isActive() )
1780  {
1781  //
1782  // Combine all resize operations that are requested as long a
1783  // the timer runs.
1784  //
1785  mResizeTimer.start( 100, true );
1786  }
1787 }
1788 
1789 
1790 //-----------------------------------------------------------------------------
1791 void KMReaderWin::slotDelayedResize()
1792 {
1793  mSplitter->setGeometry(0, 0, width(), height());
1794 }
1795 
1796 
1797 //-----------------------------------------------------------------------------
1798 void KMReaderWin::slotTouchMessage()
1799 {
1800  if ( !message() )
1801  return;
1802 
1803  if ( !message()->isNew() && !message()->isUnread() )
1804  return;
1805 
1806  SerNumList serNums;
1807  serNums.append( message()->getMsgSerNum() );
1808  KMCommand *command = new KMSeStatusCommand( KMMsgStatusRead, serNums );
1809  command->start();
1810 
1811  // should we send an MDN?
1812  if ( mNoMDNsWhenEncrypted &&
1813  message()->encryptionState() != KMMsgNotEncrypted &&
1814  message()->encryptionState() != KMMsgEncryptionStateUnknown )
1815  return;
1816 
1817  KMFolder *folder = message()->parent();
1818  if (folder &&
1819  (folder->isOutbox() || folder->isSent() || folder->isTrash() ||
1820  folder->isDrafts() || folder->isTemplates() ) )
1821  return;
1822 
1823  if ( KMMessage * receipt = message()->createMDN( MDN::ManualAction,
1824  MDN::Displayed,
1825  true /* allow GUI */ ) )
1826  if ( !kmkernel->msgSender()->send( receipt ) ) // send or queue
1827  KMessageBox::error( this, i18n("Could not send MDN.") );
1828 }
1829 
1830 
1831 //-----------------------------------------------------------------------------
1832 void KMReaderWin::closeEvent(TQCloseEvent *e)
1833 {
1834  TQWidget::closeEvent(e);
1835  writeConfig();
1836 }
1837 
1838 
1839 bool foundSMIMEData( const TQString aUrl,
1840  TQString& displayName,
1841  TQString& libName,
1842  TQString& keyId )
1843 {
1844  static TQString showCertMan("showCertificate#");
1845  displayName = "";
1846  libName = "";
1847  keyId = "";
1848  int i1 = aUrl.find( showCertMan );
1849  if( -1 < i1 ) {
1850  i1 += showCertMan.length();
1851  int i2 = aUrl.find(" ### ", i1);
1852  if( i1 < i2 )
1853  {
1854  displayName = aUrl.mid( i1, i2-i1 );
1855  i1 = i2+5;
1856  i2 = aUrl.find(" ### ", i1);
1857  if( i1 < i2 )
1858  {
1859  libName = aUrl.mid( i1, i2-i1 );
1860  i2 += 5;
1861 
1862  keyId = aUrl.mid( i2 );
1863  /*
1864  int len = aUrl.length();
1865  if( len > i2+1 ) {
1866  keyId = aUrl.mid( i2, 2 );
1867  i2 += 2;
1868  while( len > i2+1 ) {
1869  keyId += ':';
1870  keyId += aUrl.mid( i2, 2 );
1871  i2 += 2;
1872  }
1873  }
1874  */
1875  }
1876  }
1877  }
1878  return !keyId.isEmpty();
1879 }
1880 
1881 
1882 //-----------------------------------------------------------------------------
1883 void KMReaderWin::slotUrlOn(const TQString &aUrl)
1884 {
1885  const KURL url(aUrl);
1886 
1887  if ( url.protocol() == "kmail" || url.protocol() == "x-kmail" || url.protocol() == "attachment"
1888  || (url.protocol().isEmpty() && url.path().isEmpty()) ) {
1889  mViewer->setDNDEnabled( false );
1890  } else {
1891  mViewer->setDNDEnabled( true );
1892  }
1893 
1894  if ( aUrl.stripWhiteSpace().isEmpty() ) {
1895  KPIM::BroadcastStatus::instance()->reset();
1896  mHoveredUrl = KURL();
1897  mLastClickImagePath = TQString();
1898  return;
1899  }
1900 
1901  mHoveredUrl = url;
1902 
1903  const TQString msg = URLHandlerManager::instance()->statusBarMessage( url, this );
1904 
1905  kdWarning( msg.isEmpty(), 5006 ) << "KMReaderWin::slotUrlOn(): Unhandled URL hover!" << endl;
1906  KPIM::BroadcastStatus::instance()->setTransienStatusMsg( msg );
1907 }
1908 
1909 
1910 //-----------------------------------------------------------------------------
1911 void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &)
1912 {
1913  mClickedUrl = aUrl;
1914 
1915  if ( URLHandlerManager::instance()->handleClick( aUrl, this ) )
1916  return;
1917 
1918  kdWarning( 5006 ) << "KMReaderWin::slotOpenUrl(): Unhandled URL click!" << endl;
1919  emit urlClicked( aUrl, Qt::LeftButton );
1920 }
1921 
1922 //-----------------------------------------------------------------------------
1923 void KMReaderWin::slotUrlPopup(const TQString &aUrl, const TQPoint& aPos)
1924 {
1925  const KURL url( aUrl );
1926  mClickedUrl = url;
1927 
1928  if ( url.protocol() == "mailto" ) {
1929  mCopyURLAction->setText( i18n( "Copy Email Address" ) );
1930  } else {
1931  mCopyURLAction->setText( i18n( "Copy Link Address" ) );
1932  }
1933 
1934  if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) )
1935  return;
1936 
1937  if ( message() ) {
1938  kdWarning( 5006 ) << "KMReaderWin::slotUrlPopup(): Unhandled URL right-click!" << endl;
1939  emitPopupMenu( url, aPos );
1940  }
1941 }
1942 
1943 // Checks if the given node has a parent node that is a DIV which has an ID attribute
1944 // with the value specified here
1945 static bool hasParentDivWithId( const DOM::Node &start, const TQString &id )
1946 {
1947  if ( start.isNull() )
1948  return false;
1949 
1950  if ( start.nodeName().string() == "div" ) {
1951  for ( unsigned int i = 0; i < start.attributes().length(); i++ ) {
1952  if ( start.attributes().item( i ).nodeName().string() == "id" &&
1953  start.attributes().item( i ).nodeValue().string() == id )
1954  return true;
1955  }
1956  }
1957 
1958  if ( !start.parentNode().isNull() )
1959  return hasParentDivWithId( start.parentNode(), id );
1960  else return false;
1961 }
1962 
1963 //-----------------------------------------------------------------------------
1964 void KMReaderWin::showAttachmentPopup( int id, const TQString & name, const TQPoint & p )
1965 {
1966  mAtmCurrent = id;
1967  mAtmCurrentName = name;
1968  KPopupMenu *menu = new KPopupMenu();
1969  menu->insertItem(SmallIcon("fileopen"),i18n("to open", "Open"), 1);
1970  menu->insertItem(i18n("Open With..."), 2);
1971  menu->insertItem(i18n("to view something", "View"), 3);
1972  menu->insertItem(SmallIcon("filesaveas"),i18n("Save As..."), 4);
1973  menu->insertItem(SmallIcon("editcopy"), i18n("Copy"), 9 );
1974  const bool canChange = message()->parent() ? !message()->parent()->isReadOnly() : false;
1975  if ( GlobalSettings::self()->allowAttachmentEditing() && canChange )
1976  menu->insertItem(SmallIcon("edit"), i18n("Edit Attachment"), 8 );
1977  if ( GlobalSettings::self()->allowAttachmentDeletion() && canChange )
1978  menu->insertItem(SmallIcon("editdelete"), i18n("Delete Attachment"), 7 );
1979  if ( name.endsWith( ".xia", false ) &&
1980  Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ) )
1981  menu->insertItem( i18n( "Decrypt With Chiasmus..." ), 6 );
1982  menu->insertItem(i18n("Properties"), 5);
1983 
1984  const bool attachmentInHeader = hasParentDivWithId( mViewer->nodeUnderMouse(), "attachmentInjectionPoint" );
1985  const bool hasScrollbar = mViewer->view()->verticalScrollBar()->isVisible();
1986  if ( attachmentInHeader && hasScrollbar ) {
1987  menu->insertItem( i18n("Scroll To"), 10 );
1988  }
1989 
1990  connect(menu, TQT_SIGNAL(activated(int)), TQT_TQOBJECT(this), TQT_SLOT(slotHandleAttachment(int)));
1991  menu->exec( p ,0 );
1992  delete menu;
1993 }
1994 
1995 //-----------------------------------------------------------------------------
1997 {
1998  if ( !mBox )
1999  return;
2000  // set the width of the frame to a reasonable value for the current GUI style
2001  int frameWidth;
2002  if( style().isA("KeramikStyle") )
2003  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) - 1;
2004  else
2005  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth );
2006  if ( frameWidth < 0 )
2007  frameWidth = 0;
2008  if ( frameWidth != mBox->lineWidth() )
2009  mBox->setLineWidth( frameWidth );
2010 }
2011 
2012 //-----------------------------------------------------------------------------
2013 void KMReaderWin::styleChange( TQStyle& oldStyle )
2014 {
2016  TQWidget::styleChange( oldStyle );
2017 }
2018 
2019 //-----------------------------------------------------------------------------
2020 void KMReaderWin::slotHandleAttachment( int choice )
2021 {
2022  mAtmUpdate = true;
2023  partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
2024  if ( mAtmCurrentName.isEmpty() && node )
2025  mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
2026  if ( choice < 7 ) {
2027  KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand(
2028  node, message(), mAtmCurrent, mAtmCurrentName,
2029  KMHandleAttachmentCommand::AttachmentAction( choice ), 0, this );
2030  connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
2031  TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
2032  command->start();
2033  } else if ( choice == 7 ) {
2034  slotDeleteAttachment( node );
2035  } else if ( choice == 8 ) {
2036  slotEditAttachment( node );
2037  } else if ( choice == 9 ) {
2038  if ( !node ) return;
2039  KURL::List urls;
2040  KURL url = tempFileUrlFromPartNode( node );
2041  if (!url.isValid() ) return;
2042  urls.append( url );
2043  KURLDrag* drag = new KURLDrag( urls, this );
2044  TQApplication::clipboard()->setData( drag, TQClipboard::Clipboard );
2045  } else if ( choice == 10 ) { // Scroll To
2046  scrollToAttachment( node );
2047  }
2048 }
2049 
2050 //-----------------------------------------------------------------------------
2052 {
2053  mViewer->findText();
2054 }
2055 
2056 //-----------------------------------------------------------------------------
2058 {
2059  mViewer->findTextNext();
2060 }
2061 
2062 //-----------------------------------------------------------------------------
2064 {
2065  mUseFixedFont = !mUseFixedFont;
2067  update(true);
2068 }
2069 
2070 
2071 //-----------------------------------------------------------------------------
2073 {
2074  kapp->clipboard()->setText( mViewer->selectedText() );
2075 }
2076 
2077 
2078 //-----------------------------------------------------------------------------
2079 void KMReaderWin::atmViewMsg( KMMessagePart* aMsgPart, int nodeId )
2080 {
2081  assert(aMsgPart!=0);
2082  KMMessage* msg = new KMMessage;
2083  msg->fromString(aMsgPart->bodyDecoded());
2084  assert(msg != 0);
2085  msg->setMsgSerNum( 0 ); // because lookups will fail
2086  // some information that is needed for imap messages with LOD
2087  msg->setParent( message()->parent() );
2088  msg->setUID(message()->UID());
2089  msg->setReadyToShow(true);
2090  KMReaderMainWin *win = new KMReaderMainWin();
2091  win->showMsg( overrideEncoding(), msg, message()->getMsgSerNum(), nodeId );
2092  win->show();
2093 }
2094 
2095 
2096 void KMReaderWin::setMsgPart( partNode * node ) {
2097  htmlWriter()->reset();
2098  mColorBar->hide();
2099  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2100  htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
2101  // end ###
2102  if ( node ) {
2103  ObjectTreeParser otp( this, 0, true );
2104  otp.parseObjectTree( node );
2105  }
2106  // ### this, too
2107  htmlWriter()->queue( "</body></html>" );
2108  htmlWriter()->flush();
2109 }
2110 
2111 //-----------------------------------------------------------------------------
2112 void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML,
2113  const TQString& aFileName, const TQString& pname )
2114 {
2115  KCursorSaver busy(KBusyPtr::busy());
2116  if (kasciistricmp(aMsgPart->typeStr(), "message")==0) {
2117  // if called from compose win
2118  KMMessage* msg = new KMMessage;
2119  assert(aMsgPart!=0);
2120  msg->fromString(aMsgPart->bodyDecoded());
2121  mMainWindow->setCaption(msg->subject());
2122  setMsg(msg, true);
2123  setAutoDelete(true);
2124  } else if (kasciistricmp(aMsgPart->typeStr(), "text")==0) {
2125  if (kasciistricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) {
2126  showVCard( aMsgPart );
2127  return;
2128  }
2129  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2130  htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
2131 
2132  if (aHTML && (kasciistricmp(aMsgPart->subtypeStr(), "html")==0)) { // HTML
2133  // ### this is broken. It doesn't stip off the HTML header and footer!
2134  htmlWriter()->queue( aMsgPart->bodyToUnicode( overrideCodec() ) );
2135  mColorBar->setHtmlMode();
2136  } else { // plain text
2137  const TQCString str = aMsgPart->bodyDecoded();
2138  ObjectTreeParser otp( this );
2139  otp.writeBodyStr( str,
2140  overrideCodec() ? overrideCodec() : aMsgPart->codec(),
2141  message() ? message()->from() : TQString() );
2142  }
2143  htmlWriter()->queue("</body></html>");
2144  htmlWriter()->flush();
2145  mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
2146  } else if (kasciistricmp(aMsgPart->typeStr(), "image")==0 ||
2147  (kasciistricmp(aMsgPart->typeStr(), "application")==0 &&
2148  kasciistricmp(aMsgPart->subtypeStr(), "postscript")==0))
2149  {
2150  if (aFileName.isEmpty()) return; // prevent crash
2151  // Open the window with a size so the image fits in (if possible):
2152  TQImageIO *iio = new TQImageIO();
2153  iio->setFileName(aFileName);
2154  if( iio->read() ) {
2155  TQImage img = iio->image();
2156  TQRect desk = KGlobalSettings::desktopGeometry(mMainWindow);
2157  // determine a reasonable window size
2158  int width, height;
2159  if( img.width() < 50 )
2160  width = 70;
2161  else if( img.width()+20 < desk.width() )
2162  width = img.width()+20;
2163  else
2164  width = desk.width();
2165  if( img.height() < 50 )
2166  height = 70;
2167  else if( img.height()+20 < desk.height() )
2168  height = img.height()+20;
2169  else
2170  height = desk.height();
2171  mMainWindow->resize( width, height );
2172  }
2173  // Just write the img tag to HTML:
2174  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2175  htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
2176  htmlWriter()->write( "<img src=\"file:" +
2177  KURL::encode_string( aFileName ) +
2178  "\" border=\"0\">\n"
2179  "</body></html>\n" );
2180  htmlWriter()->end();
2181  setCaption( i18n("View Attachment: %1").arg( pname ) );
2182  show();
2183  delete iio;
2184  } else {
2185  htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
2186  htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
2187  htmlWriter()->queue( "<pre>" );
2188 
2189  TQString str = aMsgPart->bodyDecoded();
2190  // A TQString cannot handle binary data. So if it's shorter than the
2191  // attachment, we assume the attachment is binary:
2192  if( str.length() < (unsigned) aMsgPart->decodedSize() ) {
2193  str.prepend( i18n("[KMail: Attachment contains binary data. Trying to show first character.]",
2194  "[KMail: Attachment contains binary data. Trying to show first %n characters.]",
2195  str.length()) + TQChar('\n') );
2196  }
2197  htmlWriter()->queue( TQStyleSheet::escape( str ) );
2198  htmlWriter()->queue( "</pre>" );
2199  htmlWriter()->queue("</body></html>");
2200  htmlWriter()->flush();
2201  mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
2202  }
2203  // ---Sven's view text, html and image attachments in html widget end ---
2204 }
2205 
2206 
2207 //-----------------------------------------------------------------------------
2208 void KMReaderWin::slotAtmView( int id, const TQString& name )
2209 {
2210  partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
2211  if( node ) {
2212  mAtmCurrent = id;
2213  mAtmCurrentName = name;
2214  if ( mAtmCurrentName.isEmpty() )
2215  mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
2216 
2217  KMMessagePart& msgPart = node->msgPart();
2218  TQString pname = msgPart.fileName();
2219  if (pname.isEmpty()) pname=msgPart.name();
2220  if (pname.isEmpty()) pname=msgPart.contentDescription();
2221  if (pname.isEmpty()) pname="unnamed";
2222  // image Attachment is saved already
2223  if (kasciistricmp(msgPart.typeStr(), "message")==0) {
2224  atmViewMsg( &msgPart,id );
2225  } else if ((kasciistricmp(msgPart.typeStr(), "text")==0) &&
2226  (kasciistricmp(msgPart.subtypeStr(), "x-vcard")==0)) {
2227  setMsgPart( &msgPart, htmlMail(), name, pname );
2228  } else {
2229  KMReaderMainWin *win = new KMReaderMainWin(&msgPart, htmlMail(),
2230  name, pname, overrideEncoding() );
2231  win->show();
2232  }
2233  }
2234 }
2235 
2236 //-----------------------------------------------------------------------------
2237 void KMReaderWin::openAttachment( int id, const TQString & name )
2238 {
2239  mAtmCurrentName = name;
2240  mAtmCurrent = id;
2241 
2242  TQString str, pname, cmd, fileName;
2243 
2244  partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
2245  if( !node ) {
2246  kdWarning(5006) << "KMReaderWin::openAttachment - could not find node " << id << endl;
2247  return;
2248  }
2249  if ( mAtmCurrentName.isEmpty() )
2250  mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
2251 
2252  KMMessagePart& msgPart = node->msgPart();
2253  if (kasciistricmp(msgPart.typeStr(), "message")==0)
2254  {
2255  atmViewMsg( &msgPart, id );
2256  return;
2257  }
2258 
2259  TQCString contentTypeStr( msgPart.typeStr() + '/' + msgPart.subtypeStr() );
2260  KPIM::kAsciiToLower( contentTypeStr.data() );
2261 
2262  if ( qstrcmp( contentTypeStr, "text/x-vcard" ) == 0 ) {
2263  showVCard( &msgPart );
2264  return;
2265  }
2266 
2267  // determine the MIME type of the attachment
2268  KMimeType::Ptr mimetype;
2269  // prefer the value of the Content-Type header
2270  mimetype = KMimeType::mimeType( TQString::fromLatin1( contentTypeStr ) );
2271  if ( mimetype->name() == "application/octet-stream" ) {
2272  // consider the filename if Content-Type is application/octet-stream
2273  mimetype = KMimeType::findByPath( name, 0, true /* no disk access */ );
2274  }
2275  if ( ( mimetype->name() == "application/octet-stream" )
2276  && msgPart.isComplete() ) {
2277  // consider the attachment's contents if neither the Content-Type header
2278  // nor the filename give us a clue
2279  mimetype = KMimeType::findByFileContent( name );
2280  }
2281 
2282  KService::Ptr offer =
2283  KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
2284 
2285  TQString open_text;
2286  TQString filenameText = msgPart.fileName();
2287  if ( filenameText.isEmpty() )
2288  filenameText = msgPart.name();
2289  if ( offer ) {
2290  open_text = i18n("&Open with '%1'").arg( offer->name() );
2291  } else {
2292  open_text = i18n("&Open With...");
2293  }
2294  const TQString text = i18n("Open attachment '%1'?\n"
2295  "Note that opening an attachment may compromise "
2296  "your system's security.")
2297  .arg( filenameText );
2298  const int choice = KMessageBox::questionYesNoCancel( this, text,
2299  i18n("Open Attachment?"), KStdGuiItem::saveAs(), open_text,
2300  TQString::fromLatin1("askSave") + mimetype->name() ); // dontAskAgainName
2301 
2302  if( choice == KMessageBox::Yes ) { // Save
2303  mAtmUpdate = true;
2304  KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node,
2305  message(), mAtmCurrent, mAtmCurrentName, KMHandleAttachmentCommand::Save,
2306  offer, this );
2307  connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
2308  TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
2309  command->start();
2310  }
2311  else if( choice == KMessageBox::No ) { // Open
2312  KMHandleAttachmentCommand::AttachmentAction action = ( offer ?
2313  KMHandleAttachmentCommand::Open : KMHandleAttachmentCommand::OpenWith );
2314  mAtmUpdate = true;
2315  KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node,
2316  message(), mAtmCurrent, mAtmCurrentName, action, offer, this );
2317  connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
2318  TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
2319  command->start();
2320  } else { // Cancel
2321  kdDebug(5006) << "Canceled opening attachment" << endl;
2322  }
2323 }
2324 
2325 //-----------------------------------------------------------------------------
2327 {
2328  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, -10);
2329 }
2330 
2331 
2332 //-----------------------------------------------------------------------------
2333 void KMReaderWin::slotScrollDown()
2334 {
2335  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, 10);
2336 }
2337 
2338 bool KMReaderWin::atBottom() const
2339 {
2340  const TQScrollView *view = static_cast<const TQScrollView *>(mViewer->widget());
2341  return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
2342 }
2343 
2344 //-----------------------------------------------------------------------------
2345 void KMReaderWin::slotJumpDown()
2346 {
2347  TQScrollView *view = static_cast<TQScrollView *>(mViewer->widget());
2348  int offs = (view->clipper()->height() < 30) ? view->clipper()->height() : 30;
2349  view->scrollBy( 0, view->clipper()->height() - offs );
2350 }
2351 
2352 //-----------------------------------------------------------------------------
2353 void KMReaderWin::slotScrollPrior()
2354 {
2355  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, -(int)(height()*0.8));
2356 }
2357 
2358 
2359 //-----------------------------------------------------------------------------
2360 void KMReaderWin::slotScrollNext()
2361 {
2362  static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, (int)(height()*0.8));
2363 }
2364 
2365 //-----------------------------------------------------------------------------
2366 void KMReaderWin::slotDocumentChanged()
2367 {
2368 
2369 }
2370 
2371 
2372 //-----------------------------------------------------------------------------
2373 void KMReaderWin::slotTextSelected(bool)
2374 {
2375  TQString temp = mViewer->selectedText();
2376  kapp->clipboard()->setText(temp);
2377 }
2378 
2379 //-----------------------------------------------------------------------------
2381 {
2382  mViewer->selectAll();
2383 }
2384 
2385 //-----------------------------------------------------------------------------
2387 {
2388  TQString temp = mViewer->selectedText();
2389  return temp;
2390 }
2391 
2392 
2393 //-----------------------------------------------------------------------------
2394 void KMReaderWin::slotDocumentDone()
2395 {
2396  // mSbVert->setValue(0);
2397 }
2398 
2399 
2400 //-----------------------------------------------------------------------------
2401 void KMReaderWin::setHtmlOverride(bool override)
2402 {
2403  mHtmlOverride = override;
2404  if (message())
2406 }
2407 
2408 
2409 //-----------------------------------------------------------------------------
2410 void KMReaderWin::setHtmlLoadExtOverride(bool override)
2411 {
2412  mHtmlLoadExtOverride = override;
2413  //if (message())
2414  // message()->setDecodeHTML(htmlMail());
2415 }
2416 
2417 
2418 //-----------------------------------------------------------------------------
2420 {
2421  return ((mHtmlMail && !mHtmlOverride) || (!mHtmlMail && mHtmlOverride));
2422 }
2423 
2424 
2425 //-----------------------------------------------------------------------------
2427 {
2428  return ((mHtmlLoadExternal && !mHtmlLoadExtOverride) ||
2429  (!mHtmlLoadExternal && mHtmlLoadExtOverride));
2430 }
2431 
2432 
2433 //-----------------------------------------------------------------------------
2435 {
2436  const TQScrollView * scrollview = static_cast<TQScrollView *>( mViewer->widget() );
2437  mSavedRelativePosition =
2438  static_cast<float>( scrollview->contentsY() ) / scrollview->contentsHeight();
2439 }
2440 
2441 
2442 //-----------------------------------------------------------------------------
2443 void KMReaderWin::update( bool force )
2444 {
2445  KMMessage* msg = message();
2446  if ( msg )
2447  setMsg( msg, force, true /* updateOnly */ );
2448 }
2449 
2450 
2451 //-----------------------------------------------------------------------------
2453 {
2454  KMFolder* tmpFolder;
2455  KMFolder*& folder = aFolder ? *aFolder : tmpFolder;
2456  folder = 0;
2457  if (mMessage)
2458  return mMessage;
2459  if (mLastSerNum) {
2460  KMMessage *message = 0;
2461  int index;
2462  KMMsgDict::instance()->getLocation( mLastSerNum, &folder, &index );
2463  if (folder )
2464  message = folder->getMsg( index );
2465  if (!message)
2466  kdWarning(5006) << "Attempt to reference invalid serial number " << mLastSerNum << "\n" << endl;
2467  return message;
2468  }
2469  return 0;
2470 }
2471 
2472 
2473 
2474 //-----------------------------------------------------------------------------
2475 void KMReaderWin::slotUrlClicked()
2476 {
2477  KMMainWidget *mainWidget = dynamic_cast<KMMainWidget*>(mMainWindow);
2478  uint identity = 0;
2479  if ( message() && message()->parent() ) {
2480  identity = message()->parent()->identity();
2481  }
2482 
2483  KMCommand *command = new KMUrlClickedCommand( mClickedUrl, identity, this,
2484  false, mainWidget );
2485  command->start();
2486 }
2487 
2488 //-----------------------------------------------------------------------------
2489 void KMReaderWin::slotMailtoCompose()
2490 {
2491  KMCommand *command = new KMMailtoComposeCommand( mClickedUrl, message() );
2492  command->start();
2493 }
2494 
2495 //-----------------------------------------------------------------------------
2496 void KMReaderWin::slotMailtoForward()
2497 {
2498  KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mClickedUrl,
2499  message() );
2500  command->start();
2501 }
2502 
2503 //-----------------------------------------------------------------------------
2504 void KMReaderWin::slotMailtoAddAddrBook()
2505 {
2506  KMCommand *command = new KMMailtoAddAddrBookCommand( mClickedUrl,
2507  mMainWindow);
2508  command->start();
2509 }
2510 
2511 //-----------------------------------------------------------------------------
2512 void KMReaderWin::slotMailtoOpenAddrBook()
2513 {
2514  KMCommand *command = new KMMailtoOpenAddrBookCommand( mClickedUrl,
2515  mMainWindow );
2516  command->start();
2517 }
2518 
2519 //-----------------------------------------------------------------------------
2521 {
2522  // we don't necessarily need a mainWidget for KMUrlCopyCommand so
2523  // it doesn't matter if the dynamic_cast fails.
2524  KMCommand *command =
2525  new KMUrlCopyCommand( mClickedUrl,
2526  dynamic_cast<KMMainWidget*>( mMainWindow ) );
2527  command->start();
2528 }
2529 
2530 //-----------------------------------------------------------------------------
2531 void KMReaderWin::slotUrlOpen( const KURL &url )
2532 {
2533  if ( !url.isEmpty() )
2534  mClickedUrl = url;
2535  KMCommand *command = new KMUrlOpenCommand( mClickedUrl, this );
2536  command->start();
2537 }
2538 
2539 //-----------------------------------------------------------------------------
2540 void KMReaderWin::slotAddBookmarks()
2541 {
2542  KMCommand *command = new KMAddBookmarksCommand( mClickedUrl, this );
2543  command->start();
2544 }
2545 
2546 //-----------------------------------------------------------------------------
2548 {
2549  KMCommand *command = new KMUrlSaveCommand( mClickedUrl, mMainWindow );
2550  command->start();
2551 }
2552 
2553 //-----------------------------------------------------------------------------
2555 {
2556  KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mClickedUrl,
2557  message(), copyText() );
2558  command->start();
2559 }
2560 
2561 //-----------------------------------------------------------------------------
2562 partNode * KMReaderWin::partNodeFromUrl( const KURL & url ) {
2563  return mRootNode ? mRootNode->findId( msgPartFromUrl( url ) ) : 0 ;
2564 }
2565 
2566 partNode * KMReaderWin::partNodeForId( int id ) {
2567  return mRootNode ? mRootNode->findId( id ) : 0 ;
2568 }
2569 
2570 
2571 KURL KMReaderWin::tempFileUrlFromPartNode( const partNode * node )
2572 {
2573  if (!node) return KURL();
2574  TQStringList::const_iterator it = mTempFiles.begin();
2575  TQStringList::const_iterator end = mTempFiles.end();
2576 
2577  while ( it != end ) {
2578  TQString path = *it;
2579  it++;
2580  uint right = path.findRev('/');
2581  uint left = path.findRev('.', right);
2582 
2583  bool ok;
2584  int res = path.mid(left + 1, right - left - 1).toInt(&ok);
2585  if ( res == node->nodeId() )
2586  return KURL( path );
2587  }
2588  return KURL();
2589 }
2590 
2591 //-----------------------------------------------------------------------------
2592 void KMReaderWin::slotSaveAttachments()
2593 {
2594  mAtmUpdate = true;
2595  KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( mMainWindow,
2596  message() );
2597  saveCommand->start();
2598 }
2599 
2600 //-----------------------------------------------------------------------------
2601 void KMReaderWin::saveAttachment( const KURL &tempFileName )
2602 {
2603  mAtmCurrent = msgPartFromUrl( tempFileName );
2604  mAtmCurrentName = mClickedUrl.path();
2605  slotHandleAttachment( KMHandleAttachmentCommand::Save ); // save
2606 }
2607 
2608 //-----------------------------------------------------------------------------
2609 void KMReaderWin::slotSaveMsg()
2610 {
2611  KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand( mMainWindow, message() );
2612 
2613  if (saveCommand->url().isEmpty())
2614  delete saveCommand;
2615  else
2616  saveCommand->start();
2617 }
2618 //-----------------------------------------------------------------------------
2620 {
2621  KMCommand *command = new KMIMChatCommand( mClickedUrl, message() );
2622  command->start();
2623 }
2624 
2625 //-----------------------------------------------------------------------------
2626 static TQString linkForNode( const DOM::Node &node )
2627 {
2628  try {
2629  if ( node.isNull() )
2630  return TQString();
2631 
2632  const DOM::NamedNodeMap attributes = node.attributes();
2633  if ( !attributes.isNull() ) {
2634  const DOM::Node href = attributes.getNamedItem( DOM::DOMString( "href" ) );
2635  if ( !href.isNull() ) {
2636  return href.nodeValue().string();
2637  }
2638  }
2639  if ( !node.parentNode().isNull() ) {
2640  return linkForNode( node.parentNode() );
2641  } else {
2642  return TQString();
2643  }
2644  } catch ( DOM::DOMException &e ) {
2645  kdWarning(5006) << "Got an exception when trying to determine link under cursor!" << endl;
2646  return TQString();
2647  }
2648 }
2649 
2650 //-----------------------------------------------------------------------------
2651 bool KMReaderWin::eventFilter( TQObject *, TQEvent *e )
2652 {
2653  if ( e->type() == TQEvent::MouseButtonPress ) {
2654  TQMouseEvent* me = TQT_TQMOUSEEVENT(e);
2655  if ( me->button() == Qt::LeftButton && ( me->state() & ShiftButton ) ) {
2656  // special processing for shift+click
2657  URLHandlerManager::instance()->handleShiftClick( mHoveredUrl, this );
2658  return true;
2659  }
2660 
2661  if ( me->button() == Qt::LeftButton ) {
2662 
2663  TQString imagePath;
2664  const DOM::Node nodeUnderMouse = mViewer->nodeUnderMouse();
2665  if ( !nodeUnderMouse.isNull() ) {
2666  const DOM::NamedNodeMap attributes = nodeUnderMouse.attributes();
2667  if ( !attributes.isNull() ) {
2668  const DOM::Node src = attributes.getNamedItem( DOM::DOMString( "src" ) );
2669  if ( !src.isNull() ) {
2670  imagePath = src.nodeValue().string();
2671  }
2672  }
2673  }
2674 
2675  mCanStartDrag = URLHandlerManager::instance()->willHandleDrag( mHoveredUrl, imagePath, this );
2676  mLastClickPosition = me->pos();
2677  mLastClickImagePath = imagePath;
2678  }
2679  }
2680 
2681  if ( e->type() == TQEvent::MouseButtonRelease ) {
2682  mCanStartDrag = false;
2683  }
2684 
2685  if ( e->type() == TQEvent::MouseMove ) {
2686  TQMouseEvent* me = TQT_TQMOUSEEVENT( e );
2687 
2688  // Handle this ourselves instead of connecting to mViewer::onURL(), since KHTML misses some
2689  // notifications in case we started a drag ourselves
2690  slotUrlOn( linkForNode( mViewer->nodeUnderMouse() ) );
2691 
2692  if ( ( mLastClickPosition - me->pos() ).manhattanLength() > KGlobalSettings::dndEventDelay() ) {
2693  if ( mCanStartDrag && ( !( mHoveredUrl.isEmpty() && mLastClickImagePath.isEmpty() ) ) ) {
2694  if ( URLHandlerManager::instance()->handleDrag( mHoveredUrl, mLastClickImagePath, this ) ) {
2695  mCanStartDrag = false;
2696  slotUrlOn( TQString() );
2697 
2698  // HACK: Send a mouse release event to the KHTMLView, as otherwise that will be missed in
2699  // case we started a drag. If the event is missed, the HTML view gets into a wrong
2700  // state, in which funny things like unsolicited drags start to happen.
2701  TQMouseEvent mouseEvent( TQEvent::MouseButtonRelease, me->pos(), Qt::NoButton, Qt::NoButton );
2702  TQT_TQOBJECT( mViewer->view() )->eventFilter( mViewer->view()->viewport(),
2703  &mouseEvent );
2704  return true;
2705  }
2706  }
2707  }
2708  }
2709 
2710  // standard event processing
2711  return false;
2712 }
2713 
2714 void KMReaderWin::fillCommandInfo( partNode *node, KMMessage **msg, int *nodeId )
2715 {
2716  Q_ASSERT( msg && nodeId );
2717 
2718  if ( mSerNumOfOriginalMessage != 0 ) {
2719  KMFolder *folder = 0;
2720  int index = -1;
2721  KMMsgDict::instance()->getLocation( mSerNumOfOriginalMessage, &folder, &index );
2722  if ( folder && index != -1 )
2723  *msg = folder->getMsg( index );
2724 
2725  if ( !( *msg ) ) {
2726  kdWarning( 5006 ) << "Unable to find the original message, aborting attachment deletion!" << endl;
2727  return;
2728  }
2729 
2730  *nodeId = node->nodeId() + mNodeIdOffset;
2731  }
2732  else {
2733  *nodeId = node->nodeId();
2734  *msg = message();
2735  }
2736 }
2737 
2738 void KMReaderWin::slotDeleteAttachment(partNode * node)
2739 {
2740  if ( KMessageBox::warningContinueCancel( this,
2741  i18n("Deleting an attachment might invalidate any digital signature on this message."),
2742  i18n("Delete Attachment"), KStdGuiItem::del(), "DeleteAttachmentSignatureWarning" )
2743  != KMessageBox::Continue ) {
2744  return;
2745  }
2746 
2747  int nodeId = -1;
2748  KMMessage *msg = 0;
2749  fillCommandInfo( node, &msg, &nodeId );
2750  if ( msg && nodeId != -1 ) {
2751  KMDeleteAttachmentCommand* command = new KMDeleteAttachmentCommand( nodeId, msg, this );
2752  command->start();
2753  connect( command, TQT_SIGNAL( completed( KMCommand * ) ),
2754  TQT_TQOBJECT(this), TQT_SLOT( updateReaderWin() ) );
2755  connect( command, TQT_SIGNAL( completed( KMCommand * ) ),
2756  TQT_TQOBJECT(this), TQT_SLOT( disconnectMsgAdded() ) );
2757 
2758  // ### HACK: Since the command will do delete + add, a new message will arrive. However, we don't
2759  // want the selection to change. Therefore, as soon as a new message arrives, select it, and then
2760  // disconnect.
2761  // Of course the are races, another message can arrive before ours, but we take the risk.
2762  // And it won't work properly with multiple main windows
2763  const KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers();
2764  connect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ),
2765  TQT_TQOBJECT(this), TQT_SLOT( msgAdded( TQListViewItem* ) ) );
2766  }
2767 
2768  // If we are operating on a copy of parts of the message, make sure to update the copy as well.
2769  if ( mSerNumOfOriginalMessage != 0 && message() ) {
2770  message()->deleteBodyPart( node->nodeId() );
2771  update( true );
2772  }
2773 }
2774 
2775 void KMReaderWin::msgAdded( TQListViewItem *item )
2776 {
2777  // A new message was added to the message list view. Select it.
2778  // This is only connected right after we started a attachment delete command, so we expect a new
2779  // message. Disconnect right afterwards, we only want this particular message to be selected.
2781  KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers();
2782  headers->setCurrentItem( item );
2783  headers->clearSelection();
2784  headers->setSelected( item, true );
2785 }
2786 
2788 {
2789  const KMHeaders *const headers = KMKernel::self()->getKMMainWidget()->headers();
2790  disconnect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ),
2791  TQT_TQOBJECT(this), TQT_SLOT( msgAdded( TQListViewItem* ) ) );
2792 }
2793 
2794 void KMReaderWin::slotEditAttachment(partNode * node)
2795 {
2796  if ( KMessageBox::warningContinueCancel( this,
2797  i18n("Modifying an attachment might invalidate any digital signature on this message."),
2798  i18n("Edit Attachment"), KGuiItem( i18n("Edit"), "edit" ), "EditAttachmentSignatureWarning" )
2799  != KMessageBox::Continue ) {
2800  return;
2801  }
2802 
2803  int nodeId = -1;
2804  KMMessage *msg = 0;
2805  fillCommandInfo( node, &msg, &nodeId );
2806  if ( msg && nodeId != -1 ) {
2807  KMEditAttachmentCommand* command = new KMEditAttachmentCommand( nodeId, msg, this );
2808  command->start();
2809  }
2810 
2811  // FIXME: If we are operating on a copy of parts of the message, make sure to update the copy as well.
2812 }
2813 
2814 KMail::CSSHelper* KMReaderWin::cssHelper()
2815 {
2816  return mCSSHelper;
2817 }
2818 
2820 {
2821  if ( !GlobalSettings::self()->alwaysDecrypt() )
2822  return mDecrytMessageOverwrite;
2823  return true;
2824 }
2825 
2826 void KMReaderWin::scrollToAttachment( const partNode *node )
2827 {
2828  DOM::Document doc = mViewer->htmlDocument();
2829 
2830  // The anchors for this are created in ObjectTreeParser::parseObjectTree()
2831  mViewer->gotoAnchor( TQString::fromLatin1( "att%1" ).arg( node->nodeId() ) );
2832 
2833  // Remove any old color markings which might be there
2834  const partNode *root = node->topLevelParent();
2835  for ( int i = 0; i <= root->totalChildCount() + 1; i++ ) {
2836  DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( i + 1 ) );
2837  if ( !attachmentDiv.isNull() )
2838  attachmentDiv.removeAttribute( "style" );
2839  }
2840 
2841  // Don't mark hidden nodes, that would just produce a strange yellow line
2842  if ( node->isDisplayedHidden() )
2843  return;
2844 
2845  // Now, color the div of the attachment in yellow, so that the user sees what happened.
2846  // We created a special marked div for this in writeAttachmentMarkHeader() in ObjectTreeParser,
2847  // find and modify that now.
2848  DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( node->nodeId() ) );
2849  if ( attachmentDiv.isNull() ) {
2850  kdWarning( 5006 ) << "Could not find attachment div for attachment " << node->nodeId() << endl;
2851  return;
2852  }
2853 
2854  attachmentDiv.setAttribute( "style", TQString( "border:2px solid %1" )
2855  .arg( cssHelper()->pgpWarnColor().name() ) );
2856 
2857  // Update rendering, otherwise the rendering is not updated when the user clicks on an attachment
2858  // that causes scrolling and the open attachment dialog
2859  doc.updateRendering();
2860 }
2861 
2862 void KMReaderWin::injectAttachments()
2863 {
2864  // inject attachments in header view
2865  // we have to do that after the otp has run so we also see encrypted parts
2866  DOM::Document doc = mViewer->htmlDocument();
2867  DOM::Element injectionPoint = doc.getElementById( "attachmentInjectionPoint" );
2868  if ( injectionPoint.isNull() )
2869  return;
2870 
2871  TQString imgpath( locate("data","kmail/pics/") );
2872  TQString visibility;
2873  TQString urlHandle;
2874  TQString imgSrc;
2875  if( !showAttachmentQuicklist() ) {
2876  urlHandle.append( "kmail:showAttachmentQuicklist" );
2877  imgSrc.append( "attachmentQuicklistClosed.png" );
2878  } else {
2879  urlHandle.append( "kmail:hideAttachmentQuicklist" );
2880  imgSrc.append( "attachmentQuicklistOpened.png" );
2881  }
2882 
2883  TQString html = renderAttachments( mRootNode, TQApplication::palette().active().background() );
2884  if ( html.isEmpty() )
2885  return;
2886 
2887  TQString link("");
2888  if ( headerStyle() == HeaderStyle::fancy() ) {
2889  link += "<div style=\"text-align: left;\"><a href=\"" + urlHandle + "\"><img src=\"" +
2890  imgpath + imgSrc + "\"/></a></div>";
2891  html.prepend( link );
2892  html.prepend( TQString::fromLatin1( "<div style=\"float:left;\">%1&nbsp;</div>" ).
2893  arg( i18n( "Attachments:" ) ) );
2894  } else {
2895  link += "<div style=\"text-align: right;\"><a href=\"" + urlHandle + "\"><img src=\"" +
2896  imgpath + imgSrc + "\"/></a></div>";
2897  html.prepend( link );
2898  }
2899 
2900  assert( injectionPoint.tagName() == "div" );
2901  static_cast<DOM::HTMLElement>( injectionPoint ).setInnerHTML( html );
2902 }
2903 
2904 static TQColor nextColor( const TQColor & c )
2905 {
2906  int h, s, v;
2907  c.hsv( &h, &s, &v );
2908  return TQColor( (h + 50) % 360, TQMAX(s, 64), v, TQColor::Hsv );
2909 }
2910 
2911 TQString KMReaderWin::renderAttachments(partNode * node, const TQColor &bgColor )
2912 {
2913  if ( !node )
2914  return TQString();
2915 
2916  TQString html;
2917  if ( node->firstChild() ) {
2918  TQString subHtml = renderAttachments( node->firstChild(), nextColor( bgColor ) );
2919  if ( !subHtml.isEmpty() ) {
2920 
2921  TQString visibility;
2922  if ( !showAttachmentQuicklist() ) {
2923  visibility.append( "display:none;" );
2924  }
2925 
2926  TQString margin;
2927  if ( node != mRootNode || headerStyle() != HeaderStyle::enterprise() )
2928  margin = "padding:2px; margin:2px; ";
2929  TQString align = "left";
2930  if ( headerStyle() == HeaderStyle::enterprise() )
2931  align = "right";
2932  if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode )
2933  html += TQString::fromLatin1("<div style=\"background:%1; %2"
2934  "vertical-align:middle; float:%3; %4\">").arg( bgColor.name() ).arg( margin )
2935  .arg( align ).arg( visibility );
2936  html += subHtml;
2937  if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode )
2938  html += "</div>";
2939  }
2940  } else {
2941  partNode::AttachmentDisplayInfo info = node->attachmentDisplayInfo();
2942  if ( info.displayInHeader ) {
2943  html += "<div style=\"float:left;\">";
2944  html += TQString::fromLatin1( "<span style=\"white-space:nowrap; border-width: 0px; border-left-width: 5px; border-color: %1; 2px; border-left-style: solid;\">" ).arg( bgColor.name() );
2945  TQString fileName = writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
2946  TQString href = node->asHREF( "header" );
2947  html += TQString::fromLatin1( "<a href=\"" ) + href +
2948  TQString::fromLatin1( "\">" );
2949  html += "<img style=\"vertical-align:middle;\" src=\"" + info.icon + "\"/>&nbsp;";
2950  if ( headerStyle() == HeaderStyle::enterprise() ) {
2951  TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() );
2952  TQFontMetrics fm( bodyFont );
2953  html += KStringHandler::rPixelSqueeze( info.label, fm, 140 );
2954  } else if ( headerStyle() == HeaderStyle::fancy() ) {
2955  TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() );
2956  TQFontMetrics fm( bodyFont );
2957  html += KStringHandler::rPixelSqueeze( info.label, fm, 640 );
2958  } else {
2959  html += info.label;
2960  }
2961  html += "</a></span></div> ";
2962  }
2963  }
2964 
2965  html += renderAttachments( node->nextSibling(), nextColor ( bgColor ) );
2966  return html;
2967 }
2968 
2969 using namespace KMail::Interface;
2970 
2971 void KMReaderWin::setBodyPartMemento( const partNode * node, const TQCString & which, BodyPartMemento * memento )
2972 {
2973  const TQCString index = node->path() + ':' + which.lower();
2974 
2975  const std::map<TQCString,BodyPartMemento*>::iterator it = mBodyPartMementoMap.lower_bound( index );
2976  if ( it != mBodyPartMementoMap.end() && it->first == index ) {
2977 
2978  if ( memento && memento == it->second )
2979  return;
2980 
2981  delete it->second;
2982 
2983  if ( memento ) {
2984  it->second = memento;
2985  }
2986  else {
2987  mBodyPartMementoMap.erase( it );
2988  }
2989 
2990  } else {
2991  if ( memento ) {
2992  mBodyPartMementoMap.insert( it, std::make_pair( index, memento ) );
2993  }
2994  }
2995 
2996  if ( Observable * o = memento ? memento->asObservable() : 0 )
2997  o->attach( this );
2998 }
2999 
3000 BodyPartMemento * KMReaderWin::bodyPartMemento( const partNode * node, const TQCString & which ) const
3001 {
3002  const TQCString index = node->path() + ':' + which.lower();
3003  const std::map<TQCString,BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.find( index );
3004  if ( it == mBodyPartMementoMap.end() ) {
3005  return 0;
3006  }
3007  else {
3008  return it->second;
3009  }
3010 }
3011 
3012 static void detach_and_delete( BodyPartMemento * memento, KMReaderWin * obs ) {
3013  if ( Observable * const o = memento ? memento->asObservable() : 0 )
3014  o->detach( obs );
3015  delete memento;
3016 }
3017 
3018 void KMReaderWin::clearBodyPartMementos()
3019 {
3020  for ( std::map<TQCString,BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end() ; it != end ; ++it )
3021  // Detach the memento from the reader. When cancelling it, it might trigger an update of the
3022  // reader, which we are not interested in, and which is dangerous, since half the mementos are
3023  // already deleted.
3024  // https://issues.kolab.org/issue4187
3025  detach_and_delete( it->second, this );
3026 
3027  mBodyPartMementoMap.clear();
3028 }
3029 
3030 #include "kmreaderwin.moc"
3031 
3032