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