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