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