kmail

kmreaderwin.cpp
00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmreaderwin.cpp
00003 // Author: Markus Wuebben <markus.wuebben@kde.org>
00004 
00005 // define this to copy all html that is written to the readerwindow to
00006 // filehtmlwriter.out in the current working directory
00007 // #define KMAIL_READER_HTML_DEBUG 1
00008 
00009 #include <config.h>
00010 
00011 #include "kmreaderwin.h"
00012 
00013 #include "globalsettings.h"
00014 #include "kmversion.h"
00015 #include "kmmainwidget.h"
00016 #include "kmreadermainwin.h"
00017 #include <libtdepim/tdefileio.h>
00018 #include "kmfolderindex.h"
00019 #include "kmcommands.h"
00020 #include "kmmsgpartdlg.h"
00021 #include "mailsourceviewer.h"
00022 using KMail::MailSourceViewer;
00023 #include "partNode.h"
00024 #include "kmmsgdict.h"
00025 #include "messagesender.h"
00026 #include "kcursorsaver.h"
00027 #include "kmfolder.h"
00028 #include "vcardviewer.h"
00029 using KMail::VCardViewer;
00030 #include "objecttreeparser.h"
00031 using KMail::ObjectTreeParser;
00032 #include "partmetadata.h"
00033 using KMail::PartMetaData;
00034 #include "attachmentstrategy.h"
00035 using KMail::AttachmentStrategy;
00036 #include "headerstrategy.h"
00037 using KMail::HeaderStrategy;
00038 #include "headerstyle.h"
00039 using KMail::HeaderStyle;
00040 #include "tdehtmlparthtmlwriter.h"
00041 using KMail::HtmlWriter;
00042 using KMail::KHtmlPartHtmlWriter;
00043 #include "htmlstatusbar.h"
00044 using KMail::HtmlStatusBar;
00045 #include "folderjob.h"
00046 using KMail::FolderJob;
00047 #include "csshelper.h"
00048 using KMail::CSSHelper;
00049 #include "isubject.h"
00050 using KMail::ISubject;
00051 #include "urlhandlermanager.h"
00052 using KMail::URLHandlerManager;
00053 #include "interfaces/observable.h"
00054 #include "util.h"
00055 #include "kmheaders.h"
00056 
00057 #include "broadcaststatus.h"
00058 
00059 #include <kmime_mdn.h>
00060 using namespace KMime;
00061 #ifdef KMAIL_READER_HTML_DEBUG
00062 #include "filehtmlwriter.h"
00063 using KMail::FileHtmlWriter;
00064 #include "teehtmlwriter.h"
00065 using KMail::TeeHtmlWriter;
00066 #endif
00067 
00068 #include <kasciistringtools.h>
00069 #include <kstringhandler.h>
00070 
00071 #include <mimelib/mimepp.h>
00072 #include <mimelib/body.h>
00073 #include <mimelib/utility.h>
00074 
00075 #include <kleo/specialjob.h>
00076 #include <kleo/cryptobackend.h>
00077 #include <kleo/cryptobackendfactory.h>
00078 
00079 // KABC includes
00080 #include <tdeabc/addressee.h>
00081 #include <tdeabc/vcardconverter.h>
00082 
00083 // tdehtml headers
00084 #include <tdehtml_part.h>
00085 #include <tdehtmlview.h> // So that we can get rid of the frames
00086 #include <dom/html_element.h>
00087 #include <dom/html_block.h>
00088 #include <dom/html_document.h>
00089 #include <dom/dom_string.h>
00090 #include <dom/dom_exception.h>
00091 
00092 #include <tdeapplication.h>
00093 // for the click on attachment stuff (dnaber):
00094 #include <kuserprofile.h>
00095 #include <kcharsets.h>
00096 #include <tdepopupmenu.h>
00097 #include <kstandarddirs.h>  // Sven's : for access and getpid
00098 #include <kcursor.h>
00099 #include <kdebug.h>
00100 #include <tdefiledialog.h>
00101 #include <tdelocale.h>
00102 #include <tdemessagebox.h>
00103 #include <tdeglobalsettings.h>
00104 #include <krun.h>
00105 #include <tdetempfile.h>
00106 #include <kprocess.h>
00107 #include <kdialog.h>
00108 #include <tdeaction.h>
00109 #include <kiconloader.h>
00110 #include <kmdcodec.h>
00111 #include <kasciistricmp.h>
00112 #include <kurldrag.h>
00113 
00114 #include <tqclipboard.h>
00115 #include <tqhbox.h>
00116 #include <tqtextcodec.h>
00117 #include <tqpaintdevicemetrics.h>
00118 #include <tqlayout.h>
00119 #include <tqlabel.h>
00120 #include <tqsplitter.h>
00121 #include <tqstyle.h>
00122 
00123 // X headers...
00124 #undef Never
00125 #undef Always
00126 
00127 #include <unistd.h>
00128 #include <stdlib.h>
00129 #include <sys/stat.h>
00130 #include <errno.h>
00131 #include <stdio.h>
00132 #include <ctype.h>
00133 #include <string.h>
00134 
00135 #ifdef HAVE_PATHS_H
00136 #include <paths.h>
00137 #endif
00138 
00139 class NewByteArray : public TQByteArray
00140 {
00141 public:
00142     NewByteArray &appendNULL();
00143     NewByteArray &operator+=( const char * );
00144     NewByteArray &operator+=( const TQByteArray & );
00145     NewByteArray &operator+=( const TQCString & );
00146     TQByteArray& qByteArray();
00147 };
00148 
00149 NewByteArray& NewByteArray::appendNULL()
00150 {
00151     TQByteArray::detach();
00152     uint len1 = size();
00153     if ( !TQByteArray::resize( len1 + 1 ) )
00154         return *this;
00155     *(data() + len1) = '\0';
00156     return *this;
00157 }
00158 NewByteArray& NewByteArray::operator+=( const char * newData )
00159 {
00160     if ( !newData )
00161         return *this;
00162     TQByteArray::detach();
00163     uint len1 = size();
00164     uint len2 = tqstrlen( newData );
00165     if ( !TQByteArray::resize( len1 + len2 ) )
00166         return *this;
00167     memcpy( data() + len1, newData, len2 );
00168     return *this;
00169 }
00170 NewByteArray& NewByteArray::operator+=( const TQByteArray & newData )
00171 {
00172     if ( newData.isNull() )
00173         return *this;
00174     TQByteArray::detach();
00175     uint len1 = size();
00176     uint len2 = newData.size();
00177     if ( !TQByteArray::resize( len1 + len2 ) )
00178         return *this;
00179     memcpy( data() + len1, newData.data(), len2 );
00180     return *this;
00181 }
00182 NewByteArray& NewByteArray::operator+=( const TQCString & newData )
00183 {
00184     if ( newData.isEmpty() )
00185         return *this;
00186     TQByteArray::detach();
00187     uint len1 = size();
00188     uint len2 = newData.length(); // forget about the trailing 0x00 !
00189     if ( !TQByteArray::resize( len1 + len2 ) )
00190         return *this;
00191     memcpy( data() + len1, newData.data(), len2 );
00192     return *this;
00193 }
00194 TQByteArray& NewByteArray::qByteArray()
00195 {
00196     return *((TQByteArray*)this);
00197 }
00198 
00199 // This function returns the complete data that were in this
00200 // message parts - *after* all encryption has been removed that
00201 // could be removed.
00202 // - This is used to store the message in decrypted form.
00203 void KMReaderWin::objectTreeToDecryptedMsg( partNode* node,
00204                                             NewByteArray& resultingData,
00205                                             KMMessage& theMessage,
00206                                             bool weAreReplacingTheRootNode,
00207                                             int recCount )
00208 {
00209   kdDebug(5006) << TQString("-------------------------------------------------" ) << endl;
00210   kdDebug(5006) << TQString("KMReaderWin::objectTreeToDecryptedMsg( %1 )  START").arg( recCount ) << endl;
00211   if( node ) {
00212 
00213     kdDebug(5006) << node->typeString() << '/' << node->subTypeString() << endl;
00214 
00215     partNode* curNode = node;
00216     partNode* dataNode = curNode;
00217     partNode * child = node->firstChild();
00218     const bool bIsMultipart = node->type() == DwMime::kTypeMultipart ;
00219     bool bKeepPartAsIs = false;
00220 
00221     switch( curNode->type() ){
00222       case DwMime::kTypeMultipart: {
00223           switch( curNode->subType() ){
00224           case DwMime::kSubtypeSigned: {
00225               bKeepPartAsIs = true;
00226             }
00227             break;
00228           case DwMime::kSubtypeEncrypted: {
00229               if ( child )
00230                   dataNode = child;
00231             }
00232             break;
00233           }
00234         }
00235         break;
00236       case DwMime::kTypeMessage: {
00237           switch( curNode->subType() ){
00238           case DwMime::kSubtypeRfc822: {
00239               if ( child )
00240                 dataNode = child;
00241             }
00242             break;
00243           }
00244         }
00245         break;
00246       case DwMime::kTypeApplication: {
00247           switch( curNode->subType() ){
00248           case DwMime::kSubtypeOctetStream: {
00249               if ( child )
00250                 dataNode = child;
00251             }
00252             break;
00253           case DwMime::kSubtypePkcs7Signature: {
00254               // note: subtype Pkcs7Signature specifies a signature part
00255               //       which we do NOT want to remove!
00256               bKeepPartAsIs = true;
00257             }
00258             break;
00259           case DwMime::kSubtypePkcs7Mime: {
00260               // note: subtype Pkcs7Mime can also be signed
00261               //       and we do NOT want to remove the signature!
00262               if ( child && curNode->encryptionState() != KMMsgNotEncrypted )
00263                 dataNode = child;
00264             }
00265             break;
00266           }
00267         }
00268         break;
00269     }
00270 
00271 
00272     DwHeaders& rootHeaders( theMessage.headers() );
00273     DwBodyPart * part = dataNode->dwPart() ? dataNode->dwPart() : 0;
00274     DwHeaders * headers(
00275         (part && part->hasHeaders())
00276         ? &part->Headers()
00277         : (  (weAreReplacingTheRootNode || !dataNode->parentNode())
00278             ? &rootHeaders
00279             : 0 ) );
00280     if( dataNode == curNode ) {
00281 kdDebug(5006) << "dataNode == curNode:  Save curNode without replacing it." << endl;
00282 
00283       // A) Store the headers of this part IF curNode is not the root node
00284       //    AND we are not replacing a node that already *has* replaced
00285       //    the root node in previous recursion steps of this function...
00286       if( headers ) {
00287         if( dataNode->parentNode() && !weAreReplacingTheRootNode ) {
00288 kdDebug(5006) << "dataNode is NOT replacing the root node:  Store the headers." << endl;
00289           resultingData += headers->AsString().c_str();
00290         } else if( weAreReplacingTheRootNode && part && part->hasHeaders() ){
00291 kdDebug(5006) << "dataNode replace the root node:  Do NOT store the headers but change" << endl;
00292 kdDebug(5006) << "                                 the Message's headers accordingly." << endl;
00293 kdDebug(5006) << "              old Content-Type = " << rootHeaders.ContentType().AsString().c_str() << endl;
00294 kdDebug(5006) << "              new Content-Type = " << headers->ContentType(   ).AsString().c_str() << endl;
00295           rootHeaders.ContentType()             = headers->ContentType();
00296           theMessage.setContentTransferEncodingStr(
00297               headers->HasContentTransferEncoding()
00298             ? headers->ContentTransferEncoding().AsString().c_str()
00299             : "" );
00300           rootHeaders.ContentDescription() = headers->ContentDescription();
00301           rootHeaders.ContentDisposition() = headers->ContentDisposition();
00302           theMessage.setNeedsAssembly();
00303         }
00304       }
00305 
00306       if ( bKeepPartAsIs ) {
00307           resultingData += dataNode->encodedBody();
00308       } else {
00309 
00310       // B) Store the body of this part.
00311       if( headers && bIsMultipart && dataNode->firstChild() )  {
00312 kdDebug(5006) << "is valid Multipart, processing children:" << endl;
00313         TQCString boundary = headers->ContentType().Boundary().c_str();
00314         curNode = dataNode->firstChild();
00315         // store children of multipart
00316         while( curNode ) {
00317 kdDebug(5006) << "--boundary" << endl;
00318           if( resultingData.size() &&
00319               ( '\n' != resultingData.at( resultingData.size()-1 ) ) )
00320             resultingData += TQCString( "\n" );
00321           resultingData += TQCString( "\n" );
00322           resultingData += "--";
00323           resultingData += boundary;
00324           resultingData += "\n";
00325           // note: We are processing a harmless multipart that is *not*
00326           //       to be replaced by one of it's children, therefor
00327           //       we set their doStoreHeaders to true.
00328           objectTreeToDecryptedMsg( curNode,
00329                                     resultingData,
00330                                     theMessage,
00331                                     false,
00332                                     recCount + 1 );
00333           curNode = curNode->nextSibling();
00334         }
00335 kdDebug(5006) << "--boundary--" << endl;
00336         resultingData += "\n--";
00337         resultingData += boundary;
00338         resultingData += "--\n\n";
00339 kdDebug(5006) << "Multipart processing children - DONE" << endl;
00340       } else if( part ){
00341         // store simple part
00342 kdDebug(5006) << "is Simple part or invalid Multipart, storing body data .. DONE" << endl;
00343         resultingData += part->Body().AsString().c_str();
00344       }
00345       }
00346     } else {
00347 kdDebug(5006) << "dataNode != curNode:  Replace curNode by dataNode." << endl;
00348       bool rootNodeReplaceFlag = weAreReplacingTheRootNode || !curNode->parentNode();
00349       if( rootNodeReplaceFlag ) {
00350 kdDebug(5006) << "                      Root node will be replaced." << endl;
00351       } else {
00352 kdDebug(5006) << "                      Root node will NOT be replaced." << endl;
00353       }
00354       // store special data to replace the current part
00355       // (e.g. decrypted data or embedded RfC 822 data)
00356       objectTreeToDecryptedMsg( dataNode,
00357                                 resultingData,
00358                                 theMessage,
00359                                 rootNodeReplaceFlag,
00360                                 recCount + 1 );
00361     }
00362   }
00363   kdDebug(5006) << TQString("\nKMReaderWin::objectTreeToDecryptedMsg( %1 )  END").arg( recCount ) << endl;
00364 }
00365 
00366 
00367 /*
00368  ===========================================================================
00369 
00370 
00371         E N D    O F     T E M P O R A R Y     M I M E     C O D E
00372 
00373 
00374  ===========================================================================
00375 */
00376 
00377 
00378 
00379 
00380 
00381 
00382 
00383 
00384 
00385 
00386 
00387 void KMReaderWin::createWidgets() {
00388   TQVBoxLayout * vlay = new TQVBoxLayout( this );
00389   mSplitter = new TQSplitter( Qt::Vertical, this, "mSplitter" );
00390   vlay->addWidget( mSplitter );
00391   mMimePartTree = new KMMimePartTree( this, mSplitter, "mMimePartTree" );
00392   mBox = new TQHBox( mSplitter, "mBox" );
00393   setStyleDependantFrameWidth();
00394   mBox->setFrameStyle( mMimePartTree->frameStyle() );
00395   mColorBar = new HtmlStatusBar( mBox, "mColorBar" );
00396   mViewer = new TDEHTMLPart( mBox, "mViewer" );
00397   mSplitter->setOpaqueResize( TDEGlobalSettings::opaqueResize() );
00398   mSplitter->setResizeMode( mMimePartTree, TQSplitter::KeepSize );
00399 }
00400 
00401 const int KMReaderWin::delay = 150;
00402 
00403 //-----------------------------------------------------------------------------
00404 KMReaderWin::KMReaderWin(TQWidget *aParent,
00405              TQWidget *mainWindow,
00406              TDEActionCollection* actionCollection,
00407                          const char *aName,
00408                          int aFlags )
00409   : TQWidget(aParent, aName, aFlags | TQt::WDestructiveClose),
00410     mSerNumOfOriginalMessage( 0 ),
00411     mNodeIdOffset( -1 ),
00412     mAttachmentStrategy( 0 ),
00413     mHeaderStrategy( 0 ),
00414     mHeaderStyle( 0 ),
00415     mUpdateReaderWinTimer( 0, "mUpdateReaderWinTimer" ),
00416     mResizeTimer( 0, "mResizeTimer" ),
00417     mDelayedMarkTimer( 0, "mDelayedMarkTimer" ),
00418     mHeaderRefreshTimer( 0, "mHeaderRefreshTimer" ),
00419     mOldGlobalOverrideEncoding( "---" ), // init with dummy value
00420     mCSSHelper( 0 ),
00421     mRootNode( 0 ),
00422     mMainWindow( mainWindow ),
00423     mActionCollection( actionCollection ),
00424     mMailToComposeAction( 0 ),
00425     mMailToReplyAction( 0 ),
00426     mMailToForwardAction( 0 ),
00427     mAddAddrBookAction( 0 ),
00428     mOpenAddrBookAction( 0 ),
00429     mCopyAction( 0 ),
00430     mCopyURLAction( 0 ),
00431     mUrlOpenAction( 0 ),
00432     mUrlSaveAsAction( 0 ),
00433     mAddBookmarksAction( 0 ),
00434     mStartIMChatAction( 0 ),
00435     mSelectAllAction( 0 ),
00436     mHeaderOnlyAttachmentsAction( 0 ),
00437     mSelectEncodingAction( 0 ),
00438     mToggleFixFontAction( 0 ),
00439     mToggleMimePartTreeAction( 0 ),
00440     mCanStartDrag( false ),
00441     mHtmlWriter( 0 ),
00442     mSavedRelativePosition( 0 ),
00443     mDecrytMessageOverwrite( false ),
00444     mShowSignatureDetails( false ),
00445     mShowAttachmentQuicklist( true ),
00446     mShowRawToltecMail( false )
00447 {
00448   mExternalWindow  = (aParent == mainWindow );
00449   mSplitterSizes << 180 << 100;
00450   mMimeTreeMode = 1;
00451     mMimeTreeModeOverride = -1;
00452   mMimeTreeAtBottom = true;
00453   mAutoDelete = false;
00454   mLastSerNum = 0;
00455   mWaitingForSerNum = 0;
00456   mMessage = 0;
00457   mMsgDisplay = true;
00458   mPrinting = false;
00459   mShowColorbar = false;
00460   mAtmUpdate = false;
00461 
00462   createWidgets();
00463   createActions( actionCollection );
00464   initHtmlWidget();
00465   readConfig();
00466 
00467   mHtmlOverride = false;
00468   mHtmlLoadExtOverride = false;
00469 
00470   mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin() - 1;
00471 
00472   connect( &mUpdateReaderWinTimer, TQT_SIGNAL(timeout()),
00473        TQT_TQOBJECT(this), TQT_SLOT(updateReaderWin()) );
00474   connect( &mResizeTimer, TQT_SIGNAL(timeout()),
00475        TQT_TQOBJECT(this), TQT_SLOT(slotDelayedResize()) );
00476   connect( &mDelayedMarkTimer, TQT_SIGNAL(timeout()),
00477            TQT_TQOBJECT(this), TQT_SLOT(slotTouchMessage()) );
00478   connect( &mHeaderRefreshTimer, TQT_SIGNAL(timeout()),
00479            TQT_TQOBJECT(this), TQT_SLOT(updateHeader()) );
00480 
00481 }
00482 
00483 void KMReaderWin::createActions( TDEActionCollection * ac ) {
00484   if ( !ac )
00485       return;
00486 
00487   TDERadioAction *raction = 0;
00488 
00489   // header style
00490   TDEActionMenu *headerMenu =
00491     new TDEActionMenu( i18n("View->", "&Headers"), ac, "view_headers" );
00492   headerMenu->setToolTip( i18n("Choose display style of message headers") );
00493 
00494   connect( headerMenu, TQT_SIGNAL(activated()),
00495            TQT_TQOBJECT(this), TQT_SLOT(slotCycleHeaderStyles()) );
00496 
00497   raction = new TDERadioAction( i18n("View->headers->", "&Enterprise Headers"), 0,
00498                               TQT_TQOBJECT(this), TQT_SLOT(slotEnterpriseHeaders()),
00499                               ac, "view_headers_enterprise" );
00500   raction->setToolTip( i18n("Show the list of headers in Enterprise style") );
00501   raction->setExclusiveGroup( "view_headers_group" );
00502   headerMenu->insert(raction);
00503 
00504   raction = new TDERadioAction( i18n("View->headers->", "&Fancy Headers"), 0,
00505                               TQT_TQOBJECT(this), TQT_SLOT(slotFancyHeaders()),
00506                               ac, "view_headers_fancy" );
00507   raction->setToolTip( i18n("Show the list of headers in a fancy format") );
00508   raction->setExclusiveGroup( "view_headers_group" );
00509   headerMenu->insert( raction );
00510 
00511   raction = new TDERadioAction( i18n("View->headers->", "&Brief Headers"), 0,
00512                               TQT_TQOBJECT(this), TQT_SLOT(slotBriefHeaders()),
00513                               ac, "view_headers_brief" );
00514   raction->setToolTip( i18n("Show brief list of message headers") );
00515   raction->setExclusiveGroup( "view_headers_group" );
00516   headerMenu->insert( raction );
00517 
00518   raction = new TDERadioAction( i18n("View->headers->", "&Standard Headers"), 0,
00519                               TQT_TQOBJECT(this), TQT_SLOT(slotStandardHeaders()),
00520                               ac, "view_headers_standard" );
00521   raction->setToolTip( i18n("Show standard list of message headers") );
00522   raction->setExclusiveGroup( "view_headers_group" );
00523   headerMenu->insert( raction );
00524 
00525   raction = new TDERadioAction( i18n("View->headers->", "&Long Headers"), 0,
00526                               TQT_TQOBJECT(this), TQT_SLOT(slotLongHeaders()),
00527                               ac, "view_headers_long" );
00528   raction->setToolTip( i18n("Show long list of message headers") );
00529   raction->setExclusiveGroup( "view_headers_group" );
00530   headerMenu->insert( raction );
00531 
00532   raction = new TDERadioAction( i18n("View->headers->", "&All Headers"), 0,
00533                               TQT_TQOBJECT(this), TQT_SLOT(slotAllHeaders()),
00534                               ac, "view_headers_all" );
00535   raction->setToolTip( i18n("Show all message headers") );
00536   raction->setExclusiveGroup( "view_headers_group" );
00537   headerMenu->insert( raction );
00538 
00539   // attachment style
00540   TDEActionMenu *attachmentMenu =
00541     new TDEActionMenu( i18n("View->", "&Attachments"), ac, "view_attachments" );
00542   attachmentMenu->setToolTip( i18n("Choose display style of attachments") );
00543   connect( attachmentMenu, TQT_SIGNAL(activated()),
00544            TQT_TQOBJECT(this), TQT_SLOT(slotCycleAttachmentStrategy()) );
00545 
00546   raction = new TDERadioAction( i18n("View->attachments->", "&As Icons"), 0,
00547                               TQT_TQOBJECT(this), TQT_SLOT(slotIconicAttachments()),
00548                               ac, "view_attachments_as_icons" );
00549   raction->setToolTip( i18n("Show all attachments as icons. Click to see them.") );
00550   raction->setExclusiveGroup( "view_attachments_group" );
00551   attachmentMenu->insert( raction );
00552 
00553   raction = new TDERadioAction( i18n("View->attachments->", "&Smart"), 0,
00554                               TQT_TQOBJECT(this), TQT_SLOT(slotSmartAttachments()),
00555                               ac, "view_attachments_smart" );
00556   raction->setToolTip( i18n("Show attachments as suggested by sender.") );
00557   raction->setExclusiveGroup( "view_attachments_group" );
00558   attachmentMenu->insert( raction );
00559 
00560   raction = new TDERadioAction( i18n("View->attachments->", "&Inline"), 0,
00561                               TQT_TQOBJECT(this), TQT_SLOT(slotInlineAttachments()),
00562                               ac, "view_attachments_inline" );
00563   raction->setToolTip( i18n("Show all attachments inline (if possible)") );
00564   raction->setExclusiveGroup( "view_attachments_group" );
00565   attachmentMenu->insert( raction );
00566 
00567   raction = new TDERadioAction( i18n("View->attachments->", "&Hide"), 0,
00568                               TQT_TQOBJECT(this), TQT_SLOT(slotHideAttachments()),
00569                               ac, "view_attachments_hide" );
00570   raction->setToolTip( i18n("Do not show attachments in the message viewer") );
00571   raction->setExclusiveGroup( "view_attachments_group" );
00572   attachmentMenu->insert( raction );
00573 
00574   mHeaderOnlyAttachmentsAction = new TDERadioAction( i18n( "View->attachments->", "In Header &Only" ), 0,
00575                               TQT_TQOBJECT(this), TQT_SLOT( slotHeaderOnlyAttachments() ),
00576                               ac, "view_attachments_headeronly" );
00577   mHeaderOnlyAttachmentsAction->setToolTip( i18n( "Show Attachments only in the header of the mail" ) );
00578   mHeaderOnlyAttachmentsAction->setExclusiveGroup( "view_attachments_group" );
00579   attachmentMenu->insert( mHeaderOnlyAttachmentsAction );
00580 
00581   // Set Encoding submenu
00582   mSelectEncodingAction = new TDESelectAction( i18n( "&Set Encoding" ), "charset", 0,
00583                                  TQT_TQOBJECT(this), TQT_SLOT( slotSetEncoding() ),
00584                                  ac, "encoding" );
00585   TQStringList encodings = KMMsgBase::supportedEncodings( false );
00586   encodings.prepend( i18n( "Auto" ) );
00587   mSelectEncodingAction->setItems( encodings );
00588   mSelectEncodingAction->setCurrentItem( 0 );
00589 
00590   mMailToComposeAction = new TDEAction( i18n("New Message To..."), "mail-message-new",
00591                                       0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoCompose()), ac,
00592                                       "mailto_compose" );
00593   mMailToReplyAction = new TDEAction( i18n("Reply To..."), "mail-reply-sender",
00594                                     0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoReply()), ac,
00595                     "mailto_reply" );
00596   mMailToForwardAction = new TDEAction( i18n("Forward To..."), "mail-forward",
00597                                       0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoForward()), ac,
00598                                       "mailto_forward" );
00599   mAddAddrBookAction = new TDEAction( i18n("Add to Address Book"),
00600                     0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoAddAddrBook()),
00601                     ac, "add_addr_book" );
00602   mOpenAddrBookAction = new TDEAction( i18n("Open in Address Book"),
00603                                      0, TQT_TQOBJECT(this), TQT_SLOT(slotMailtoOpenAddrBook()),
00604                                      ac, "openin_addr_book" );
00605   mCopyAction = KStdAction::copy( TQT_TQOBJECT(this), TQT_SLOT(slotCopySelectedText()), ac, "kmail_copy");
00606   mSelectAllAction = new TDEAction( i18n("Select All Text"), CTRL+SHIFT+Key_A, TQT_TQOBJECT(this),
00607                                   TQT_SLOT(selectAll()), ac, "mark_all_text" );
00608   mCopyURLAction = new TDEAction( i18n("Copy Link Address"), 0, TQT_TQOBJECT(this),
00609                 TQT_SLOT(slotUrlCopy()), ac, "copy_url" );
00610   mUrlOpenAction = new TDEAction( i18n("Open URL"), 0, TQT_TQOBJECT(this),
00611                                 TQT_SLOT(slotUrlOpen()), ac, "open_url" );
00612   mAddBookmarksAction = new TDEAction( i18n("Bookmark This Link"),
00613                                      "bookmark_add",
00614                                      0, TQT_TQOBJECT(this), TQT_SLOT(slotAddBookmarks()),
00615                                      ac, "add_bookmarks" );
00616   mUrlSaveAsAction = new TDEAction( i18n("Save Link As..."), 0, TQT_TQOBJECT(this),
00617                                   TQT_SLOT(slotUrlSave()), ac, "saveas_url" );
00618 
00619   mToggleFixFontAction = new TDEToggleAction( i18n("Use Fi&xed Font"),
00620                                             Key_X, TQT_TQOBJECT(this), TQT_SLOT(slotToggleFixedFont()),
00621                                             ac, "toggle_fixedfont" );
00622 
00623   mToggleMimePartTreeAction = new TDEToggleAction( i18n("Show Message Structure"),
00624                                             0, ac, "toggle_mimeparttree" );
00625   connect(mToggleMimePartTreeAction, TQT_SIGNAL(toggled(bool)),
00626           TQT_TQOBJECT(this), TQT_SLOT(slotToggleMimePartTree()));
00627 
00628   mStartIMChatAction = new TDEAction( i18n("Chat &With..."), 0, TQT_TQOBJECT(this),
00629                     TQT_SLOT(slotIMChat()), ac, "start_im_chat" );
00630 }
00631 
00632 // little helper function
00633 TDERadioAction *KMReaderWin::actionForHeaderStyle( const HeaderStyle * style, const HeaderStrategy * strategy ) {
00634   if ( !mActionCollection )
00635     return 0;
00636   const char * actionName = 0;
00637   if ( style == HeaderStyle::enterprise() )
00638     actionName = "view_headers_enterprise";
00639   if ( style == HeaderStyle::fancy() )
00640     actionName = "view_headers_fancy";
00641   else if ( style == HeaderStyle::brief() )
00642     actionName = "view_headers_brief";
00643   else if ( style == HeaderStyle::plain() ) {
00644     if ( strategy == HeaderStrategy::standard() )
00645       actionName = "view_headers_standard";
00646     else if ( strategy == HeaderStrategy::rich() )
00647       actionName = "view_headers_long";
00648     else if ( strategy == HeaderStrategy::all() )
00649       actionName = "view_headers_all";
00650   }
00651   if ( actionName )
00652     return static_cast<TDERadioAction*>(mActionCollection->action(actionName));
00653   else
00654     return 0;
00655 }
00656 
00657 TDERadioAction *KMReaderWin::actionForAttachmentStrategy( const AttachmentStrategy * as ) {
00658   if ( !mActionCollection )
00659     return 0;
00660   const char * actionName = 0;
00661   if ( as == AttachmentStrategy::iconic() )
00662     actionName = "view_attachments_as_icons";
00663   else if ( as == AttachmentStrategy::smart() )
00664     actionName = "view_attachments_smart";
00665   else if ( as == AttachmentStrategy::inlined() )
00666     actionName = "view_attachments_inline";
00667   else if ( as == AttachmentStrategy::hidden() )
00668     actionName = "view_attachments_hide";
00669   else if ( as == AttachmentStrategy::headerOnly() )
00670     actionName = "view_attachments_headeronly";
00671 
00672   if ( actionName )
00673     return static_cast<TDERadioAction*>(mActionCollection->action(actionName));
00674   else
00675     return 0;
00676 }
00677 
00678 void KMReaderWin::slotEnterpriseHeaders() {
00679   setHeaderStyleAndStrategy( HeaderStyle::enterprise(),
00680                              HeaderStrategy::rich() );
00681   if( !mExternalWindow )
00682      writeConfig();
00683 }
00684 
00685 void KMReaderWin::slotFancyHeaders() {
00686   setHeaderStyleAndStrategy( HeaderStyle::fancy(),
00687                              HeaderStrategy::rich() );
00688   if( !mExternalWindow )
00689      writeConfig();
00690 }
00691 
00692 void KMReaderWin::slotBriefHeaders() {
00693   setHeaderStyleAndStrategy( HeaderStyle::brief(),
00694                              HeaderStrategy::brief() );
00695   if( !mExternalWindow )
00696      writeConfig();
00697 }
00698 
00699 void KMReaderWin::slotStandardHeaders() {
00700   setHeaderStyleAndStrategy( HeaderStyle::plain(),
00701                              HeaderStrategy::standard());
00702   writeConfig();
00703 }
00704 
00705 void KMReaderWin::slotLongHeaders() {
00706   setHeaderStyleAndStrategy( HeaderStyle::plain(),
00707                              HeaderStrategy::rich() );
00708   if( !mExternalWindow )
00709      writeConfig();
00710 }
00711 
00712 void KMReaderWin::slotAllHeaders() {
00713   setHeaderStyleAndStrategy( HeaderStyle::plain(),
00714                              HeaderStrategy::all() );
00715   if( !mExternalWindow )
00716      writeConfig();
00717 }
00718 
00719 void KMReaderWin::slotLevelQuote( int l )
00720 {
00721   mLevelQuote = l;
00722   saveRelativePosition();
00723   update(true);
00724 }
00725 
00726 void KMReaderWin::slotCycleHeaderStyles() {
00727   const HeaderStrategy * strategy = headerStrategy();
00728   const HeaderStyle * style = headerStyle();
00729 
00730   const char * actionName = 0;
00731   if ( style == HeaderStyle::enterprise() ) {
00732     slotFancyHeaders();
00733     actionName = "view_headers_fancy";
00734   }
00735   if ( style == HeaderStyle::fancy() ) {
00736     slotBriefHeaders();
00737     actionName = "view_headers_brief";
00738   } else if ( style == HeaderStyle::brief() ) {
00739     slotStandardHeaders();
00740     actionName = "view_headers_standard";
00741   } else if ( style == HeaderStyle::plain() ) {
00742     if ( strategy == HeaderStrategy::standard() ) {
00743       slotLongHeaders();
00744       actionName = "view_headers_long";
00745     } else if ( strategy == HeaderStrategy::rich() ) {
00746       slotAllHeaders();
00747       actionName = "view_headers_all";
00748     } else if ( strategy == HeaderStrategy::all() ) {
00749       slotEnterpriseHeaders();
00750       actionName = "view_headers_enterprise";
00751     }
00752   }
00753 
00754   if ( actionName )
00755     static_cast<TDERadioAction*>( mActionCollection->action( actionName ) )->setChecked( true );
00756 }
00757 
00758 
00759 void KMReaderWin::slotIconicAttachments() {
00760   setAttachmentStrategy( AttachmentStrategy::iconic() );
00761 }
00762 
00763 void KMReaderWin::slotSmartAttachments() {
00764   setAttachmentStrategy( AttachmentStrategy::smart() );
00765 }
00766 
00767 void KMReaderWin::slotInlineAttachments() {
00768   setAttachmentStrategy( AttachmentStrategy::inlined() );
00769 }
00770 
00771 void KMReaderWin::slotHideAttachments() {
00772   setAttachmentStrategy( AttachmentStrategy::hidden() );
00773 }
00774 
00775 void KMReaderWin::slotHeaderOnlyAttachments() {
00776   setAttachmentStrategy( AttachmentStrategy::headerOnly() );
00777 }
00778 
00779 void KMReaderWin::slotCycleAttachmentStrategy() {
00780   setAttachmentStrategy( attachmentStrategy()->next() );
00781   TDERadioAction * action = actionForAttachmentStrategy( attachmentStrategy() );
00782   assert( action );
00783   action->setChecked( true );
00784 }
00785 
00786 
00787 //-----------------------------------------------------------------------------
00788 KMReaderWin::~KMReaderWin()
00789 {
00790   if (message()) {
00791     message()->detach( this );
00792   }
00793   clearBodyPartMementos();
00794   delete mHtmlWriter; mHtmlWriter = 0;
00795   delete mCSSHelper;
00796   if (mAutoDelete) delete message();
00797   delete mRootNode; mRootNode = 0;
00798   removeTempFiles();
00799 }
00800 
00801 
00802 //-----------------------------------------------------------------------------
00803 void KMReaderWin::slotMessageArrived( KMMessage *msg )
00804 {
00805   if (msg && ((KMMsgBase*)msg)->isMessage()) {
00806     if ( msg->getMsgSerNum() == mWaitingForSerNum ) {
00807       setMsg( msg, true );
00808     } else {
00809       //kdDebug( 5006 ) <<  "KMReaderWin::slotMessageArrived - ignoring update" << endl;
00810     }
00811   }
00812 }
00813 
00814 //-----------------------------------------------------------------------------
00815 void KMReaderWin::update( KMail::Interface::Observable * observable )
00816 {
00817   if ( !mAtmUpdate ) {
00818     // reparse the msg
00819     //kdDebug(5006) << "KMReaderWin::update - message" << endl;
00820     updateReaderWin();
00821     return;
00822   }
00823 
00824   if ( !mRootNode )
00825     return;
00826 
00827   KMMessage* msg = static_cast<KMMessage*>( observable );
00828   assert( msg != 0 );
00829 
00830   // find our partNode and update it
00831   if ( !msg->lastUpdatedPart() ) {
00832     kdDebug(5006) << "KMReaderWin::update - no updated part" << endl;
00833     return;
00834   }
00835   partNode* node = mRootNode->findNodeForDwPart( msg->lastUpdatedPart() );
00836   if ( !node ) {
00837     kdDebug(5006) << "KMReaderWin::update - can't find node for part" << endl;
00838     return;
00839   }
00840   node->setDwPart( msg->lastUpdatedPart() );
00841 
00842   // update the tmp file
00843   // we have to set it writeable temporarily
00844   ::chmod( TQFile::encodeName( mAtmCurrentName ), S_IRWXU );
00845   TQByteArray data = node->msgPart().bodyDecodedBinary();
00846   size_t size = data.size();
00847   if ( node->msgPart().type() == DwMime::kTypeText && size) {
00848     size = KMail::Util::crlf2lf( data.data(), size );
00849   }
00850   KPIM::kBytesToFile( data.data(), size, mAtmCurrentName, false, false, false );
00851   ::chmod( TQFile::encodeName( mAtmCurrentName ), S_IRUSR );
00852 
00853   mAtmUpdate = false;
00854 }
00855 
00856 //-----------------------------------------------------------------------------
00857 void KMReaderWin::removeTempFiles()
00858 {
00859   for (TQStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end();
00860     it++)
00861   {
00862     TQFile::remove(*it);
00863   }
00864   mTempFiles.clear();
00865   for (TQStringList::Iterator it = mTempDirs.begin(); it != mTempDirs.end();
00866     it++)
00867   {
00868     TQDir(*it).rmdir(*it);
00869   }
00870   mTempDirs.clear();
00871 }
00872 
00873 
00874 //-----------------------------------------------------------------------------
00875 bool KMReaderWin::event(TQEvent *e)
00876 {
00877   if (e->type() == TQEvent::ApplicationPaletteChange)
00878   {
00879     delete mCSSHelper;
00880     mCSSHelper = new KMail::CSSHelper(  TQPaintDeviceMetrics( mViewer->view() ) );
00881     if (message())
00882       message()->readConfig();
00883     update( true ); // Force update
00884     return true;
00885   }
00886   return TQWidget::event(e);
00887 }
00888 
00889 
00890 //-----------------------------------------------------------------------------
00891 void KMReaderWin::readConfig(void)
00892 {
00893   const TDEConfigGroup mdnGroup( KMKernel::config(), "MDN" );
00894   /*should be: const*/ TDEConfigGroup reader( KMKernel::config(), "Reader" );
00895 
00896   delete mCSSHelper;
00897   mCSSHelper = new KMail::CSSHelper( TQPaintDeviceMetrics( mViewer->view() ) );
00898 
00899   mNoMDNsWhenEncrypted = mdnGroup.readBoolEntry( "not-send-when-encrypted", true );
00900 
00901   mUseFixedFont = reader.readBoolEntry( "useFixedFont", false );
00902   if ( mToggleFixFontAction )
00903     mToggleFixFontAction->setChecked( mUseFixedFont );
00904 
00905   mHtmlMail = reader.readBoolEntry( "htmlMail", false );
00906   mHtmlLoadExternal = reader.readBoolEntry( "htmlLoadExternal", false );
00907 
00908   setHeaderStyleAndStrategy( HeaderStyle::create( reader.readEntry( "header-style", "fancy" ) ),
00909                  HeaderStrategy::create( reader.readEntry( "header-set-displayed", "rich" ) ) );
00910   TDERadioAction *raction = actionForHeaderStyle( headerStyle(), headerStrategy() );
00911   if ( raction )
00912     raction->setChecked( true );
00913 
00914   setAttachmentStrategy( AttachmentStrategy::create( reader.readEntry( "attachment-strategy", "smart" ) ) );
00915   raction = actionForAttachmentStrategy( attachmentStrategy() );
00916   if ( raction )
00917     raction->setChecked( true );
00918 
00919   // if the user uses OpenPGP then the color bar defaults to enabled
00920   // else it defaults to disabled
00921   mShowColorbar = reader.readBoolEntry( "showColorbar", Kpgp::Module::getKpgp()->usePGP() );
00922   // if the value defaults to enabled and KMail (with color bar) is used for
00923   // the first time the config dialog doesn't know this if we don't save the
00924   // value now
00925   reader.writeEntry( "showColorbar", mShowColorbar );
00926 
00927   mMimeTreeAtBottom = reader.readEntry( "MimeTreeLocation", "bottom" ) != "top";
00928   const TQString s = reader.readEntry( "MimeTreeMode", "smart" );
00929   if ( s == "never" )
00930     mMimeTreeMode = 0;
00931   else if ( s == "always" )
00932     mMimeTreeMode = 2;
00933   else
00934     mMimeTreeMode = 1;
00935 
00936   const int mimeH = reader.readNumEntry( "MimePaneHeight", 100 );
00937   const int messageH = reader.readNumEntry( "MessagePaneHeight", 180 );
00938   mSplitterSizes.clear();
00939   if ( mMimeTreeAtBottom )
00940     mSplitterSizes << messageH << mimeH;
00941   else
00942     mSplitterSizes << mimeH << messageH;
00943 
00944   adjustLayout();
00945 
00946   readGlobalOverrideCodec();
00947 
00948   if (message())
00949     update();
00950   KMMessage::readConfig();
00951 }
00952 
00953 
00954 void KMReaderWin::adjustLayout() {
00955   if ( mMimeTreeAtBottom )
00956     mSplitter->moveToLast( mMimePartTree );
00957   else
00958     mSplitter->moveToFirst( mMimePartTree );
00959   mSplitter->setSizes( mSplitterSizes );
00960 
00961   if ( mMimeTreeMode == 2 && mMsgDisplay )
00962     mMimePartTree->show();
00963   else
00964     mMimePartTree->hide();
00965 
00966   if ( mShowColorbar && mMsgDisplay )
00967     mColorBar->show();
00968   else
00969     mColorBar->hide();
00970 }
00971 
00972 
00973 void KMReaderWin::saveSplitterSizes( TDEConfigBase & c ) const {
00974   if ( !mSplitter || !mMimePartTree )
00975     return;
00976   if ( mMimePartTree->isHidden() )
00977     return; // don't rely on TQSplitter maintaining sizes for hidden widgets.
00978 
00979   c.writeEntry( "MimePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 1 : 0 ] );
00980   c.writeEntry( "MessagePaneHeight", mSplitter->sizes()[ mMimeTreeAtBottom ? 0 : 1 ] );
00981 }
00982 
00983 //-----------------------------------------------------------------------------
00984 void KMReaderWin::writeConfig( bool sync ) const {
00985   TDEConfigGroup reader( KMKernel::config(), "Reader" );
00986 
00987   reader.writeEntry( "useFixedFont", mUseFixedFont );
00988   if ( headerStyle() )
00989     reader.writeEntry( "header-style", headerStyle()->name() );
00990   if ( headerStrategy() )
00991     reader.writeEntry( "header-set-displayed", headerStrategy()->name() );
00992   if ( attachmentStrategy() )
00993     reader.writeEntry( "attachment-strategy", attachmentStrategy()->name() );
00994 
00995   saveSplitterSizes( reader );
00996 
00997   if ( sync )
00998     kmkernel->slotRequestConfigSync();
00999 }
01000 
01001 //-----------------------------------------------------------------------------
01002 void KMReaderWin::initHtmlWidget(void)
01003 {
01004   mViewer->widget()->setFocusPolicy(TQ_WheelFocus);
01005   // Let's better be paranoid and disable plugins (it defaults to enabled):
01006   mViewer->setPluginsEnabled(false);
01007   mViewer->setJScriptEnabled(false); // just make this explicit
01008   mViewer->setJavaEnabled(false);    // just make this explicit
01009   mViewer->setMetaRefreshEnabled(false);
01010   mViewer->setURLCursor(KCursor::handCursor());
01011   // Espen 2000-05-14: Getting rid of thick ugly frames
01012   mViewer->view()->setLineWidth(0);
01013   // register our own event filter for shift-click
01014   mViewer->view()->viewport()->installEventFilter( this );
01015 
01016   if ( !htmlWriter() )
01017 #ifdef KMAIL_READER_HTML_DEBUG
01018     mHtmlWriter = new TeeHtmlWriter( new FileHtmlWriter( TQString() ),
01019                      new KHtmlPartHtmlWriter( mViewer, 0 ) );
01020 #else
01021     mHtmlWriter = new KHtmlPartHtmlWriter( mViewer, 0 );
01022 #endif
01023 
01024   connect(mViewer->browserExtension(),
01025           TQT_SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),this,
01026           TQT_SLOT(slotUrlOpen(const KURL &)));
01027   connect(mViewer->browserExtension(),
01028           TQT_SIGNAL(createNewWindow(const KURL &, const KParts::URLArgs &)),this,
01029           TQT_SLOT(slotUrlOpen(const KURL &)));
01030   connect(mViewer,TQT_SIGNAL(popupMenu(const TQString &, const TQPoint &)),
01031           TQT_SLOT(slotUrlPopup(const TQString &, const TQPoint &)));
01032   connect( kmkernel->imProxy(), TQT_SIGNAL( sigContactPresenceChanged( const TQString & ) ),
01033           TQT_TQOBJECT(this), TQT_SLOT( contactStatusChanged( const TQString & ) ) );
01034   connect( kmkernel->imProxy(), TQT_SIGNAL( sigPresenceInfoExpired() ),
01035           TQT_TQOBJECT(this), TQT_SLOT( updateReaderWin() ) );
01036 }
01037 
01038 void KMReaderWin::contactStatusChanged( const TQString &uid)
01039 {
01040 //  kdDebug( 5006 ) << k_funcinfo << " got a presence change for " << uid << endl;
01041   // get the list of nodes for this contact from the htmlView
01042   DOM::NodeList presenceNodes = mViewer->htmlDocument()
01043     .getElementsByName( DOM::DOMString( TQString::fromLatin1("presence-") + uid ) );
01044   for ( unsigned int i = 0; i < presenceNodes.length(); ++i ) {
01045     DOM::Node n =  presenceNodes.item( i );
01046     kdDebug( 5006 ) << "name is " << n.nodeName().string() << endl;
01047     kdDebug( 5006 ) << "value of content was " << n.firstChild().nodeValue().string() << endl;
01048     TQString newPresence = kmkernel->imProxy()->presenceString( uid );
01049     if ( newPresence.isNull() ) // TDEHTML crashes if you setNodeValue( TQString() )
01050       newPresence = TQString::fromLatin1( "ENOIMRUNNING" );
01051     n.firstChild().setNodeValue( newPresence );
01052 //    kdDebug( 5006 ) << "value of content is now " << n.firstChild().nodeValue().string() << endl;
01053   }
01054 //  kdDebug( 5006 ) << "and we updated the above presence nodes" << uid << endl;
01055 }
01056 
01057 void KMReaderWin::setAttachmentStrategy( const AttachmentStrategy * strategy ) {
01058   mAttachmentStrategy = strategy ? strategy : AttachmentStrategy::smart();
01059   update( true );
01060 }
01061 
01062 void KMReaderWin::setHeaderStyleAndStrategy( const HeaderStyle * style,
01063                          const HeaderStrategy * strategy ) {
01064   mHeaderStyle = style ? style : HeaderStyle::fancy();
01065   mHeaderStrategy = strategy ? strategy : HeaderStrategy::rich();
01066   if ( mHeaderOnlyAttachmentsAction ) {
01067     const bool styleHasAttachmentQuickList = mHeaderStyle == HeaderStyle::fancy() ||
01068                                              mHeaderStyle == HeaderStyle::enterprise();
01069     mHeaderOnlyAttachmentsAction->setEnabled( styleHasAttachmentQuickList );
01070     if ( !styleHasAttachmentQuickList && mAttachmentStrategy == AttachmentStrategy::headerOnly() ) {
01071       // Style changed to something without an attachment quick list, need to change attachment
01072       // strategy
01073       setAttachmentStrategy( AttachmentStrategy::smart() );
01074     }
01075   }
01076   update( true );
01077 }
01078 
01079 //-----------------------------------------------------------------------------
01080 void KMReaderWin::setOverrideEncoding( const TQString & encoding )
01081 {
01082   if ( encoding == mOverrideEncoding )
01083     return;
01084 
01085   mOverrideEncoding = encoding;
01086   if ( mSelectEncodingAction ) {
01087     if ( encoding.isEmpty() ) {
01088       mSelectEncodingAction->setCurrentItem( 0 );
01089     }
01090     else {
01091       TQStringList encodings = mSelectEncodingAction->items();
01092       uint i = 0;
01093       for ( TQStringList::const_iterator it = encodings.begin(), end = encodings.end(); it != end; ++it, ++i ) {
01094         if ( TDEGlobal::charsets()->encodingForName( *it ) == encoding ) {
01095           mSelectEncodingAction->setCurrentItem( i );
01096           break;
01097         }
01098       }
01099       if ( i == encodings.size() ) {
01100         // the value of encoding is unknown => use Auto
01101         kdWarning(5006) << "Unknown override character encoding \"" << encoding
01102                         << "\". Using Auto instead." << endl;
01103         mSelectEncodingAction->setCurrentItem( 0 );
01104         mOverrideEncoding = TQString();
01105       }
01106     }
01107   }
01108   update( true );
01109 }
01110 
01111 
01112 void KMReaderWin::setPrintFont( const TQFont& font )
01113 {
01114 
01115   mCSSHelper->setPrintFont( font );
01116 }
01117 
01118 //-----------------------------------------------------------------------------
01119 const TQTextCodec * KMReaderWin::overrideCodec() const
01120 {
01121   if ( mOverrideEncoding.isEmpty() || mOverrideEncoding == "Auto" ) // Auto
01122     return 0;
01123   else
01124     return KMMsgBase::codecForName( mOverrideEncoding.latin1() );
01125 }
01126 
01127 //-----------------------------------------------------------------------------
01128 void KMReaderWin::slotSetEncoding()
01129 {
01130   if ( mSelectEncodingAction->currentItem() == 0 ) // Auto
01131     mOverrideEncoding = TQString();
01132   else
01133     mOverrideEncoding = TDEGlobal::charsets()->encodingForName( mSelectEncodingAction->currentText() );
01134   update( true );
01135 }
01136 
01137 //-----------------------------------------------------------------------------
01138 void KMReaderWin::readGlobalOverrideCodec()
01139 {
01140   // if the global character encoding wasn't changed then there's nothing to do
01141   if ( GlobalSettings::self()->overrideCharacterEncoding() == mOldGlobalOverrideEncoding )
01142     return;
01143 
01144   setOverrideEncoding( GlobalSettings::self()->overrideCharacterEncoding() );
01145   mOldGlobalOverrideEncoding = GlobalSettings::self()->overrideCharacterEncoding();
01146 }
01147 
01148 //-----------------------------------------------------------------------------
01149 void KMReaderWin::setOriginalMsg( unsigned long serNumOfOriginalMessage, int nodeIdOffset )
01150 {
01151   mSerNumOfOriginalMessage = serNumOfOriginalMessage;
01152   mNodeIdOffset = nodeIdOffset;
01153 }
01154 
01155 //-----------------------------------------------------------------------------
01156 void KMReaderWin::setMsg( KMMessage* aMsg, bool force, bool updateOnly )
01157 {
01158   if ( aMsg ) {
01159     kdDebug(5006) << "(" << aMsg->getMsgSerNum() << ", last " << mLastSerNum << ") " << aMsg->subject() << " "
01160                   << aMsg->fromStrip() << ", readyToShow " << (aMsg->readyToShow()) << endl;
01161   }
01162 
01163   // Reset message-transient state
01164   if ( aMsg && aMsg->getMsgSerNum() != mLastSerNum && !updateOnly ){
01165     mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin()-1;
01166     mShowRawToltecMail = !GlobalSettings::self()->showToltecReplacementText();
01167     clearBodyPartMementos();
01168   }
01169   if ( mPrinting )
01170     mLevelQuote = -1;
01171 
01172   bool complete = true;
01173   if ( aMsg &&
01174        !aMsg->readyToShow() &&
01175        (aMsg->getMsgSerNum() != mLastSerNum) &&
01176        !aMsg->isComplete() )
01177     complete = false;
01178 
01179   // If not forced and there is aMsg and aMsg is same as mMsg then return
01180   if (!force && aMsg && mLastSerNum != 0 && aMsg->getMsgSerNum() == mLastSerNum)
01181     return;
01182 
01183   // (de)register as observer
01184   if (aMsg && message())
01185     message()->detach( this );
01186   if (aMsg)
01187     aMsg->attach( this );
01188   mAtmUpdate = false;
01189 
01190   mDelayedMarkTimer.stop();
01191 
01192   mMessage = 0;
01193   if ( !aMsg ) {
01194     mWaitingForSerNum = 0; // otherwise it has been set
01195     mLastSerNum = 0;
01196   } else {
01197     mLastSerNum = aMsg->getMsgSerNum();
01198     // Check if the serial number can be used to find the assoc KMMessage
01199     // If so, keep only the serial number (and not mMessage), to avoid a dangling mMessage
01200     // when going to another message in the mainwindow.
01201     // Otherwise, keep only mMessage, this is fine for standalone KMReaderMainWins since
01202     // we're working on a copy of the KMMessage, which we own.
01203     if (message() != aMsg) {
01204       mMessage = aMsg;
01205       mLastSerNum = 0;
01206     }
01207   }
01208 
01209   if (aMsg) {
01210     aMsg->setOverrideCodec( overrideCodec() );
01211     aMsg->setDecodeHTML( htmlMail() );
01212     // FIXME: workaround to disable DND for IMAP load-on-demand
01213     if ( !aMsg->isComplete() )
01214       mViewer->setDNDEnabled( false );
01215     else
01216       mViewer->setDNDEnabled( true );
01217   }
01218 
01219   // only display the msg if it is complete
01220   // otherwise we'll get flickering with progressively loaded messages
01221   if ( complete )
01222   {
01223     // Avoid flicker, somewhat of a cludge
01224     if (force) {
01225       // stop the timer to avoid calling updateReaderWin twice
01226       mUpdateReaderWinTimer.stop();
01227       updateReaderWin();
01228     }
01229     else if (mUpdateReaderWinTimer.isActive())
01230       mUpdateReaderWinTimer.changeInterval( delay );
01231     else
01232       mUpdateReaderWinTimer.start( 0, true );
01233   }
01234 
01235   if ( aMsg && (aMsg->isUnread() || aMsg->isNew()) && GlobalSettings::self()->delayedMarkAsRead() ) {
01236     if ( GlobalSettings::self()->delayedMarkTime() != 0 )
01237       mDelayedMarkTimer.start( GlobalSettings::self()->delayedMarkTime() * 1000, true );
01238     else
01239       slotTouchMessage();
01240   }
01241 
01242   mHeaderRefreshTimer.start( 1000, false );
01243 }
01244 
01245 //-----------------------------------------------------------------------------
01246 void KMReaderWin::clearCache()
01247 {
01248   mUpdateReaderWinTimer.stop();
01249   clear();
01250   mDelayedMarkTimer.stop();
01251   mLastSerNum = 0;
01252   mWaitingForSerNum = 0;
01253   mMessage = 0;
01254 }
01255 
01256 // enter items for the "Important changes" list here:
01257 static const char * const kmailChanges[] = {
01258   ""
01259 };
01260 static const int numKMailChanges =
01261   sizeof kmailChanges / sizeof *kmailChanges;
01262 
01263 // enter items for the "new features" list here, so the main body of
01264 // the welcome page can be left untouched (probably much easier for
01265 // the translators). Note that the <li>...</li> tags are added
01266 // automatically below:
01267 static const char * const kmailNewFeatures[] = {
01268   I18N_NOOP("Full namespace support for IMAP"),
01269   I18N_NOOP("Offline mode"),
01270   I18N_NOOP("Sieve script management and editing"),
01271   I18N_NOOP("Account specific filtering"),
01272   I18N_NOOP("Filtering of incoming mail for online IMAP accounts"),
01273   I18N_NOOP("Online IMAP folders can be used when filtering into folders"),
01274   I18N_NOOP("Automatically delete older mails on POP servers")
01275 };
01276 static const int numKMailNewFeatures =
01277   sizeof kmailNewFeatures / sizeof *kmailNewFeatures;
01278 
01279 
01280 //-----------------------------------------------------------------------------
01281 //static
01282 TQString KMReaderWin::newFeaturesMD5()
01283 {
01284   TQCString str;
01285   for ( int i = 0 ; i < numKMailChanges ; ++i )
01286     str += kmailChanges[i];
01287   for ( int i = 0 ; i < numKMailNewFeatures ; ++i )
01288     str += kmailNewFeatures[i];
01289   KMD5 md5( str );
01290   return md5.base64Digest();
01291 }
01292 
01293 //-----------------------------------------------------------------------------
01294 void KMReaderWin::displaySplashPage( const TQString &info )
01295 {
01296   mMsgDisplay = false;
01297   adjustLayout();
01298 
01299   TQString location = locate("data", "kmail/about/main.html");
01300   TQString content = KPIM::kFileToString(location);
01301   content = content.arg( locate( "data", "libtdepim/about/kde_infopage.css" ) );
01302   if ( kapp->reverseLayout() )
01303     content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libtdepim/about/kde_infopage_rtl.css" ) );
01304   else
01305     content = content.arg( "" );
01306 
01307   mViewer->begin(KURL( location ));
01308 
01309   TQString fontSize = TQString::number( pointsToPixel( mCSSHelper->bodyFont().pointSize() ) );
01310   TQString appTitle = i18n("KMail");
01311   TQString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
01312   TQString quickDescription = i18n("The email client for the Trinity Desktop Environment.");
01313   mViewer->write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info));
01314   mViewer->end();
01315 }
01316 
01317 void KMReaderWin::displayBusyPage()
01318 {
01319   TQString info =
01320     i18n( "<h2 style='margin-top: 0px;'>Retrieving Folder Contents</h2><p>Please wait . . .</p>&nbsp;" );
01321 
01322   displaySplashPage( info );
01323 }
01324 
01325 void KMReaderWin::displayOfflinePage()
01326 {
01327   TQString info =
01328     i18n( "<h2 style='margin-top: 0px;'>Offline</h2><p>KMail is currently in offline mode. "
01329         "Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p>&nbsp;" );
01330 
01331   displaySplashPage( info );
01332 }
01333 
01334 
01335 //-----------------------------------------------------------------------------
01336 void KMReaderWin::displayAboutPage()
01337 {
01338   TQString info =
01339     i18n("%1: KMail version; %2: help:// URL; %3: homepage URL; "
01340      "%4: prior KMail version; %5: prior TDE version; "
01341      "%6: generated list of new features; "
01342      "%7: First-time user text (only shown on first start); "
01343          "%8: generated list of important changes; "
01344      "--- end of comment ---",
01345      "<h2 style='margin-top: 0px;'>Welcome to KMail %1</h2><p>KMail is the email client for the Trinity "
01346      "Desktop Environment. It is designed to be fully compatible with "
01347      "Internet mailing standards including MIME, SMTP, POP3 and IMAP."
01348      "</p>\n"
01349      "<ul><li>KMail has many powerful features which are described in the "
01350      "<a href=\"%2\">documentation</a></li>\n"
01351      "<li>The <a href=\"%3\">KMail (TDE) homepage</A> offers information about "
01352      "new versions of KMail</li></ul>\n"
01353          "%8\n"                                                         // important changes
01354      "<p>Some of the new features in this release of KMail include "
01355      "(compared to KMail %4, which is part of TDE %5):</p>\n"
01356      "<ul>\n%6</ul>\n"
01357      "%7\n"
01358      "<p>We hope that you will enjoy KMail.</p>\n"
01359      "<p>Thank you,</p>\n"
01360          "<p style='margin-bottom: 0px'>&nbsp; &nbsp; The KMail Team</p>")
01361     .arg(KMAIL_VERSION)                                                 // KMail version
01362     .arg("help:/kmail/index.html")                                      // KMail help:// URL
01363     .arg("http://www.trinitydesktop.org")                               // homepage URL
01364     .arg("1.8").arg("3.4");                                             // prior KMail and TDE version
01365 
01366   TQString featureItems;
01367   for ( int i = 0 ; i < numKMailNewFeatures ; i++ )
01368     featureItems += i18n("<li>%1</li>\n").arg( i18n( kmailNewFeatures[i] ) );
01369 
01370   info = info.arg( featureItems );
01371 
01372   if( kmkernel->firstStart() ) {
01373     info = info.arg( i18n("<p>Please take a moment to fill in the KMail "
01374               "configuration panel at Settings-&gt;Configure "
01375               "KMail.\n"
01376               "You need to create at least a default identity and "
01377               "an incoming as well as outgoing mail account."
01378               "</p>\n") );
01379   } else {
01380     info = info.arg( TQString() );
01381   }
01382 
01383   if ( ( numKMailChanges > 1 ) || ( numKMailChanges == 1 && strlen(kmailChanges[0]) > 0 ) ) {
01384     TQString changesText =
01385       i18n("<p><span style='font-size:125%; font-weight:bold;'>"
01386            "Important changes</span> (compared to KMail %1):</p>\n")
01387       .arg("1.8");
01388     changesText += "<ul>\n";
01389     for ( int i = 0 ; i < numKMailChanges ; i++ )
01390       changesText += i18n("<li>%1</li>\n").arg( i18n( kmailChanges[i] ) );
01391     changesText += "</ul>\n";
01392     info = info.arg( changesText );
01393   }
01394   else
01395     info = info.arg(""); // remove the %8
01396 
01397   displaySplashPage( info );
01398 }
01399 
01400 void KMReaderWin::enableMsgDisplay() {
01401   mMsgDisplay = true;
01402   adjustLayout();
01403 }
01404 
01405 
01406 //-----------------------------------------------------------------------------
01407 
01408 void KMReaderWin::updateReaderWin()
01409 {
01410   if (!mMsgDisplay) return;
01411 
01412   mViewer->setOnlyLocalReferences(!htmlLoadExternal());
01413 
01414   htmlWriter()->reset();
01415 
01416   KMFolder* folder = 0;
01417   if (message(&folder))
01418   {
01419     if ( mShowColorbar )
01420       mColorBar->show();
01421     else
01422       mColorBar->hide();
01423     displayMessage();
01424   }
01425   else
01426   {
01427     mColorBar->hide();
01428     mMimePartTree->hide();
01429     mMimePartTree->clear();
01430     htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01431     htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) + "</body></html>" );
01432     htmlWriter()->end();
01433   }
01434 
01435   if (mSavedRelativePosition)
01436   {
01437     TQScrollView * scrollview = static_cast<TQScrollView *>(mViewer->widget());
01438     scrollview->setContentsPos( 0,
01439       tqRound( scrollview->contentsHeight() * mSavedRelativePosition ) );
01440     mSavedRelativePosition = 0;
01441   }
01442 }
01443 
01444 //-----------------------------------------------------------------------------
01445 int KMReaderWin::pointsToPixel(int pointSize) const
01446 {
01447   const TQPaintDeviceMetrics pdm(mViewer->view());
01448 
01449   return (pointSize * pdm.logicalDpiY() + 36) / 72;
01450 }
01451 
01452 //-----------------------------------------------------------------------------
01453 void KMReaderWin::showHideMimeTree( bool isPlainTextTopLevel ) {
01454   if ( mMimeTreeModeOverride == 2 ||
01455       ( mMimeTreeModeOverride != 0 && (mMimeTreeMode == 2 ||
01456       ( mMimeTreeMode == 1 && !isPlainTextTopLevel ) ) ) ) {
01457     mMimePartTree->show();
01458   }
01459   else {
01460     // don't rely on TQSplitter maintaining sizes for hidden widgets:
01461     TDEConfigGroup reader( KMKernel::config(), "Reader" );
01462     saveSplitterSizes( reader );
01463     mMimePartTree->hide();
01464   }
01465   // mToggleMimePartTreeAction is null in case the reader win was created without an actionCollection
01466   if ( mToggleMimePartTreeAction && mToggleMimePartTreeAction->isChecked() != mMimePartTree->isVisible() ) {
01467     mToggleMimePartTreeAction->setChecked( mMimePartTree->isVisible() );
01468   }
01469 }
01470 
01471 void KMReaderWin::displayMessage() {
01472   KMMessage * msg = message();
01473 
01474   mMimePartTree->clear();
01475   mMimeTreeModeOverride = -1; // clear any previous manual overiding
01476   showHideMimeTree( !msg || // treat no message as "text/plain"
01477             ( msg->type() == DwMime::kTypeText
01478               && msg->subtype() == DwMime::kSubtypePlain ) );
01479 
01480   if ( !msg )
01481     return;
01482 
01483   msg->setOverrideCodec( overrideCodec() );
01484 
01485   htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
01486   htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
01487 
01488   if (!parent())
01489     setCaption(msg->subject());
01490 
01491   removeTempFiles();
01492 
01493   mColorBar->setNeutralMode();
01494 
01495   parseMsg(msg);
01496 
01497   if( mColorBar->isNeutral() )
01498     mColorBar->setNormalMode();
01499 
01500   htmlWriter()->queue("</body></html>");
01501   htmlWriter()->flush();
01502 
01503   TQTimer::singleShot( 1, TQT_TQOBJECT(this), TQT_SLOT(injectAttachments()) );
01504 }
01505 
01506 static bool message_was_saved_decrypted_before( const KMMessage * msg ) {
01507   if ( !msg )
01508     return false;
01509   //kdDebug(5006) << "msgId = " << msg->msgId() << endl;
01510   return msg->msgId().stripWhiteSpace().startsWith( "<DecryptedMsg." );
01511 }
01512 
01513 //-----------------------------------------------------------------------------
01514 void KMReaderWin::parseMsg(KMMessage* aMsg)
01515 {
01516   KMMessagePart msgPart;
01517   TQCString subtype, contDisp;
01518   TQByteArray str;
01519 
01520   assert(aMsg!=0);
01521 
01522   aMsg->setIsBeingParsed( true );
01523 
01524   if ( mRootNode && !mRootNode->processed() )
01525   {
01526     kdWarning() << "The root node is not yet processed! Danger!\n";
01527     return;
01528   } else
01529     delete mRootNode;
01530   mRootNode = partNode::fromMessage( aMsg, this );
01531   const TQCString mainCntTypeStr = mRootNode->typeString() + '/' + mRootNode->subTypeString();
01532 
01533   TQString cntDesc = aMsg->subject();
01534   if( cntDesc.isEmpty() )
01535     cntDesc = i18n("( body part )");
01536   TDEIO::filesize_t cntSize = aMsg->msgSize();
01537   TQString cntEnc;
01538   if( aMsg->contentTransferEncodingStr().isEmpty() )
01539     cntEnc = "7bit";
01540   else
01541     cntEnc = aMsg->contentTransferEncodingStr();
01542 
01543   // fill the MIME part tree viewer
01544   mRootNode->fillMimePartTree( 0,
01545                    mMimePartTree,
01546                    cntDesc,
01547                    mainCntTypeStr,
01548                    cntEnc,
01549                    cntSize );
01550 
01551   partNode* vCardNode = mRootNode->findType( DwMime::kTypeText, DwMime::kSubtypeXVCard );
01552   bool hasVCard = false;
01553   if( vCardNode ) {
01554     // ### FIXME: We should only do this if the vCard belongs to the sender,
01555     // ### i.e. if the sender's email address is contained in the vCard.
01556     TDEABC::VCardConverter t;
01557 #if defined(KABC_VCARD_ENCODING_FIX)
01558     const TQByteArray vcard = vCardNode->msgPart().bodyDecodedBinary();
01559     if ( !t.parseVCardsRaw( vcard.data() ).empty() ) {
01560 #else
01561     const TQString vcard = vCardNode->msgPart().bodyToUnicode( overrideCodec() );
01562     if ( !t.parseVCards( vcard ).empty() ) {
01563 #endif
01564       hasVCard = true;
01565       writeMessagePartToTempFile( &vCardNode->msgPart(), vCardNode->nodeId() );
01566     }
01567   }
01568 
01569   if ( !mRootNode || !mRootNode->isToltecMessage() || mShowRawToltecMail ) {
01570     htmlWriter()->queue( writeMsgHeader(aMsg, hasVCard ? vCardNode : 0, true ) );
01571   }
01572 
01573   // show message content
01574   ObjectTreeParser otp( this );
01575   otp.setAllowAsync( true );
01576   otp.setShowRawToltecMail( mShowRawToltecMail );
01577   otp.parseObjectTree( mRootNode );
01578 
01579   // store encrypted/signed status information in the KMMessage
01580   //  - this can only be done *after* calling parseObjectTree()
01581   KMMsgEncryptionState encryptionState = mRootNode->overallEncryptionState();
01582   KMMsgSignatureState  signatureState  = mRootNode->overallSignatureState();
01583   // Don't crash when switching message while GPG passphrase entry dialog is shown #53185
01584   if (aMsg != message()) {
01585     displayMessage();
01586     return;
01587   }
01588   aMsg->setEncryptionState( encryptionState );
01589   // Don't reset the signature state to "not signed" (e.g. if one canceled the
01590   // decryption of a signed messages which has already been decrypted before).
01591   if ( signatureState != KMMsgNotSigned ||
01592        aMsg->signatureState() == KMMsgSignatureStateUnknown ) {
01593     aMsg->setSignatureState( signatureState );
01594   }
01595 
01596   bool emitReplaceMsgByUnencryptedVersion = false;
01597   const TDEConfigGroup reader( KMKernel::config(), "Reader" );
01598   if ( reader.readBoolEntry( "store-displayed-messages-unencrypted", false ) ) {
01599 
01600   // Hack to make sure the S/MIME CryptPlugs follows the strict requirement
01601   // of german government:
01602   // --> All received encrypted messages *must* be stored in unencrypted form
01603   //     after they have been decrypted once the user has read them.
01604   //     ( "Aufhebung der Verschluesselung nach dem Lesen" )
01605   //
01606   // note: Since there is no configuration option for this, we do that for
01607   //       all kinds of encryption now - *not* just for S/MIME.
01608   //       This could be changed in the objectTreeToDecryptedMsg() function
01609   //       by deciding when (or when not, resp.) to set the 'dataNode' to
01610   //       something different than 'curNode'.
01611 
01612 
01613 kdDebug(5006) << "\n\n\nKMReaderWin::parseMsg()  -  special post-encryption handling:\n1." << endl;
01614 kdDebug(5006) << "(aMsg == msg) = "                               << (aMsg == message()) << endl;
01615 kdDebug(5006) << "aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() = " << (aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder()) << endl;
01616 kdDebug(5006) << "message_was_saved_decrypted_before( aMsg ) = " << message_was_saved_decrypted_before( aMsg ) << endl;
01617 kdDebug(5006) << "this->decryptMessage() = " << decryptMessage() << endl;
01618 kdDebug(5006) << "otp.hasPendingAsyncJobs() = " << otp.hasPendingAsyncJobs() << endl;
01619 kdDebug(5006) << "   (KMMsgFullyEncrypted == encryptionState) = "     << (KMMsgFullyEncrypted == encryptionState) << endl;
01620 kdDebug(5006) << "|| (KMMsgPartiallyEncrypted == encryptionState) = " << (KMMsgPartiallyEncrypted == encryptionState) << endl;
01621          // only proceed if we were called the normal way - not by
01622          // double click on the message (==not running in a separate window)
01623   if(    (aMsg == message())
01624          // don't remove encryption in the outbox folder :)
01625       && ( aMsg->parent() && aMsg->parent() != kmkernel->outboxFolder() )
01626          // only proceed if this message was not saved encryptedly before
01627       && !message_was_saved_decrypted_before( aMsg )
01628          // only proceed if the message has actually been decrypted
01629       && decryptMessage()
01630          // only proceed if no pending async jobs are running:
01631       && !otp.hasPendingAsyncJobs()
01632          // only proceed if this message is (at least partially) encrypted
01633       && (    (KMMsgFullyEncrypted == encryptionState)
01634            || (KMMsgPartiallyEncrypted == encryptionState) ) ) {
01635 
01636 kdDebug(5006) << "KMReaderWin  -  calling objectTreeToDecryptedMsg()" << endl;
01637 
01638     NewByteArray decryptedData;
01639     // note: The following call may change the message's headers.
01640     objectTreeToDecryptedMsg( mRootNode, decryptedData, *aMsg );
01641     // add a \0 to the data
01642     decryptedData.appendNULL();
01643     TQCString resultString( decryptedData.data() );
01644 kdDebug(5006) << "KMReaderWin  -  resulting data:" << resultString << endl;
01645 
01646     if( !resultString.isEmpty() ) {
01647 kdDebug(5006) << "KMReaderWin  -  composing unencrypted message" << endl;
01648       // try this:
01649       aMsg->setBody( resultString );
01650       KMMessage* unencryptedMessage = new KMMessage( *aMsg );
01651       unencryptedMessage->setParent( 0 );
01652       // because this did not work:
01653       /*
01654       DwMessage dwMsg( aMsg->asDwString() );
01655       dwMsg.Body() = DwBody( DwString( resultString.data() ) );
01656       dwMsg.Body().Parse();
01657       KMMessage* unencryptedMessage = new KMMessage( &dwMsg );
01658       */
01659       //kdDebug(5006) << "KMReaderWin  -  resulting message:" << unencryptedMessage->asString() << endl;
01660       kdDebug(5006) << "KMReaderWin  -  attach unencrypted message to aMsg" << endl;
01661       aMsg->setUnencryptedMsg( unencryptedMessage );
01662       emitReplaceMsgByUnencryptedVersion = true;
01663     }
01664   }
01665   }
01666 
01667   // save current main Content-Type before deleting mRootNode
01668   const int rootNodeCntType = mRootNode ? mRootNode->type() : DwMime::kTypeText;
01669   const int rootNodeCntSubtype = mRootNode ? mRootNode->subType() : DwMime::kSubtypePlain;
01670 
01671   // store message id to avoid endless recursions
01672   setIdOfLastViewedMessage( aMsg->msgId() );
01673 
01674   if( emitReplaceMsgByUnencryptedVersion ) {
01675     kdDebug(5006) << "KMReaderWin  -  invoce saving in decrypted form:" << endl;
01676     emit replaceMsgByUnencryptedVersion();
01677   } else {
01678     kdDebug(5006) << "KMReaderWin  -  finished parsing and displaying of message." << endl;
01679     showHideMimeTree( rootNodeCntType == DwMime::kTypeText &&
01680               rootNodeCntSubtype == DwMime::kSubtypePlain );
01681   }
01682 
01683   aMsg->setIsBeingParsed( false );
01684 }
01685 
01686 
01687 //-----------------------------------------------------------------------------
01688 void KMReaderWin::updateHeader()
01689 {
01690   /*
01691    * TODO: mess around with TDEHTML DOM some more and figure out how to
01692    * replace the entire header div w/out flickering to hell and back
01693    *
01694    * DOM::NodeList divs(mViewer->document().documentElement().getElementsByTagName("div"));
01695    * static_cast<DOM::HTMLDivElement>(divs.item(0)).setInnerHTML(writeMsgHeader());
01696    */
01697 
01698    KMMessage* currentMessage = message();
01699 
01700   if (currentMessage &&
01701       mHeaderStyle == HeaderStyle::fancy() &&
01702       currentMessage->parent())
01703   {
01704     int i;
01705     int divNumber = -1;
01706     DOM::NodeList divs(mViewer->document().documentElement().getElementsByTagName("div"));
01707     DOM::NodeList headerDivs(static_cast<DOM::HTMLDivElement>(divs.item(0)).getElementsByTagName("div"));
01708     for (i=0; i<((int)headerDivs.length()); i++) {
01709         if (static_cast<DOM::HTMLDivElement>(headerDivs.item(i)).id().string() == "sendersCurrentTime") {
01710             divNumber = i;
01711             break;
01712         }
01713     }
01714 
01715     if (divNumber >= 0) {
01716         DOM::HTMLDivElement elem = static_cast<DOM::HTMLDivElement>(headerDivs.item(i));
01717 
01718         // HACK
01719         // Get updated time information
01720         TQString latestHeader = headerStyle()->format( currentMessage, headerStrategy(), "", mPrinting, false );
01721         int startPos = latestHeader.find("<div id=\"sendersCurrentTime\" style=\"");
01722         if (startPos >= 0) {
01723             latestHeader = latestHeader.mid(startPos);
01724             int endPos = latestHeader.find("</div>");
01725             if (endPos >= 0) {
01726                 endPos = endPos + 6;
01727                 latestHeader.truncate(endPos);
01728 
01729                 TQString divText = latestHeader;
01730                 TQString divStyle = latestHeader;
01731 
01732                 divText = divText.mid(divText.find(">")+1);
01733                 divText.truncate(divText.find("</div>"));
01734 
01735                 divStyle = divStyle.mid(TQString("<div id=\"sendersCurrentTime\" style=\"").length());
01736                 divStyle.truncate(divStyle.find("\""));
01737 
01738                 elem.setInnerHTML(divText);
01739                 elem.setAttribute("style", divStyle);
01740                 elem.applyChanges();
01741             }
01742         }
01743     }
01744   }
01745 }
01746 
01747 //-----------------------------------------------------------------------------
01748 TQString KMReaderWin::writeMsgHeader( KMMessage* aMsg, partNode *vCardNode, bool topLevel )
01749 {
01750   kdFatal( !headerStyle(), 5006 )
01751     << "trying to writeMsgHeader() without a header style set!" << endl;
01752   kdFatal( !headerStrategy(), 5006 )
01753     << "trying to writeMsgHeader() without a header strategy set!" << endl;
01754   TQString href;
01755   if ( vCardNode )
01756     href = vCardNode->asHREF( "body" );
01757 
01758  return headerStyle()->format( aMsg, headerStrategy(), href, mPrinting, topLevel );
01759 }
01760 
01761 
01762 
01763 //-----------------------------------------------------------------------------
01764 TQString KMReaderWin::writeMessagePartToTempFile( KMMessagePart* aMsgPart,
01765                                                  int aPartNum )
01766 {
01767   TQString fileName = aMsgPart->fileName();
01768   if( fileName.isEmpty() )
01769     fileName = aMsgPart->name();
01770 
01771   //--- Sven's save attachments to /tmp start ---
01772   TQString fname = createTempDir( TQString::number( aPartNum ) );
01773   if ( fname.isEmpty() )
01774     return TQString();
01775 
01776   // strip off a leading path
01777   int slashPos = fileName.findRev( '/' );
01778   if( -1 != slashPos )
01779     fileName = fileName.mid( slashPos + 1 );
01780   if( fileName.isEmpty() )
01781     fileName = "unnamed";
01782   fname += "/" + fileName;
01783 
01784   TQByteArray data = aMsgPart->bodyDecodedBinary();
01785   size_t size = data.size();
01786   if ( aMsgPart->type() == DwMime::kTypeText && size) {
01787     // convert CRLF to LF before writing text attachments to disk
01788     size = KMail::Util::crlf2lf( data.data(), size );
01789   }
01790   if( !KPIM::kBytesToFile( data.data(), size, fname, false, false, false ) )
01791     return TQString();
01792 
01793   mTempFiles.append( fname );
01794   // make file read-only so that nobody gets the impression that he might
01795   // edit attached files (cf. bug #52813)
01796   ::chmod( TQFile::encodeName( fname ), S_IRUSR );
01797 
01798   return fname;
01799 }
01800 
01801 TQString KMReaderWin::createTempDir( const TQString &param )
01802 {
01803   KTempFile *tempFile = new KTempFile( TQString(), "." + param );
01804   tempFile->setAutoDelete( true );
01805   TQString fname = tempFile->name();
01806   delete tempFile;
01807 
01808   if( ::access( TQFile::encodeName( fname ), W_OK ) != 0 )
01809     // Not there or not writable
01810     if( ::mkdir( TQFile::encodeName( fname ), 0 ) != 0
01811         || ::chmod( TQFile::encodeName( fname ), S_IRWXU ) != 0 )
01812       return TQString(); //failed create
01813 
01814   assert( !fname.isNull() );
01815 
01816   mTempDirs.append( fname );
01817   return fname;
01818 }
01819 
01820 //-----------------------------------------------------------------------------
01821 void KMReaderWin::showVCard( KMMessagePart *msgPart )
01822 {
01823 #if defined(KABC_VCARD_ENCODING_FIX)
01824   const TQByteArray vCard = msgPart->bodyDecodedBinary();
01825 #else
01826   const TQString vCard = msgPart->bodyToUnicode( overrideCodec() );
01827 #endif
01828   VCardViewer *vcv = new VCardViewer( this, vCard, "vCardDialog" );
01829   vcv->show();
01830 }
01831 
01832 //-----------------------------------------------------------------------------
01833 void KMReaderWin::printMsg()
01834 {
01835   if (!message()) return;
01836   mViewer->view()->print();
01837 }
01838 
01839 
01840 //-----------------------------------------------------------------------------
01841 int KMReaderWin::msgPartFromUrl(const KURL &aUrl)
01842 {
01843   if (aUrl.isEmpty()) return -1;
01844   if (!aUrl.isLocalFile()) return -1;
01845 
01846   TQString path = aUrl.path();
01847   uint right = path.findRev('/');
01848   uint left = path.findRev('.', right);
01849 
01850   bool ok;
01851   int res = path.mid(left + 1, right - left - 1).toInt(&ok);
01852   return (ok) ? res : -1;
01853 }
01854 
01855 
01856 //-----------------------------------------------------------------------------
01857 void KMReaderWin::resizeEvent(TQResizeEvent *)
01858 {
01859   if( !mResizeTimer.isActive() )
01860   {
01861     //
01862     // Combine all resize operations that are requested as long a
01863     // the timer runs.
01864     //
01865     mResizeTimer.start( 100, true );
01866   }
01867 }
01868 
01869 
01870 //-----------------------------------------------------------------------------
01871 void KMReaderWin::slotDelayedResize()
01872 {
01873   mSplitter->setGeometry(0, 0, width(), height());
01874 }
01875 
01876 
01877 //-----------------------------------------------------------------------------
01878 void KMReaderWin::slotTouchMessage()
01879 {
01880   if ( !message() )
01881     return;
01882 
01883   if ( !message()->isNew() && !message()->isUnread() )
01884     return;
01885 
01886   SerNumList serNums;
01887   serNums.append( message()->getMsgSerNum() );
01888   KMCommand *command = new KMSeStatusCommand( KMMsgStatusRead, serNums );
01889   command->start();
01890 
01891   // should we send an MDN?
01892   if ( mNoMDNsWhenEncrypted &&
01893        message()->encryptionState() != KMMsgNotEncrypted &&
01894        message()->encryptionState() != KMMsgEncryptionStateUnknown )
01895     return;
01896 
01897   KMFolder *folder = message()->parent();
01898   if (folder &&
01899      (folder->isOutbox() || folder->isSent() || folder->isTrash() ||
01900       folder->isDrafts() || folder->isTemplates() ) )
01901     return;
01902 
01903   if ( KMMessage * receipt = message()->createMDN( MDN::ManualAction,
01904                            MDN::Displayed,
01905                            true /* allow GUI */ ) )
01906     if ( !kmkernel->msgSender()->send( receipt ) ) // send or queue
01907       KMessageBox::error( this, i18n("Could not send MDN.") );
01908 }
01909 
01910 
01911 //-----------------------------------------------------------------------------
01912 void KMReaderWin::closeEvent(TQCloseEvent *e)
01913 {
01914   TQWidget::closeEvent(e);
01915   writeConfig();
01916 }
01917 
01918 
01919 bool foundSMIMEData( const TQString aUrl,
01920                      TQString& displayName,
01921                      TQString& libName,
01922                      TQString& keyId )
01923 {
01924   static TQString showCertMan("showCertificate#");
01925   displayName = "";
01926   libName = "";
01927   keyId = "";
01928   int i1 = aUrl.find( showCertMan );
01929   if( -1 < i1 ) {
01930     i1 += showCertMan.length();
01931     int i2 = aUrl.find(" ### ", i1);
01932     if( i1 < i2 )
01933     {
01934       displayName = aUrl.mid( i1, i2-i1 );
01935       i1 = i2+5;
01936       i2 = aUrl.find(" ### ", i1);
01937       if( i1 < i2 )
01938       {
01939         libName = aUrl.mid( i1, i2-i1 );
01940         i2 += 5;
01941 
01942         keyId = aUrl.mid( i2 );
01943         /*
01944         int len = aUrl.length();
01945         if( len > i2+1 ) {
01946           keyId = aUrl.mid( i2, 2 );
01947           i2 += 2;
01948           while( len > i2+1 ) {
01949             keyId += ':';
01950             keyId += aUrl.mid( i2, 2 );
01951             i2 += 2;
01952           }
01953         }
01954         */
01955       }
01956     }
01957   }
01958   return !keyId.isEmpty();
01959 }
01960 
01961 
01962 //-----------------------------------------------------------------------------
01963 void KMReaderWin::slotUrlOn(const TQString &aUrl)
01964 {
01965   const KURL url(aUrl);
01966 
01967   if ( url.protocol() == "kmail" || url.protocol() == "x-kmail" || url.protocol() == "attachment"
01968        || (url.protocol().isEmpty() && url.path().isEmpty()) ) {
01969     mViewer->setDNDEnabled( false );
01970   } else {
01971     mViewer->setDNDEnabled( true );
01972   }
01973 
01974   if ( aUrl.stripWhiteSpace().isEmpty() ) {
01975     KPIM::BroadcastStatus::instance()->reset();
01976     mHoveredUrl = KURL();
01977     mLastClickImagePath = TQString();
01978     return;
01979   }
01980 
01981   mHoveredUrl = url;
01982 
01983   const TQString msg = URLHandlerManager::instance()->statusBarMessage( url, this );
01984 
01985   kdWarning( msg.isEmpty(), 5006 ) << "KMReaderWin::slotUrlOn(): Unhandled URL hover!" << endl;
01986   KPIM::BroadcastStatus::instance()->setTransienStatusMsg( msg );
01987 }
01988 
01989 
01990 //-----------------------------------------------------------------------------
01991 void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &)
01992 {
01993   mClickedUrl = aUrl;
01994 
01995   if ( URLHandlerManager::instance()->handleClick( aUrl, this ) )
01996     return;
01997 
01998   kdWarning( 5006 ) << "KMReaderWin::slotOpenUrl(): Unhandled URL click!" << endl;
01999   emit urlClicked( aUrl, Qt::LeftButton );
02000 }
02001 
02002 //-----------------------------------------------------------------------------
02003 void KMReaderWin::slotUrlPopup(const TQString &aUrl, const TQPoint& aPos)
02004 {
02005   const KURL url( aUrl );
02006   mClickedUrl = url;
02007 
02008   if ( url.protocol() == "mailto" ) {
02009     mCopyURLAction->setText( i18n( "Copy Email Address" ) );
02010   } else {
02011     mCopyURLAction->setText( i18n( "Copy Link Address" ) );
02012   }
02013 
02014   if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) )
02015     return;
02016 
02017   if ( message() ) {
02018     kdWarning( 5006 ) << "KMReaderWin::slotUrlPopup(): Unhandled URL right-click!" << endl;
02019     emitPopupMenu( url, aPos );
02020   }
02021 }
02022 
02023 // Checks if the given node has a parent node that is a DIV which has an ID attribute
02024 // with the value specified here
02025 static bool hasParentDivWithId( const DOM::Node &start, const TQString &id )
02026 {
02027   if ( start.isNull() )
02028     return false;
02029 
02030   if ( start.nodeName().string() == "div" ) {
02031     for ( unsigned int i = 0; i < start.attributes().length(); i++ ) {
02032       if ( start.attributes().item( i ).nodeName().string() == "id" &&
02033            start.attributes().item( i ).nodeValue().string() == id )
02034         return true;
02035     }
02036   }
02037 
02038   if ( !start.parentNode().isNull() )
02039     return hasParentDivWithId( start.parentNode(), id );
02040   else return false;
02041 }
02042 
02043 //-----------------------------------------------------------------------------
02044 void KMReaderWin::showAttachmentPopup( int id, const TQString & name, const TQPoint & p )
02045 {
02046   mAtmCurrent = id;
02047   mAtmCurrentName = name;
02048   TDEPopupMenu *menu = new TDEPopupMenu();
02049   menu->insertItem(SmallIcon("document-open"),i18n("to open", "Open"), 1);
02050   menu->insertItem(i18n("Open With..."), 2);
02051   menu->insertItem(i18n("to view something", "View"), 3);
02052   menu->insertItem(SmallIcon("document-save-as"),i18n("Save As..."), 4);
02053   menu->insertItem(SmallIcon("edit-copy"), i18n("Copy"), 9 );
02054   const bool canChange = message()->parent() ? !message()->parent()->isReadOnly() : false;
02055   if ( GlobalSettings::self()->allowAttachmentEditing() && canChange )
02056     menu->insertItem(SmallIcon("edit"), i18n("Edit Attachment"), 8 );
02057   if ( GlobalSettings::self()->allowAttachmentDeletion() && canChange )
02058     menu->insertItem(SmallIcon("edit-delete"), i18n("Delete Attachment"), 7 );
02059   if ( name.endsWith( ".xia", false ) &&
02060        Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ) )
02061     menu->insertItem( i18n( "Decrypt With Chiasmus..." ), 6 );
02062   menu->insertItem(i18n("Properties"), 5);
02063 
02064   const bool attachmentInHeader = hasParentDivWithId( mViewer->nodeUnderMouse(), "attachmentInjectionPoint" );
02065   const bool hasScrollbar = mViewer->view()->verticalScrollBar()->isVisible();
02066   if ( attachmentInHeader && hasScrollbar ) {
02067     menu->insertItem( i18n("Scroll To"), 10 );
02068   }
02069 
02070   connect(menu, TQT_SIGNAL(activated(int)), TQT_TQOBJECT(this), TQT_SLOT(slotHandleAttachment(int)));
02071   menu->exec( p ,0 );
02072   delete menu;
02073 }
02074 
02075 //-----------------------------------------------------------------------------
02076 void KMReaderWin::setStyleDependantFrameWidth()
02077 {
02078   if ( !mBox )
02079     return;
02080   // set the width of the frame to a reasonable value for the current GUI style
02081   int frameWidth;
02082   if( style().isA("KeramikStyle") )
02083     frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) - 1;
02084   else
02085     frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth );
02086   if ( frameWidth < 0 )
02087     frameWidth = 0;
02088   if ( frameWidth != mBox->lineWidth() )
02089     mBox->setLineWidth( frameWidth );
02090 }
02091 
02092 //-----------------------------------------------------------------------------
02093 void KMReaderWin::styleChange( TQStyle& oldStyle )
02094 {
02095   setStyleDependantFrameWidth();
02096   TQWidget::styleChange( oldStyle );
02097 }
02098 
02099 //-----------------------------------------------------------------------------
02100 void KMReaderWin::slotHandleAttachment( int choice )
02101 {
02102   mAtmUpdate = true;
02103   partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0;
02104   if ( mAtmCurrentName.isEmpty() && node )
02105     mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
02106   if ( choice < 7 ) {
02107   KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand(
02108       node, message(), mAtmCurrent, mAtmCurrentName,
02109       KMHandleAttachmentCommand::AttachmentAction( choice ), 0, this );
02110   connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
02111       TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
02112   command->start();
02113   } else if ( choice == 7 ) {
02114     slotDeleteAttachment( node );
02115   } else if ( choice == 8 ) {
02116     slotEditAttachment( node );
02117   } else if ( choice == 9 ) {
02118     if ( !node ) return;
02119     KURL::List urls;
02120     KURL url = tempFileUrlFromPartNode( node );
02121     if (!url.isValid() ) return;
02122     urls.append( url );
02123     KURLDrag* drag = new KURLDrag( urls, this );
02124     TQApplication::clipboard()->setData( drag, TQClipboard::Clipboard );
02125   } else if ( choice == 10 ) { // Scroll To
02126     scrollToAttachment( node );
02127   }
02128 }
02129 
02130 //-----------------------------------------------------------------------------
02131 void KMReaderWin::slotFind()
02132 {
02133   mViewer->findText();
02134 }
02135 
02136 //-----------------------------------------------------------------------------
02137 void KMReaderWin::slotFindNext()
02138 {
02139   mViewer->findTextNext();
02140 }
02141 
02142 //-----------------------------------------------------------------------------
02143 void KMReaderWin::slotToggleFixedFont()
02144 {
02145   mUseFixedFont = !mUseFixedFont;
02146   saveRelativePosition();
02147   update(true);
02148 }
02149 
02150 
02151 //-----------------------------------------------------------------------------
02152 void KMReaderWin::slotToggleMimePartTree()
02153 {
02154   if ( mToggleMimePartTreeAction->isChecked() ) {
02155     mMimeTreeModeOverride = 2;  // always
02156   } else {
02157     mMimeTreeModeOverride = 0;  // never
02158   }
02159   showHideMimeTree(false);
02160 }
02161 
02162 //-----------------------------------------------------------------------------
02163 void KMReaderWin::slotCopySelectedText()
02164 {
02165   kapp->clipboard()->setText( mViewer->selectedText() );
02166 }
02167 
02168 
02169 //-----------------------------------------------------------------------------
02170 void KMReaderWin::atmViewMsg( KMMessagePart* aMsgPart, int nodeId )
02171 {
02172   assert(aMsgPart!=0);
02173   KMMessage* msg = new KMMessage;
02174   msg->fromString(aMsgPart->bodyDecoded());
02175   assert(msg != 0);
02176   msg->setMsgSerNum( 0 ); // because lookups will fail
02177   // some information that is needed for imap messages with LOD
02178   msg->setParent( message()->parent() );
02179   msg->setUID(message()->UID());
02180   msg->setReadyToShow(true);
02181   KMReaderMainWin *win = new KMReaderMainWin();
02182   win->showMsg( overrideEncoding(), msg, message()->getMsgSerNum(), nodeId );
02183   win->show();
02184 }
02185 
02186 
02187 void KMReaderWin::setMsgPart( partNode * node ) {
02188   htmlWriter()->reset();
02189   mColorBar->hide();
02190   htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
02191   htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
02192   // end ###
02193   if ( node ) {
02194     ObjectTreeParser otp( this, 0, true );
02195     otp.parseObjectTree( node );
02196   }
02197   // ### this, too
02198   htmlWriter()->queue( "</body></html>" );
02199   htmlWriter()->flush();
02200 }
02201 
02202 //-----------------------------------------------------------------------------
02203 void KMReaderWin::setMsgPart( KMMessagePart* aMsgPart, bool aHTML,
02204                   const TQString& aFileName, const TQString& pname )
02205 {
02206   KCursorSaver busy(KBusyPtr::busy());
02207   if (kasciistricmp(aMsgPart->typeStr(), "message")==0) {
02208       // if called from compose win
02209       KMMessage* msg = new KMMessage;
02210       assert(aMsgPart!=0);
02211       msg->fromString(aMsgPart->bodyDecoded());
02212       mMainWindow->setCaption(msg->subject());
02213       setMsg(msg, true);
02214       setAutoDelete(true);
02215   } else if (kasciistricmp(aMsgPart->typeStr(), "text")==0) {
02216       if (kasciistricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) {
02217         showVCard( aMsgPart );
02218     return;
02219       }
02220       htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
02221       htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
02222 
02223       if (aHTML && (kasciistricmp(aMsgPart->subtypeStr(), "html")==0)) { // HTML
02224         // ### this is broken. It doesn't stip off the HTML header and footer!
02225         htmlWriter()->queue( aMsgPart->bodyToUnicode( overrideCodec() ) );
02226         mColorBar->setHtmlMode();
02227       } else { // plain text
02228         const TQCString str = aMsgPart->bodyDecoded();
02229         ObjectTreeParser otp( this );
02230         otp.writeBodyStr( str,
02231                           overrideCodec() ? overrideCodec() : aMsgPart->codec(),
02232                           message() ? message()->from() : TQString() );
02233       }
02234       htmlWriter()->queue("</body></html>");
02235       htmlWriter()->flush();
02236       mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
02237   } else if (kasciistricmp(aMsgPart->typeStr(), "image")==0 ||
02238              (kasciistricmp(aMsgPart->typeStr(), "application")==0 &&
02239               kasciistricmp(aMsgPart->subtypeStr(), "postscript")==0))
02240   {
02241       if (aFileName.isEmpty()) return;  // prevent crash
02242       // Open the window with a size so the image fits in (if possible):
02243       TQImageIO *iio = new TQImageIO();
02244       iio->setFileName(aFileName);
02245       if( iio->read() ) {
02246           TQImage img = iio->image();
02247           TQRect desk = TDEGlobalSettings::desktopGeometry(mMainWindow);
02248           // determine a reasonable window size
02249           int width, height;
02250           if( img.width() < 50 )
02251               width = 70;
02252           else if( img.width()+20 < desk.width() )
02253               width = img.width()+20;
02254           else
02255               width = desk.width();
02256           if( img.height() < 50 )
02257               height = 70;
02258           else if( img.height()+20 < desk.height() )
02259               height = img.height()+20;
02260           else
02261               height = desk.height();
02262           mMainWindow->resize( width, height );
02263       }
02264       // Just write the img tag to HTML:
02265       htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
02266       htmlWriter()->write( mCSSHelper->htmlHead( isFixedFont() ) );
02267       htmlWriter()->write( "<img src=\"file:" +
02268                            KURL::encode_string( aFileName ) +
02269                            "\" border=\"0\">\n"
02270                            "</body></html>\n" );
02271       htmlWriter()->end();
02272       setCaption( i18n("View Attachment: %1").arg( pname ) );
02273       show();
02274       delete iio;
02275   } else {
02276     htmlWriter()->begin( mCSSHelper->cssDefinitions( isFixedFont() ) );
02277     htmlWriter()->queue( mCSSHelper->htmlHead( isFixedFont() ) );
02278     htmlWriter()->queue( "<pre>" );
02279 
02280     TQString str = aMsgPart->bodyDecoded();
02281     // A TQString cannot handle binary data. So if it's shorter than the
02282     // attachment, we assume the attachment is binary:
02283     if( str.length() < (unsigned) aMsgPart->decodedSize() ) {
02284       str.prepend( i18n("[KMail: Attachment contains binary data. Trying to show first character.]",
02285           "[KMail: Attachment contains binary data. Trying to show first %n characters.]",
02286           str.length()) + TQChar('\n') );
02287     }
02288     htmlWriter()->queue( TQStyleSheet::escape( str ) );
02289     htmlWriter()->queue( "</pre>" );
02290     htmlWriter()->queue("</body></html>");
02291     htmlWriter()->flush();
02292     mMainWindow->setCaption(i18n("View Attachment: %1").arg(pname));
02293   }
02294   // ---Sven's view text, html and image attachments in html widget end ---
02295 }
02296 
02297 
02298 //-----------------------------------------------------------------------------
02299 void KMReaderWin::slotAtmView( int id, const TQString& name )
02300 {
02301   partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
02302   if( node ) {
02303     mAtmCurrent = id;
02304     mAtmCurrentName = name;
02305     if ( mAtmCurrentName.isEmpty() )
02306       mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
02307 
02308     KMMessagePart& msgPart = node->msgPart();
02309     TQString pname = msgPart.fileName();
02310     if (pname.isEmpty()) pname=msgPart.name();
02311     if (pname.isEmpty()) pname=msgPart.contentDescription();
02312     if (pname.isEmpty()) pname="unnamed";
02313     // image Attachment is saved already
02314     if (kasciistricmp(msgPart.typeStr(), "message")==0) {
02315       atmViewMsg( &msgPart,id );
02316     } else if ((kasciistricmp(msgPart.typeStr(), "text")==0) &&
02317            (kasciistricmp(msgPart.subtypeStr(), "x-vcard")==0)) {
02318       setMsgPart( &msgPart, htmlMail(), name, pname );
02319     } else {
02320       KMReaderMainWin *win = new KMReaderMainWin(&msgPart, htmlMail(),
02321           name, pname, overrideEncoding() );
02322       win->show();
02323     }
02324   }
02325 }
02326 
02327 //-----------------------------------------------------------------------------
02328 void KMReaderWin::openAttachment( int id, const TQString & name )
02329 {
02330   mAtmCurrentName = name;
02331   mAtmCurrent = id;
02332 
02333   TQString str, pname, cmd, fileName;
02334 
02335   partNode* node = mRootNode ? mRootNode->findId( id ) : 0;
02336   if( !node ) {
02337     kdWarning(5006) << "KMReaderWin::openAttachment - could not find node " << id << endl;
02338     return;
02339   }
02340   if ( mAtmCurrentName.isEmpty() )
02341     mAtmCurrentName = tempFileUrlFromPartNode( node ).path();
02342 
02343   KMMessagePart& msgPart = node->msgPart();
02344   if (kasciistricmp(msgPart.typeStr(), "message")==0)
02345   {
02346     atmViewMsg( &msgPart, id );
02347     return;
02348   }
02349 
02350   TQCString contentTypeStr( msgPart.typeStr() + '/' + msgPart.subtypeStr() );
02351   KPIM::kAsciiToLower( contentTypeStr.data() );
02352 
02353   if ( qstrcmp( contentTypeStr, "text/x-vcard" ) == 0 ) {
02354     showVCard( &msgPart );
02355     return;
02356   }
02357 
02358   // determine the MIME type of the attachment
02359   KMimeType::Ptr mimetype;
02360   // prefer the value of the Content-Type header
02361   mimetype = KMimeType::mimeType( TQString::fromLatin1( contentTypeStr ) );
02362   if ( mimetype->name() == "application/octet-stream" ) {
02363     // consider the filename if Content-Type is application/octet-stream
02364     mimetype = KMimeType::findByPath( name, 0, true /* no disk access */ );
02365   }
02366   if ( ( mimetype->name() == "application/octet-stream" )
02367        && msgPart.isComplete() ) {
02368     // consider the attachment's contents if neither the Content-Type header
02369     // nor the filename give us a clue
02370     mimetype = KMimeType::findByFileContent( name );
02371   }
02372 
02373   KService::Ptr offer =
02374     KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
02375 
02376   TQString open_text;
02377   TQString filenameText = msgPart.fileName();
02378   if ( filenameText.isEmpty() )
02379     filenameText = msgPart.name();
02380   if ( offer ) {
02381     open_text = i18n("&Open with '%1'").arg( offer->name() );
02382   } else {
02383     open_text = i18n("&Open With...");
02384   }
02385   const TQString text = i18n("Open attachment '%1'?\n"
02386                             "Note that opening an attachment may compromise "
02387                             "your system's security.")
02388                        .arg( filenameText );
02389   const int choice = KMessageBox::questionYesNoCancel( this, text,
02390       i18n("Open Attachment?"), KStdGuiItem::saveAs(), open_text,
02391       TQString::fromLatin1("askSave") + mimetype->name() ); // dontAskAgainName
02392 
02393   if( choice == KMessageBox::Yes ) {        // Save
02394     mAtmUpdate = true;
02395     KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node,
02396         message(), mAtmCurrent, mAtmCurrentName, KMHandleAttachmentCommand::Save,
02397         offer, this );
02398     connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
02399         TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
02400     command->start();
02401   }
02402   else if( choice == KMessageBox::No ) {    // Open
02403     KMHandleAttachmentCommand::AttachmentAction action = ( offer ?
02404         KMHandleAttachmentCommand::Open : KMHandleAttachmentCommand::OpenWith );
02405     mAtmUpdate = true;
02406     KMHandleAttachmentCommand* command = new KMHandleAttachmentCommand( node,
02407         message(), mAtmCurrent, mAtmCurrentName, action, offer, this );
02408     connect( command, TQT_SIGNAL( showAttachment( int, const TQString& ) ),
02409         TQT_TQOBJECT(this), TQT_SLOT( slotAtmView( int, const TQString& ) ) );
02410     command->start();
02411   } else {                  // Cancel
02412     kdDebug(5006) << "Canceled opening attachment" << endl;
02413   }
02414 }
02415 
02416 //-----------------------------------------------------------------------------
02417 void KMReaderWin::slotScrollUp()
02418 {
02419   static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, -10);
02420 }
02421 
02422 
02423 //-----------------------------------------------------------------------------
02424 void KMReaderWin::slotScrollDown()
02425 {
02426   static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, 10);
02427 }
02428 
02429 bool KMReaderWin::atBottom() const
02430 {
02431     const TQScrollView *view = static_cast<const TQScrollView *>(mViewer->widget());
02432     return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
02433 }
02434 
02435 //-----------------------------------------------------------------------------
02436 void KMReaderWin::slotJumpDown()
02437 {
02438     TQScrollView *view = static_cast<TQScrollView *>(mViewer->widget());
02439     int offs = (view->clipper()->height() < 30) ? view->clipper()->height() : 30;
02440     view->scrollBy( 0, view->clipper()->height() - offs );
02441 }
02442 
02443 //-----------------------------------------------------------------------------
02444 void KMReaderWin::slotScrollPrior()
02445 {
02446   static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, -(int)(height()*0.8));
02447 }
02448 
02449 
02450 //-----------------------------------------------------------------------------
02451 void KMReaderWin::slotScrollNext()
02452 {
02453   static_cast<TQScrollView *>(mViewer->widget())->scrollBy(0, (int)(height()*0.8));
02454 }
02455 
02456 //-----------------------------------------------------------------------------
02457 void KMReaderWin::slotDocumentChanged()
02458 {
02459 
02460 }
02461 
02462 
02463 //-----------------------------------------------------------------------------
02464 void KMReaderWin::slotTextSelected(bool)
02465 {
02466   TQString temp = mViewer->selectedText();
02467   kapp->clipboard()->setText(temp);
02468 }
02469 
02470 //-----------------------------------------------------------------------------
02471 void KMReaderWin::selectAll()
02472 {
02473   mViewer->selectAll();
02474 }
02475 
02476 //-----------------------------------------------------------------------------
02477 TQString KMReaderWin::copyText()
02478 {
02479   TQString temp = mViewer->selectedText();
02480   return temp;
02481 }
02482 
02483 
02484 //-----------------------------------------------------------------------------
02485 void KMReaderWin::slotDocumentDone()
02486 {
02487   // mSbVert->setValue(0);
02488 }
02489 
02490 
02491 //-----------------------------------------------------------------------------
02492 void KMReaderWin::setHtmlOverride(bool override)
02493 {
02494   mHtmlOverride = override;
02495   if (message())
02496       message()->setDecodeHTML(htmlMail());
02497 }
02498 
02499 
02500 //-----------------------------------------------------------------------------
02501 void KMReaderWin::setHtmlLoadExtOverride(bool override)
02502 {
02503   mHtmlLoadExtOverride = override;
02504   //if (message())
02505   //    message()->setDecodeHTML(htmlMail());
02506 }
02507 
02508 
02509 //-----------------------------------------------------------------------------
02510 bool KMReaderWin::htmlMail()
02511 {
02512   return ((mHtmlMail && !mHtmlOverride) || (!mHtmlMail && mHtmlOverride));
02513 }
02514 
02515 
02516 //-----------------------------------------------------------------------------
02517 bool KMReaderWin::htmlLoadExternal()
02518 {
02519   return ((mHtmlLoadExternal && !mHtmlLoadExtOverride) ||
02520           (!mHtmlLoadExternal && mHtmlLoadExtOverride));
02521 }
02522 
02523 
02524 //-----------------------------------------------------------------------------
02525 void KMReaderWin::saveRelativePosition()
02526 {
02527   const TQScrollView * scrollview = static_cast<TQScrollView *>( mViewer->widget() );
02528   mSavedRelativePosition =
02529     static_cast<float>( scrollview->contentsY() ) / scrollview->contentsHeight();
02530 }
02531 
02532 
02533 //-----------------------------------------------------------------------------
02534 void KMReaderWin::update( bool force )
02535 {
02536   KMMessage* msg = message();
02537   if ( msg )
02538     setMsg( msg, force, true /* updateOnly */ );
02539 }
02540 
02541 
02542 //-----------------------------------------------------------------------------
02543 KMMessage* KMReaderWin::message( KMFolder** aFolder ) const
02544 {
02545   KMFolder*  tmpFolder;
02546   KMFolder*& folder = aFolder ? *aFolder : tmpFolder;
02547   folder = 0;
02548   if (mMessage)
02549       return mMessage;
02550   if (mLastSerNum) {
02551     KMMessage *message = 0;
02552     int index;
02553     KMMsgDict::instance()->getLocation( mLastSerNum, &folder, &index );
02554     if (folder )
02555       message = folder->getMsg( index );
02556     if (!message)
02557       kdWarning(5006) << "Attempt to reference invalid serial number " << mLastSerNum << "\n" << endl;
02558     return message;
02559   }
02560   return 0;
02561 }
02562 
02563 
02564 
02565 //-----------------------------------------------------------------------------
02566 void KMReaderWin::slotUrlClicked()
02567 {
02568   KMMainWidget *mainWidget = dynamic_cast<KMMainWidget*>(mMainWindow);
02569   uint identity = 0;
02570   if ( message() && message()->parent() ) {
02571     identity = message()->parent()->identity();
02572   }
02573 
02574   KMCommand *command = new KMUrlClickedCommand( mClickedUrl, identity, this,
02575                         false, mainWidget );
02576   command->start();
02577 }
02578 
02579 //-----------------------------------------------------------------------------
02580 void KMReaderWin::slotMailtoCompose()
02581 {
02582   KMCommand *command = new KMMailtoComposeCommand( mClickedUrl, message() );
02583   command->start();
02584 }
02585 
02586 //-----------------------------------------------------------------------------
02587 void KMReaderWin::slotMailtoForward()
02588 {
02589   KMCommand *command = new KMMailtoForwardCommand( mMainWindow, mClickedUrl,
02590                            message() );
02591   command->start();
02592 }
02593 
02594 //-----------------------------------------------------------------------------
02595 void KMReaderWin::slotMailtoAddAddrBook()
02596 {
02597   KMCommand *command = new KMMailtoAddAddrBookCommand( mClickedUrl,
02598                                mMainWindow);
02599   command->start();
02600 }
02601 
02602 //-----------------------------------------------------------------------------
02603 void KMReaderWin::slotMailtoOpenAddrBook()
02604 {
02605   KMCommand *command = new KMMailtoOpenAddrBookCommand( mClickedUrl,
02606                             mMainWindow );
02607   command->start();
02608 }
02609 
02610 //-----------------------------------------------------------------------------
02611 void KMReaderWin::slotUrlCopy()
02612 {
02613   // we don't necessarily need a mainWidget for KMUrlCopyCommand so
02614   // it doesn't matter if the dynamic_cast fails.
02615   KMCommand *command =
02616     new KMUrlCopyCommand( mClickedUrl,
02617                           dynamic_cast<KMMainWidget*>( mMainWindow ) );
02618   command->start();
02619 }
02620 
02621 //-----------------------------------------------------------------------------
02622 void KMReaderWin::slotUrlOpen( const KURL &url )
02623 {
02624   if ( !url.isEmpty() )
02625     mClickedUrl = url;
02626   KMCommand *command = new KMUrlOpenCommand( mClickedUrl, this );
02627   command->start();
02628 }
02629 
02630 //-----------------------------------------------------------------------------
02631 void KMReaderWin::slotAddBookmarks()
02632 {
02633     KMCommand *command = new KMAddBookmarksCommand( mClickedUrl, this );
02634     command->start();
02635 }
02636 
02637 //-----------------------------------------------------------------------------
02638 void KMReaderWin::slotUrlSave()
02639 {
02640   KMCommand *command = new KMUrlSaveCommand( mClickedUrl, mMainWindow );
02641   command->start();
02642 }
02643 
02644 //-----------------------------------------------------------------------------
02645 void KMReaderWin::slotMailtoReply()
02646 {
02647   KMCommand *command = new KMMailtoReplyCommand( mMainWindow, mClickedUrl,
02648                                                  message(), copyText() );
02649   command->start();
02650 }
02651 
02652 //-----------------------------------------------------------------------------
02653 partNode * KMReaderWin::partNodeFromUrl( const KURL & url ) {
02654   return mRootNode ? mRootNode->findId( msgPartFromUrl( url ) ) : 0 ;
02655 }
02656 
02657 partNode * KMReaderWin::partNodeForId( int id ) {
02658   return mRootNode ? mRootNode->findId( id ) : 0 ;
02659 }
02660 
02661 
02662 KURL KMReaderWin::tempFileUrlFromPartNode( const partNode * node )
02663 {
02664   if (!node) return KURL();
02665   TQStringList::const_iterator it = mTempFiles.begin();
02666   TQStringList::const_iterator end = mTempFiles.end();
02667 
02668   while ( it != end ) {
02669       TQString path = *it;
02670       it++;
02671       uint right = path.findRev('/');
02672       uint left = path.findRev('.', right);
02673 
02674       bool ok;
02675       int res = path.mid(left + 1, right - left - 1).toInt(&ok);
02676       if ( res == node->nodeId() )
02677           return KURL( path );
02678   }
02679   return KURL();
02680 }
02681 
02682 //-----------------------------------------------------------------------------
02683 void KMReaderWin::slotSaveAttachments()
02684 {
02685   mAtmUpdate = true;
02686   KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( mMainWindow,
02687                                                                         message() );
02688   saveCommand->start();
02689 }
02690 
02691 //-----------------------------------------------------------------------------
02692 void KMReaderWin::saveAttachment( const KURL &tempFileName )
02693 {
02694   mAtmCurrent = msgPartFromUrl( tempFileName );
02695   mAtmCurrentName = mClickedUrl.path();
02696   slotHandleAttachment( KMHandleAttachmentCommand::Save ); // save
02697 }
02698 
02699 //-----------------------------------------------------------------------------
02700 void KMReaderWin::slotSaveMsg()
02701 {
02702   KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand( mMainWindow, message() );
02703 
02704   if (saveCommand->url().isEmpty())
02705     delete saveCommand;
02706   else
02707     saveCommand->start();
02708 }
02709 //-----------------------------------------------------------------------------
02710 void KMReaderWin::slotIMChat()
02711 {
02712   KMCommand *command = new KMIMChatCommand( mClickedUrl, message() );
02713   command->start();
02714 }
02715 
02716 //-----------------------------------------------------------------------------
02717 static TQString linkForNode( const DOM::Node &node )
02718 {
02719   try {
02720     if ( node.isNull() )
02721       return TQString();
02722 
02723     const DOM::NamedNodeMap attributes = node.attributes();
02724     if ( !attributes.isNull() ) {
02725       const DOM::Node href = attributes.getNamedItem( DOM::DOMString( "href" ) );
02726       if ( !href.isNull() ) {
02727         return href.nodeValue().string();
02728       }
02729     }
02730     if ( !node.parentNode().isNull() ) {
02731       return linkForNode( node.parentNode() );
02732     } else {
02733       return TQString();
02734     }
02735   } catch ( DOM::DOMException &e ) {
02736     kdWarning(5006) << "Got an exception when trying to determine link under cursor!" << endl;
02737     return TQString();
02738   }
02739 }
02740 
02741 //-----------------------------------------------------------------------------
02742 bool KMReaderWin::eventFilter( TQObject *, TQEvent *e )
02743 {
02744   if ( e->type() == TQEvent::MouseButtonPress ) {
02745     TQMouseEvent* me = TQT_TQMOUSEEVENT(e);
02746     if ( me->button() == Qt::LeftButton && ( me->state() & ShiftButton ) ) {
02747       // special processing for shift+click
02748       URLHandlerManager::instance()->handleShiftClick( mHoveredUrl, this );
02749       return true;
02750     }
02751 
02752     if ( me->button() == Qt::LeftButton ) {
02753 
02754       TQString imagePath;
02755       const DOM::Node nodeUnderMouse = mViewer->nodeUnderMouse();
02756       if ( !nodeUnderMouse.isNull() ) {
02757         const DOM::NamedNodeMap attributes = nodeUnderMouse.attributes();
02758         if ( !attributes.isNull() ) {
02759           const DOM::Node src = attributes.getNamedItem( DOM::DOMString( "src" ) );
02760           if ( !src.isNull() ) {
02761             imagePath = src.nodeValue().string();
02762           }
02763         }
02764       }
02765 
02766       mCanStartDrag = URLHandlerManager::instance()->willHandleDrag( mHoveredUrl, imagePath, this );
02767       mLastClickPosition = me->pos();
02768       mLastClickImagePath = imagePath;
02769     }
02770   }
02771 
02772   if ( e->type() ==  TQEvent::MouseButtonRelease ) {
02773     mCanStartDrag = false;
02774   }
02775 
02776   if ( e->type() == TQEvent::MouseMove ) {
02777     TQMouseEvent* me = TQT_TQMOUSEEVENT( e );
02778 
02779     // Handle this ourselves instead of connecting to mViewer::onURL(), since TDEHTML misses some
02780     // notifications in case we started a drag ourselves
02781     slotUrlOn( linkForNode( mViewer->nodeUnderMouse() ) );
02782 
02783     if ( ( mLastClickPosition - me->pos() ).manhattanLength() > TDEGlobalSettings::dndEventDelay() ) {
02784       if ( mCanStartDrag && ( !( mHoveredUrl.isEmpty() && mLastClickImagePath.isEmpty() ) ) ) {
02785         if ( URLHandlerManager::instance()->handleDrag( mHoveredUrl, mLastClickImagePath, this ) ) {
02786           mCanStartDrag = false;
02787           slotUrlOn( TQString() );
02788 
02789           // HACK: Send a mouse release event to the TDEHTMLView, as otherwise that will be missed in
02790           //       case we started a drag. If the event is missed, the HTML view gets into a wrong
02791           //       state, in which funny things like unsolicited drags start to happen.
02792           TQMouseEvent mouseEvent( TQEvent::MouseButtonRelease, me->pos(), Qt::NoButton, Qt::NoButton );
02793           TQT_TQOBJECT( mViewer->view() )->eventFilter( mViewer->view()->viewport(),
02794                                                                  &mouseEvent );
02795           return true;
02796         }
02797       }
02798     }
02799   }
02800 
02801   // standard event processing
02802   return false;
02803 }
02804 
02805 void KMReaderWin::fillCommandInfo( partNode *node, KMMessage **msg, int *nodeId )
02806 {
02807   Q_ASSERT( msg && nodeId );
02808 
02809   if ( mSerNumOfOriginalMessage != 0 ) {
02810     KMFolder *folder = 0;
02811     int index = -1;
02812     KMMsgDict::instance()->getLocation( mSerNumOfOriginalMessage, &folder, &index );
02813     if ( folder && index != -1 )
02814       *msg = folder->getMsg( index );
02815 
02816     if ( !( *msg ) ) {
02817       kdWarning( 5006 ) << "Unable to find the original message, aborting attachment deletion!" << endl;
02818       return;
02819     }
02820 
02821     *nodeId = node->nodeId() + mNodeIdOffset;
02822   }
02823   else {
02824     *nodeId = node->nodeId();
02825     *msg = message();
02826   }
02827 }
02828 
02829 void KMReaderWin::slotDeleteAttachment(partNode * node)
02830 {
02831   if ( KMessageBox::warningContinueCancel( this,
02832        i18n("Deleting an attachment might invalidate any digital signature on this message."),
02833        i18n("Delete Attachment"), KStdGuiItem::del(), "DeleteAttachmentSignatureWarning" )
02834      != KMessageBox::Continue ) {
02835     return;
02836   }
02837 
02838   int nodeId = -1;
02839   KMMessage *msg = 0;
02840   fillCommandInfo( node, &msg, &nodeId );
02841   if ( msg && nodeId != -1 ) {
02842     KMDeleteAttachmentCommand* command = new KMDeleteAttachmentCommand( nodeId, msg, this );
02843     command->start();
02844     connect( command, TQT_SIGNAL( completed( KMCommand * ) ),
02845              TQT_TQOBJECT(this), TQT_SLOT( updateReaderWin() ) );
02846     connect( command, TQT_SIGNAL( completed( KMCommand * ) ),
02847              TQT_TQOBJECT(this), TQT_SLOT( disconnectMsgAdded() ) );
02848 
02849     // ### HACK: Since the command will do delete + add, a new message will arrive. However, we don't
02850     // want the selection to change. Therefore, as soon as a new message arrives, select it, and then
02851     // disconnect.
02852     // Of course the are races, another message can arrive before ours, but we take the risk.
02853     // And it won't work properly with multiple main windows
02854     const KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers();
02855     connect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ),
02856              TQT_TQOBJECT(this), TQT_SLOT( msgAdded( TQListViewItem* ) ) );
02857   }
02858 
02859   // If we are operating on a copy of parts of the message, make sure to update the copy as well.
02860   if ( mSerNumOfOriginalMessage != 0 && message() ) {
02861     message()->deleteBodyPart( node->nodeId() );
02862     update( true );
02863   }
02864 }
02865 
02866 void KMReaderWin::msgAdded( TQListViewItem *item )
02867 {
02868   // A new message was added to the message list view. Select it.
02869   // This is only connected right after we started a attachment delete command, so we expect a new
02870   // message. Disconnect right afterwards, we only want this particular message to be selected.
02871   disconnectMsgAdded();
02872   KMHeaders * const headers = KMKernel::self()->getKMMainWidget()->headers();
02873   headers->setCurrentItem( item );
02874   headers->clearSelection();
02875   headers->setSelected( item, true );
02876 }
02877 
02878 void KMReaderWin::disconnectMsgAdded()
02879 {
02880   const KMHeaders *const headers = KMKernel::self()->getKMMainWidget()->headers();
02881   disconnect( headers, TQT_SIGNAL( msgAddedToListView( TQListViewItem* ) ),
02882               TQT_TQOBJECT(this), TQT_SLOT( msgAdded( TQListViewItem* ) ) );
02883 }
02884 
02885 void KMReaderWin::slotEditAttachment(partNode * node)
02886 {
02887   if ( KMessageBox::warningContinueCancel( this,
02888         i18n("Modifying an attachment might invalidate any digital signature on this message."),
02889         i18n("Edit Attachment"), KGuiItem( i18n("Edit"), "edit" ), "EditAttachmentSignatureWarning" )
02890         != KMessageBox::Continue ) {
02891     return;
02892   }
02893 
02894   int nodeId = -1;
02895   KMMessage *msg = 0;
02896   fillCommandInfo( node, &msg, &nodeId );
02897   if ( msg && nodeId != -1 ) {
02898     KMEditAttachmentCommand* command = new KMEditAttachmentCommand( nodeId, msg, this );
02899     command->start();
02900   }
02901 
02902   // FIXME: If we are operating on a copy of parts of the message, make sure to update the copy as well.
02903 }
02904 
02905 KMail::CSSHelper* KMReaderWin::cssHelper()
02906 {
02907   return mCSSHelper;
02908 }
02909 
02910 bool KMReaderWin::decryptMessage() const
02911 {
02912   if ( !GlobalSettings::self()->alwaysDecrypt() )
02913     return mDecrytMessageOverwrite;
02914   return true;
02915 }
02916 
02917 void KMReaderWin::scrollToAttachment( const partNode *node )
02918 {
02919   DOM::Document doc = mViewer->htmlDocument();
02920 
02921   // The anchors for this are created in ObjectTreeParser::parseObjectTree()
02922   mViewer->gotoAnchor( TQString::fromLatin1( "att%1" ).arg( node->nodeId() ) );
02923 
02924   // Remove any old color markings which might be there
02925   const partNode *root = node->topLevelParent();
02926   for ( int i = 0; i <= root->totalChildCount() + 1; i++ ) {
02927     DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( i + 1 ) );
02928     if ( !attachmentDiv.isNull() )
02929       attachmentDiv.removeAttribute( "style" );
02930   }
02931 
02932   // Don't mark hidden nodes, that would just produce a strange yellow line
02933   if ( node->isDisplayedHidden() )
02934     return;
02935 
02936   // Now, color the div of the attachment in yellow, so that the user sees what happened.
02937   // We created a special marked div for this in writeAttachmentMarkHeader() in ObjectTreeParser,
02938   // find and modify that now.
02939   DOM::Element attachmentDiv = doc.getElementById( TQString( "attachmentDiv%1" ).arg( node->nodeId() ) );
02940   if ( attachmentDiv.isNull() ) {
02941     kdWarning( 5006 ) << "Could not find attachment div for attachment " << node->nodeId() << endl;
02942     return;
02943   }
02944 
02945   attachmentDiv.setAttribute( "style", TQString( "border:2px solid %1" )
02946       .arg( cssHelper()->pgpWarnColor().name() ) );
02947 
02948   // Update rendering, otherwise the rendering is not updated when the user clicks on an attachment
02949   // that causes scrolling and the open attachment dialog
02950   doc.updateRendering();
02951 }
02952 
02953 void KMReaderWin::injectAttachments()
02954 {
02955   // inject attachments in header view
02956   // we have to do that after the otp has run so we also see encrypted parts
02957   DOM::Document doc = mViewer->htmlDocument();
02958   DOM::Element injectionPoint = doc.getElementById( "attachmentInjectionPoint" );
02959   if ( injectionPoint.isNull() )
02960     return;
02961 
02962   TQString imgpath( locate("data","kmail/pics/") );
02963   TQString visibility;
02964   TQString urlHandle;
02965   TQString imgSrc;
02966   if( !showAttachmentQuicklist() ) {
02967     urlHandle.append( "kmail:showAttachmentQuicklist" );
02968     imgSrc.append( "attachmentQuicklistClosed.png" );
02969   } else {
02970     urlHandle.append( "kmail:hideAttachmentQuicklist" );
02971     imgSrc.append( "attachmentQuicklistOpened.png" );
02972   }
02973 
02974   TQString html = renderAttachments( mRootNode, TQApplication::palette().active().background() );
02975   if ( html.isEmpty() )
02976     return;
02977 
02978   TQString link("");
02979   if ( headerStyle() == HeaderStyle::fancy() ) {
02980     link += "<div style=\"text-align: left;\"><a href=\"" + urlHandle + "\"><img src=\"" +
02981             imgpath + imgSrc + "\"/></a></div>";
02982     html.prepend( link );
02983     html.prepend( TQString::fromLatin1( "<div style=\"float:left;\">%1&nbsp;</div>" ).
02984                   arg( i18n( "Attachments:" ) ) );
02985   } else {
02986     link += "<div style=\"text-align: right;\"><a href=\"" + urlHandle + "\"><img src=\"" +
02987             imgpath + imgSrc + "\"/></a></div>";
02988     html.prepend( link );
02989   }
02990 
02991   assert( injectionPoint.tagName() == "div" );
02992   static_cast<DOM::HTMLElement>( injectionPoint ).setInnerHTML( html );
02993 }
02994 
02995 static TQColor nextColor( const TQColor & c )
02996 {
02997   int h, s, v;
02998   c.hsv( &h, &s, &v );
02999   return TQColor( (h + 50) % 360, TQMAX(s, 64), v, TQColor::Hsv );
03000 }
03001 
03002 TQString KMReaderWin::renderAttachments(partNode * node, const TQColor &bgColor )
03003 {
03004   if ( !node )
03005     return TQString();
03006 
03007   TQString html;
03008   if ( node->firstChild() ) {
03009     TQString subHtml = renderAttachments( node->firstChild(), nextColor( bgColor ) );
03010     if ( !subHtml.isEmpty() ) {
03011 
03012       TQString visibility;
03013       if ( !showAttachmentQuicklist() ) {
03014         visibility.append( "display:none;" );
03015       }
03016 
03017       TQString margin;
03018       if ( node != mRootNode || headerStyle() != HeaderStyle::enterprise() )
03019         margin = "padding:2px; margin:2px; ";
03020       TQString align = "left";
03021       if ( headerStyle() == HeaderStyle::enterprise() )
03022         align = "right";
03023       if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode )
03024         html += TQString::fromLatin1("<div style=\"background:%1; %2"
03025                 "vertical-align:middle; float:%3; %4\">").arg( bgColor.name() ).arg( margin )
03026                                                          .arg( align ).arg( visibility );
03027       html += subHtml;
03028       if ( node->msgPart().typeStr().lower() == "message" || node == mRootNode )
03029         html += "</div>";
03030     }
03031   } else {
03032     partNode::AttachmentDisplayInfo info = node->attachmentDisplayInfo();
03033     if ( info.displayInHeader ) {
03034       html += "<div style=\"float:left;\">";
03035       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() );
03036       TQString fileName = writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
03037       TQString href = node->asHREF( "header" );
03038       html += TQString::fromLatin1( "<a href=\"" ) + href +
03039               TQString::fromLatin1( "\">" );
03040       html += "<img style=\"vertical-align:middle;\" src=\"" + info.icon + "\"/>&nbsp;";
03041       if ( headerStyle() == HeaderStyle::enterprise() ) {
03042         TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() );
03043         TQFontMetrics fm( bodyFont );
03044         html += KStringHandler::rPixelSqueeze( info.label, fm, 140 );
03045       } else if ( headerStyle() == HeaderStyle::fancy() ) {
03046         TQFont bodyFont = mCSSHelper->bodyFont( isFixedFont() );
03047         TQFontMetrics fm( bodyFont );
03048         html += KStringHandler::rPixelSqueeze( info.label, fm, 640 );
03049       } else {
03050         html += info.label;
03051       }
03052       html += "</a></span></div> ";
03053     }
03054   }
03055 
03056   html += renderAttachments( node->nextSibling(), nextColor ( bgColor ) );
03057   return html;
03058 }
03059 
03060 using namespace KMail::Interface;
03061 
03062 void KMReaderWin::setBodyPartMemento( const partNode * node, const TQCString & which, BodyPartMemento * memento )
03063 {
03064   const TQCString index = node->path() + ':' + which.lower();
03065 
03066   const std::map<TQCString,BodyPartMemento*>::iterator it = mBodyPartMementoMap.lower_bound( index );
03067   if ( it != mBodyPartMementoMap.end() && it->first == index ) {
03068 
03069     if ( memento && memento == it->second )
03070       return;
03071 
03072     delete it->second;
03073 
03074     if ( memento ) {
03075       it->second = memento;
03076     }
03077     else {
03078       mBodyPartMementoMap.erase( it );
03079     }
03080 
03081   } else {
03082     if ( memento ) {
03083       mBodyPartMementoMap.insert( it, std::make_pair( index, memento ) );
03084     }
03085   }
03086 
03087   if ( Observable * o = memento ? memento->asObservable() : 0 )
03088     o->attach( this );
03089 }
03090 
03091 BodyPartMemento * KMReaderWin::bodyPartMemento( const partNode * node, const TQCString & which ) const
03092 {
03093   const TQCString index = node->path() + ':' + which.lower();
03094   const std::map<TQCString,BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.find( index );
03095   if ( it == mBodyPartMementoMap.end() ) {
03096     return 0;
03097   }
03098   else {
03099     return it->second;
03100   }
03101 }
03102 
03103 static void detach_and_delete( BodyPartMemento * memento, KMReaderWin * obs ) {
03104   if ( Observable * const o = memento ? memento->asObservable() : 0 )
03105     o->detach( obs );
03106   delete memento;
03107 }
03108 
03109 void KMReaderWin::clearBodyPartMementos()
03110 {
03111   for ( std::map<TQCString,BodyPartMemento*>::const_iterator it = mBodyPartMementoMap.begin(), end = mBodyPartMementoMap.end() ; it != end ; ++it )
03112     // Detach the memento from the reader. When cancelling it, it might trigger an update of the
03113     // reader, which we are not interested in, and which is dangerous, since half the mementos are
03114     // already deleted.
03115     // https://issues.kolab.org/issue4187
03116     detach_and_delete( it->second, this );
03117 
03118   mBodyPartMementoMap.clear();
03119 }
03120 
03121 #include "kmreaderwin.moc"
03122 
03123