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