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