kmail

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