kmail

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