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