File indexing completed on 2024-05-12 05:20:44
0001 /* 0002 This file is part of KMail, the KDE mail client. 0003 SPDX-FileCopyrightText: 1997 Markus Wuebben <markus.wuebben@kde.org> 0004 SPDX-FileCopyrightText: 2009-2024 Laurent Montel <montel@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 // define this to copy all html that is written to the readerwindow to 0010 // filehtmlwriter.out in the current working directory 0011 #include "kmreaderwin.h" 0012 0013 #include "dialog/addemailtoexistingcontactdialog.h" 0014 #include "job/addemailtoexistingcontactjob.h" 0015 #include "kmmainwidget.h" 0016 #include "kmreadermainwin.h" 0017 #include <MailCommon/MailKernel> 0018 0019 #include "kmail-version.h" 0020 #include "kmcommands.h" 0021 #include <Akonadi/AddEmailAddressJob> 0022 #include <Akonadi/AddEmailDisplayJob> 0023 #include <Akonadi/OpenEmailAddressJob> 0024 #include <KEmailAddress> 0025 #include <MailCommon/MailUtil> 0026 #include <MessageViewer/CSSHelper> 0027 #include <MessageViewer/HeaderStrategy> 0028 #include <MessageViewer/MarkMessageReadHandler> 0029 #include <MessageViewer/MessageViewerSettings> 0030 #include <PimCommon/BroadcastStatus> 0031 #include <QVBoxLayout> 0032 using MessageViewer::CSSHelper; 0033 #include "util.h" 0034 #include <Akonadi/MessageFlags> 0035 #include <KMime/MDN> 0036 #include <MessageCore/StringUtil> 0037 #include <QCryptographicHash> 0038 0039 using namespace MessageViewer; 0040 #include <MessageCore/MessageCoreSettings> 0041 0042 #include <MessageComposer/Composer> 0043 #include <MessageComposer/InfoPart> 0044 #include <MessageComposer/MDNWarningWidgetJob> 0045 #include <MessageComposer/MessageSender> 0046 #include <MessageComposer/TextPart> 0047 #include <MessageViewer/AttachmentStrategy> 0048 0049 #include <KIO/JobUiDelegate> 0050 0051 #include <Akonadi/ChangeRecorder> 0052 #include <Akonadi/ContactEditorDialog> 0053 0054 #include "kmail_debug.h" 0055 #include <KActionCollection> 0056 #include <KLocalizedString> 0057 #include <KMessageBox> 0058 #include <KToggleAction> 0059 #include <QAction> 0060 #include <QDesktopServices> 0061 #include <QMenu> 0062 0063 #include <QClipboard> 0064 0065 // X headers... 0066 #undef Never 0067 #undef Always 0068 0069 #include <MailCommon/MDNWarningJob> 0070 #include <MailCommon/MailUtil> 0071 0072 #include <KLazyLocalizedString> 0073 0074 using namespace KMail; 0075 using namespace MailCommon; 0076 0077 KMReaderWin::KMReaderWin(QWidget *aParent, QWidget *mainWindow, KActionCollection *actionCollection) 0078 : QWidget(aParent) 0079 , mMainWindow(mainWindow) 0080 , mActionCollection(actionCollection) 0081 { 0082 createActions(); 0083 auto vlay = new QVBoxLayout(this); 0084 vlay->setContentsMargins(0, 0, 0, 0); 0085 mViewer = new Viewer(this, mainWindow, mActionCollection); 0086 mViewer->setIdentityManager(kmkernel->identityManager()); 0087 connect(mViewer, qOverload<const Akonadi::Item &, const QUrl &>(&Viewer::urlClicked), this, &KMReaderWin::slotUrlClicked); 0088 connect(mViewer, 0089 &Viewer::requestConfigSync, 0090 kmkernel, 0091 &KMKernel::slotRequestConfigSync, 0092 Qt::QueuedConnection); // happens anyway on shutdown, so we can skip it there with using a queued connection 0093 connect(mViewer, &Viewer::makeResourceOnline, kmkernel, &KMKernel::makeResourceOnline); 0094 connect(mViewer, &MessageViewer::Viewer::showReader, this, &KMReaderWin::slotShowReader); 0095 connect(mViewer, &MessageViewer::Viewer::showMessage, this, &KMReaderWin::slotShowMessage); 0096 connect(mViewer, &MessageViewer::Viewer::showStatusBarMessage, this, &KMReaderWin::showStatusBarMessage); 0097 connect(mViewer, &MessageViewer::Viewer::printingFinished, this, &KMReaderWin::slotPrintingFinished); 0098 connect(mViewer, &MessageViewer::Viewer::zoomChanged, this, &KMReaderWin::zoomChanged); 0099 connect(mViewer, qOverload<const Akonadi::Item &>(&Viewer::deleteMessage), this, &KMReaderWin::slotDeleteMessage); 0100 connect(mViewer, &MessageViewer::Viewer::showNextMessage, this, &KMReaderWin::showNextMessage); 0101 connect(mViewer, &MessageViewer::Viewer::showPreviousMessage, this, &KMReaderWin::showPreviousMessage); 0102 connect(mViewer, &MessageViewer::Viewer::sendResponse, this, &KMReaderWin::slotSendMdnResponse); 0103 connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemChanged, this, &KMReaderWin::slotItemModified); 0104 0105 mViewer->addMessageLoadedHandler(new MessageViewer::MarkMessageReadHandler(this)); 0106 0107 vlay->addWidget(mViewer); 0108 readConfig(); 0109 } 0110 0111 void KMReaderWin::createActions() 0112 { 0113 KActionCollection *ac = mActionCollection; 0114 if (!ac) { 0115 return; 0116 } 0117 // 0118 // Message Menu 0119 // 0120 // new message to 0121 mMailToComposeAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("New Message To..."), this); 0122 ac->addAction(QStringLiteral("mail_new"), mMailToComposeAction); 0123 ac->setShortcutsConfigurable(mMailToComposeAction, false); 0124 connect(mMailToComposeAction, &QAction::triggered, this, &KMReaderWin::slotMailtoCompose); 0125 0126 // reply to 0127 mMailToReplyAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply To..."), this); 0128 ac->addAction(QStringLiteral("mailto_reply"), mMailToReplyAction); 0129 ac->setShortcutsConfigurable(mMailToReplyAction, false); 0130 connect(mMailToReplyAction, &QAction::triggered, this, &KMReaderWin::slotMailtoReply); 0131 0132 // forward to 0133 mMailToForwardAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18n("Forward To..."), this); 0134 ac->setShortcutsConfigurable(mMailToForwardAction, false); 0135 ac->addAction(QStringLiteral("mailto_forward"), mMailToForwardAction); 0136 connect(mMailToForwardAction, &QAction::triggered, this, &KMReaderWin::slotMailtoForward); 0137 0138 // add to addressbook 0139 mAddAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Address Book"), this); 0140 ac->setShortcutsConfigurable(mAddAddrBookAction, false); 0141 ac->addAction(QStringLiteral("add_addr_book"), mAddAddrBookAction); 0142 connect(mAddAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoAddAddrBook); 0143 0144 mAddEmailToExistingContactAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Existing Contact"), this); 0145 ac->setShortcutsConfigurable(mAddEmailToExistingContactAction, false); 0146 ac->addAction(QStringLiteral("add_to_existing_contact"), mAddAddrBookAction); 0147 connect(mAddEmailToExistingContactAction, &QAction::triggered, this, &KMReaderWin::slotMailToAddToExistingContact); 0148 0149 // open in addressbook 0150 mOpenAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Open in Address Book"), this); 0151 ac->setShortcutsConfigurable(mOpenAddrBookAction, false); 0152 ac->addAction(QStringLiteral("openin_addr_book"), mOpenAddrBookAction); 0153 connect(mOpenAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoOpenAddrBook); 0154 // bookmark message 0155 mAddUrlToBookmarkAction = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Bookmark This Link"), this); 0156 ac->setShortcutsConfigurable(mAddUrlToBookmarkAction, false); 0157 ac->addAction(QStringLiteral("add_bookmarks"), mAddUrlToBookmarkAction); 0158 connect(mAddUrlToBookmarkAction, &QAction::triggered, this, &KMReaderWin::slotAddUrlToBookmark); 0159 0160 mEditContactAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Edit contact..."), this); 0161 ac->setShortcutsConfigurable(mEditContactAction, false); 0162 ac->addAction(QStringLiteral("edit_contact"), mOpenAddrBookAction); 0163 connect(mEditContactAction, &QAction::triggered, this, &KMReaderWin::slotEditContact); 0164 0165 // save URL as 0166 mUrlSaveAsAction = new QAction(i18n("Save Link As..."), this); 0167 ac->addAction(QStringLiteral("saveas_url"), mUrlSaveAsAction); 0168 ac->setShortcutsConfigurable(mUrlSaveAsAction, false); 0169 connect(mUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotUrlSave); 0170 0171 // find text 0172 auto action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("&Find in Message..."), this); 0173 ac->addAction(QStringLiteral("find_in_messages"), action); 0174 connect(action, &QAction::triggered, this, &KMReaderWin::slotFind); 0175 ac->setDefaultShortcut(action, KStandardShortcut::find().first()); 0176 0177 // save Image On Disk 0178 mImageUrlSaveAsAction = new QAction(i18n("Save Image On Disk..."), this); 0179 ac->addAction(QStringLiteral("saveas_imageurl"), mImageUrlSaveAsAction); 0180 ac->setShortcutsConfigurable(mImageUrlSaveAsAction, false); 0181 connect(mImageUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotSaveImageOnDisk); 0182 0183 // save Image On Disk 0184 mOpenImageAction = new QAction(i18n("Open Image..."), this); 0185 ac->addAction(QStringLiteral("open_image"), mOpenImageAction); 0186 ac->setShortcutsConfigurable(mOpenImageAction, false); 0187 connect(mOpenImageAction, &QAction::triggered, this, &KMReaderWin::slotOpenImage); 0188 0189 // View html options 0190 mViewHtmlOptions = new QMenu(i18n("Show HTML Format"), this); 0191 mViewAsHtml = new QAction(i18n("Show HTML format when mail comes from this contact"), mViewHtmlOptions); 0192 ac->setShortcutsConfigurable(mViewAsHtml, false); 0193 connect(mViewAsHtml, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions); 0194 mViewAsHtml->setCheckable(true); 0195 mViewHtmlOptions->addAction(mViewAsHtml); 0196 0197 mLoadExternalReference = new QAction(i18n("Load external reference when mail comes for this contact"), mViewHtmlOptions); 0198 ac->setShortcutsConfigurable(mLoadExternalReference, false); 0199 connect(mLoadExternalReference, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions); 0200 mLoadExternalReference->setCheckable(true); 0201 mViewHtmlOptions->addAction(mLoadExternalReference); 0202 0203 mShareImage = new QAction(i18n("Share image..."), this); 0204 ac->addAction(QStringLiteral("share_imageurl"), mShareImage); 0205 ac->setShortcutsConfigurable(mShareImage, false); 0206 connect(mShareImage, &QAction::triggered, this, &KMReaderWin::slotShareImage); 0207 } 0208 0209 void KMReaderWin::setUseFixedFont(bool useFixedFont) 0210 { 0211 mViewer->setUseFixedFont(useFixedFont); 0212 } 0213 0214 Viewer *KMReaderWin::viewer() const 0215 { 0216 return mViewer; 0217 } 0218 0219 bool KMReaderWin::isFixedFont() const 0220 { 0221 return mViewer->isFixedFont(); 0222 } 0223 0224 KMReaderWin::~KMReaderWin() = default; 0225 0226 void KMReaderWin::readConfig() 0227 { 0228 mViewer->readConfig(); 0229 } 0230 0231 void KMReaderWin::setAttachmentStrategy(const MessageViewer::AttachmentStrategy *strategy) 0232 { 0233 mViewer->setAttachmentStrategy(strategy); 0234 } 0235 0236 void KMReaderWin::setOverrideEncoding(const QString &encoding) 0237 { 0238 mViewer->setOverrideEncoding(encoding); 0239 } 0240 0241 void KMReaderWin::clearCache() 0242 { 0243 clear(); 0244 } 0245 0246 void KMReaderWin::updateShowMultiMessagesButton(bool enablePreviousButton, bool enableNextButton) 0247 { 0248 mViewer->updateShowMultiMessagesButton(enablePreviousButton, enableNextButton); 0249 } 0250 0251 void KMReaderWin::hasMultiMessages(bool multi) 0252 { 0253 mViewer->hasMultiMessages(multi); 0254 } 0255 0256 // enter items for the "Important changes" list here: 0257 static const KLazyLocalizedString kmailChanges[] = { 0258 kli18n("KMail is now based on the Akonadi Personal Information Management framework, which brings many " 0259 "changes all around.")}; 0260 static const int numKMailChanges = sizeof kmailChanges / sizeof *kmailChanges; 0261 0262 // enter items for the "new features" list here, so the main body of 0263 // the welcome page can be left untouched (probably much easier for 0264 // the translators). Note that the <li>...</li> tags are added 0265 // automatically below: 0266 static const KLazyLocalizedString kmailNewFeatures[] = { 0267 0268 kli18n("Push email (IMAP IDLE)"), 0269 kli18n("Improved searches"), 0270 kli18n("Support for adding notes (annotations) to mails"), 0271 kli18n("Less GUI freezes, mail checks happen in the background"), 0272 kli18n("Plugins support"), 0273 kli18n("New HTML renderer (QtWebEngine)"), 0274 kli18n("Added Check for Phishing URL"), 0275 kli18n("Added Check DKIM"), 0276 kli18n("Added Markdown Editing Support"), 0277 kli18n("Added Grammar Plugin Support"), 0278 kli18n("Added etesync resource support"), 0279 kli18n("Added Microsoft Exchange Web Services resource support"), 0280 }; 0281 static const int numKMailNewFeatures = sizeof kmailNewFeatures / sizeof *kmailNewFeatures; 0282 0283 // static 0284 QString KMReaderWin::newFeaturesMD5() 0285 { 0286 QByteArray str; 0287 for (int i = 0; i < numKMailChanges; ++i) { 0288 str += kmailChanges[i].untranslatedText(); 0289 } 0290 for (int i = 0; i < numKMailNewFeatures; ++i) { 0291 str += kmailNewFeatures[i].untranslatedText(); 0292 } 0293 QCryptographicHash md5(QCryptographicHash::Md5); 0294 md5.addData(str); 0295 return QLatin1StringView(md5.result().toBase64()); 0296 } 0297 0298 void KMReaderWin::displaySplashPage(const QString &templateName, const QVariantHash &_data) 0299 { 0300 QVariantHash data = _data; 0301 if (!data.contains(QStringLiteral("icon"))) { 0302 data[QStringLiteral("icon")] = QStringLiteral("kmail"); 0303 } 0304 if (!data.contains(QStringLiteral("name"))) { 0305 data[QStringLiteral("name")] = i18n("KMail"); 0306 } 0307 if (!data.contains(QStringLiteral("subtitle"))) { 0308 data[QStringLiteral("subtitle")] = i18n("The KDE Mail Client"); 0309 } 0310 0311 mViewer->displaySplashPage(templateName, data, QByteArrayLiteral("kmail")); 0312 } 0313 0314 void KMReaderWin::displayBusyPage() 0315 { 0316 displaySplashPage(QStringLiteral("status.html"), 0317 {{QStringLiteral("title"), i18n("Retrieving Folder Contents")}, {QStringLiteral("subtext"), i18n("Please wait . . .")}}); 0318 } 0319 0320 void KMReaderWin::displayOfflinePage() 0321 { 0322 displaySplashPage(QStringLiteral("status.html"), 0323 {{QStringLiteral("title"), i18n("Offline")}, 0324 {QStringLiteral("subtext"), 0325 i18n("KMail is currently in offline mode. " 0326 "Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p>")}}); 0327 } 0328 0329 void KMReaderWin::displayResourceOfflinePage() 0330 { 0331 displaySplashPage(QStringLiteral("status.html"), 0332 {{QStringLiteral("title"), i18n("Offline")}, 0333 {QStringLiteral("subtext"), 0334 i18n("Account is currently in offline mode. " 0335 "Click <a href=\"kmail:goResourceOnline\">here</a> to go online . . .</p>")}}); 0336 } 0337 0338 void KMReaderWin::displayAboutPage() 0339 { 0340 QVariantHash data; 0341 data[QStringLiteral("version")] = QStringLiteral(KDEPIM_VERSION); 0342 data[QStringLiteral("firstStart")] = kmkernel->firstStart(); 0343 0344 QVariantList features; 0345 features.reserve(numKMailNewFeatures); 0346 for (int i = 0; i < numKMailNewFeatures; ++i) { 0347 features.push_back(kmailNewFeatures[i].toString()); 0348 } 0349 data[QStringLiteral("newFeatures")] = features; 0350 0351 QVariantList changes; 0352 changes.reserve(numKMailChanges); 0353 for (int i = 0; i < numKMailChanges; ++i) { 0354 features.push_back(kmailChanges[i].toString()); 0355 } 0356 data[QStringLiteral("importantChanges")] = changes; 0357 0358 displaySplashPage(QStringLiteral(":/about/introduction_kmail.html"), data); 0359 } 0360 0361 void KMReaderWin::slotFind() 0362 { 0363 mViewer->slotFind(); 0364 } 0365 0366 void KMReaderWin::slotCopySelectedText() 0367 { 0368 QString selection = mViewer->selectedText(); 0369 selection.replace(QChar::Nbsp, QLatin1Char(' ')); 0370 QApplication::clipboard()->setText(selection); 0371 } 0372 0373 void KMReaderWin::setMsgPart(KMime::Content *aMsgPart) 0374 { 0375 mViewer->setMessagePart(aMsgPart); 0376 } 0377 0378 QString KMReaderWin::copyText() const 0379 { 0380 return mViewer->selectedText(); 0381 } 0382 0383 MessageViewer::Viewer::DisplayFormatMessage KMReaderWin::displayFormatMessageOverwrite() const 0384 { 0385 return mViewer->displayFormatMessageOverwrite(); 0386 } 0387 0388 void KMReaderWin::setPrintElementBackground(bool printElementBackground) 0389 { 0390 mViewer->setPrintElementBackground(printElementBackground); 0391 } 0392 0393 void KMReaderWin::setDisplayFormatMessageOverwrite(MessageViewer::Viewer::DisplayFormatMessage format) 0394 { 0395 mViewer->setDisplayFormatMessageOverwrite(format); 0396 } 0397 0398 void KMReaderWin::setHtmlLoadExtDefault(bool loadExtDefault) 0399 { 0400 mViewer->setHtmlLoadExtDefault(loadExtDefault); 0401 } 0402 0403 void KMReaderWin::setHtmlLoadExtOverride(bool loadExtOverride) 0404 { 0405 mViewer->setHtmlLoadExtOverride(loadExtOverride); 0406 } 0407 0408 bool KMReaderWin::htmlMail() const 0409 { 0410 return mViewer->htmlMail(); 0411 } 0412 0413 bool KMReaderWin::htmlLoadExternal() 0414 { 0415 return mViewer->htmlLoadExternal(); 0416 } 0417 0418 Akonadi::Item KMReaderWin::messageItem() const 0419 { 0420 return mViewer->messageItem(); 0421 } 0422 0423 QWidget *KMReaderWin::mainWindow() const 0424 { 0425 return mMainWindow; 0426 } 0427 0428 void KMReaderWin::slotMailtoCompose() 0429 { 0430 KMCommand *command = new KMMailtoComposeCommand(urlClicked(), messageItem()); 0431 command->start(); 0432 } 0433 0434 void KMReaderWin::slotMailtoForward() 0435 { 0436 KMCommand *command = new KMMailtoForwardCommand(mMainWindow, urlClicked(), messageItem()); 0437 command->start(); 0438 } 0439 0440 void KMReaderWin::slotMailtoAddAddrBook() 0441 { 0442 const QUrl url = urlClicked(); 0443 if (url.isEmpty()) { 0444 return; 0445 } 0446 const QString emailString = KEmailAddress::decodeMailtoUrl(url); 0447 0448 auto job = new Akonadi::AddEmailAddressJob(emailString, mMainWindow, this); 0449 job->setInteractive(true); 0450 connect(job, &Akonadi::AddEmailAddressJob::successMessage, this, [](const QString &message) { 0451 PimCommon::BroadcastStatus::instance()->setStatusMsg(message); 0452 }); 0453 job->start(); 0454 } 0455 0456 void KMReaderWin::slotMailToAddToExistingContact() 0457 { 0458 const QUrl url = urlClicked(); 0459 if (url.isEmpty()) { 0460 return; 0461 } 0462 const QString emailString = KEmailAddress::decodeMailtoUrl(url); 0463 QPointer<AddEmailToExistingContactDialog> dlg = new AddEmailToExistingContactDialog(this); 0464 if (dlg->exec()) { 0465 Akonadi::Item item = dlg->selectedContact(); 0466 if (item.isValid()) { 0467 auto job = new AddEmailToExistingContactJob(item, emailString, this); 0468 job->start(); 0469 } 0470 } 0471 delete dlg; 0472 } 0473 0474 void KMReaderWin::slotMailtoOpenAddrBook() 0475 { 0476 const QUrl url = urlClicked(); 0477 if (url.isEmpty()) { 0478 return; 0479 } 0480 const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower(); 0481 0482 auto job = new Akonadi::OpenEmailAddressJob(emailString, mMainWindow, this); 0483 job->start(); 0484 } 0485 0486 void KMReaderWin::slotAddUrlToBookmark() 0487 { 0488 const QUrl url = urlClicked(); 0489 if (url.isEmpty()) { 0490 return; 0491 } 0492 KMCommand *command = new KMAddBookmarksCommand(url, this); 0493 command->start(); 0494 } 0495 0496 void KMReaderWin::slotUrlSave() 0497 { 0498 const QUrl url = urlClicked(); 0499 if (url.isEmpty()) { 0500 return; 0501 } 0502 KMCommand *command = new KMUrlSaveCommand(url, mMainWindow); 0503 command->start(); 0504 } 0505 0506 void KMReaderWin::slotOpenImage() 0507 { 0508 const QUrl url = imageUrlClicked(); 0509 if (url.isEmpty()) { 0510 return; 0511 } 0512 QDesktopServices::openUrl(url); 0513 } 0514 0515 void KMReaderWin::slotSaveImageOnDisk() 0516 { 0517 const QUrl url = imageUrlClicked(); 0518 if (url.isEmpty()) { 0519 return; 0520 } 0521 KMCommand *command = new KMUrlSaveCommand(url, mMainWindow); 0522 command->start(); 0523 } 0524 0525 void KMReaderWin::slotMailtoReply() 0526 { 0527 auto command = new KMMailtoReplyCommand(mMainWindow, urlClicked(), messageItem(), copyText()); 0528 command->setReplyAsHtml(htmlMail()); 0529 command->start(); 0530 } 0531 0532 CSSHelper *KMReaderWin::cssHelper() const 0533 { 0534 return mViewer->cssHelper(); 0535 } 0536 0537 bool KMReaderWin::htmlLoadExtOverride() const 0538 { 0539 return mViewer->htmlLoadExtOverride(); 0540 } 0541 0542 void KMReaderWin::setDecryptMessageOverwrite(bool overwrite) 0543 { 0544 mViewer->setDecryptMessageOverwrite(overwrite); 0545 } 0546 0547 const MessageViewer::AttachmentStrategy *KMReaderWin::attachmentStrategy() const 0548 { 0549 return mViewer->attachmentStrategy(); 0550 } 0551 0552 QString KMReaderWin::overrideEncoding() const 0553 { 0554 return mViewer->overrideEncoding(); 0555 } 0556 0557 KToggleAction *KMReaderWin::toggleFixFontAction() const 0558 { 0559 return mViewer->toggleFixFontAction(); 0560 } 0561 0562 QAction *KMReaderWin::mailToComposeAction() const 0563 { 0564 return mMailToComposeAction; 0565 } 0566 0567 QAction *KMReaderWin::mailToReplyAction() const 0568 { 0569 return mMailToReplyAction; 0570 } 0571 0572 QAction *KMReaderWin::mailToForwardAction() const 0573 { 0574 return mMailToForwardAction; 0575 } 0576 0577 QAction *KMReaderWin::addAddrBookAction() const 0578 { 0579 return mAddAddrBookAction; 0580 } 0581 0582 QAction *KMReaderWin::openAddrBookAction() const 0583 { 0584 return mOpenAddrBookAction; 0585 } 0586 0587 bool KMReaderWin::mimePartTreeIsEmpty() const 0588 { 0589 return mViewer->mimePartTreeIsEmpty(); 0590 } 0591 0592 QAction *KMReaderWin::toggleMimePartTreeAction() const 0593 { 0594 return mViewer->toggleMimePartTreeAction(); 0595 } 0596 0597 KActionMenu *KMReaderWin::shareServiceUrlMenu() const 0598 { 0599 return mViewer->shareServiceUrlMenu(); 0600 } 0601 0602 DKIMViewerMenu *KMReaderWin::dkimViewerMenu() const 0603 { 0604 return mViewer->dkimViewerMenu(); 0605 } 0606 0607 RemoteContentMenu *KMReaderWin::remoteContentMenu() const 0608 { 0609 return mViewer->remoteContentMenu(); 0610 } 0611 0612 QList<QAction *> KMReaderWin::viewerPluginActionList(ViewerPluginInterface::SpecificFeatureTypes features) 0613 { 0614 return mViewer->viewerPluginActionList(features); 0615 } 0616 0617 QAction *KMReaderWin::selectAllAction() const 0618 { 0619 return mViewer->selectAllAction(); 0620 } 0621 0622 QAction *KMReaderWin::copyURLAction() const 0623 { 0624 return mViewer->copyURLAction(); 0625 } 0626 0627 QAction *KMReaderWin::copyImageLocation() const 0628 { 0629 return mViewer->copyImageLocation(); 0630 } 0631 0632 QAction *KMReaderWin::copyAction() const 0633 { 0634 return mViewer->copyAction(); 0635 } 0636 0637 QAction *KMReaderWin::viewSourceAction() const 0638 { 0639 return mViewer->viewSourceAction(); 0640 } 0641 0642 QAction *KMReaderWin::saveAsAction() const 0643 { 0644 return mViewer->saveAsAction(); 0645 } 0646 0647 QAction *KMReaderWin::findInMessageAction() const 0648 { 0649 return mViewer->findInMessageAction(); 0650 } 0651 0652 QAction *KMReaderWin::urlOpenAction() const 0653 { 0654 return mViewer->urlOpenAction(); 0655 } 0656 0657 QAction *KMReaderWin::urlSaveAsAction() const 0658 { 0659 return mUrlSaveAsAction; 0660 } 0661 0662 QAction *KMReaderWin::addUrlToBookmarkAction() const 0663 { 0664 return mAddUrlToBookmarkAction; 0665 } 0666 0667 void KMReaderWin::setPrinting(bool enable) 0668 { 0669 mViewer->setPrinting(enable); 0670 } 0671 0672 QAction *KMReaderWin::speakTextAction() const 0673 { 0674 return mViewer->speakTextAction(); 0675 } 0676 0677 QAction *KMReaderWin::shareTextAction() const 0678 { 0679 return mViewer->shareTextAction(); 0680 } 0681 0682 QAction *KMReaderWin::downloadImageToDiskAction() const 0683 { 0684 return mImageUrlSaveAsAction; 0685 } 0686 0687 QAction *KMReaderWin::openImageAction() const 0688 { 0689 return mOpenImageAction; 0690 } 0691 0692 void KMReaderWin::clear(bool force) 0693 { 0694 mViewer->clear(force ? MimeTreeParser::Force : MimeTreeParser::Delayed); 0695 } 0696 0697 void KMReaderWin::setMessage(const Akonadi::Item &item, MimeTreeParser::UpdateMode updateMode) 0698 { 0699 qCDebug(KMAIL_LOG) << Q_FUNC_INFO << parentWidget(); 0700 mViewer->setFolderIdentity(MailCommon::Util::folderIdentity(item)); 0701 mViewer->setMessageItem(item, updateMode); 0702 if (!item.hasAttribute<Akonadi::MDNStateAttribute>() 0703 || (item.hasAttribute<Akonadi::MDNStateAttribute>() 0704 && item.attribute<Akonadi::MDNStateAttribute>()->mdnState() == Akonadi::MDNStateAttribute::MDNStateUnknown)) { 0705 sendMdnInfo(item); 0706 } else { 0707 mViewer->mdnWarningAnimatedHide(); 0708 } 0709 } 0710 0711 void KMReaderWin::setMessage(const KMime::Message::Ptr &message) 0712 { 0713 mViewer->setFolderIdentity(0); 0714 mViewer->setMessage(message); 0715 } 0716 0717 QUrl KMReaderWin::urlClicked() const 0718 { 0719 return mViewer->urlClicked(); 0720 } 0721 0722 QUrl KMReaderWin::imageUrlClicked() const 0723 { 0724 return mViewer->imageUrlClicked(); 0725 } 0726 0727 void KMReaderWin::update(bool force) 0728 { 0729 mViewer->update(force ? MimeTreeParser::Force : MimeTreeParser::Delayed); 0730 } 0731 0732 void KMReaderWin::slotUrlClicked(const Akonadi::Item &item, const QUrl &url) 0733 { 0734 if (item.isValid() && item.parentCollection().isValid()) { 0735 const auto col = CommonKernel->collectionFromId(item.parentCollection().id()); 0736 QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(col, false); 0737 KMail::Util::handleClickedURL(url, fd, item.parentCollection()); 0738 return; 0739 } 0740 // No folder so we can't have identity and template. 0741 KMail::Util::handleClickedURL(url); 0742 } 0743 0744 void KMReaderWin::slotShowReader(KMime::Content *msgPart, bool html, const QString &encoding) 0745 { 0746 const MessageViewer::Viewer::DisplayFormatMessage format = html ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text; 0747 auto win = new KMReaderMainWin(msgPart, format, encoding); 0748 win->show(); 0749 } 0750 0751 void KMReaderWin::slotShowMessage(const KMime::Message::Ptr &message, const QString &encoding) 0752 { 0753 auto win = new KMReaderMainWin(); 0754 win->showMessage(encoding, message); 0755 win->show(); 0756 } 0757 0758 void KMReaderWin::slotDeleteMessage(const Akonadi::Item &item) 0759 { 0760 if (!item.isValid()) { 0761 return; 0762 } 0763 auto command = new KMTrashMsgCommand(item.parentCollection(), item, -1); 0764 command->start(); 0765 } 0766 0767 bool KMReaderWin::printSelectedText(bool preview) 0768 { 0769 const QString str = mViewer->selectedText(); 0770 if (str.isEmpty()) { 0771 return false; 0772 } 0773 auto composer = new ::MessageComposer::Composer; 0774 composer->textPart()->setCleanPlainText(str); 0775 composer->textPart()->setWrappedPlainText(str); 0776 auto messagePtr = messageItem().payload<KMime::Message::Ptr>(); 0777 composer->infoPart()->setFrom(messagePtr->from()->asUnicodeString()); 0778 composer->infoPart()->setTo(QStringList() << messagePtr->to()->asUnicodeString()); 0779 composer->infoPart()->setCc(QStringList() << messagePtr->cc()->asUnicodeString()); 0780 if (auto subject = messagePtr->subject(false)) { 0781 composer->infoPart()->setSubject(subject->asUnicodeString()); 0782 } 0783 composer->setProperty("preview", preview); 0784 connect(composer, &::MessageComposer::Composer::result, this, &KMReaderWin::slotPrintComposeResult); 0785 composer->start(); 0786 return true; 0787 } 0788 0789 void KMReaderWin::slotPrintComposeResult(KJob *job) 0790 { 0791 const bool preview = job->property("preview").toBool(); 0792 auto composer = qobject_cast<::MessageComposer::Composer *>(job); 0793 Q_ASSERT(composer); 0794 if (composer->error() == ::MessageComposer::Composer::NoError) { 0795 Q_ASSERT(composer->resultMessages().size() == 1); 0796 Akonadi::Item printItem; 0797 printItem.setPayload<KMime::Message::Ptr>(composer->resultMessages().constFirst()); 0798 Akonadi::MessageFlags::copyMessageFlags(*(composer->resultMessages().constFirst()), printItem); 0799 const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont(); 0800 const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(); 0801 0802 KMPrintCommandInfo commandInfo; 0803 commandInfo.mMsg = printItem; 0804 commandInfo.mHeaderStylePlugin = mViewer->headerStylePlugin(); 0805 commandInfo.mFormat = mViewer->displayFormatMessageOverwrite(); 0806 commandInfo.mHtmlLoadExtOverride = mViewer->htmlLoadExternal(); 0807 commandInfo.mPrintPreview = preview; 0808 commandInfo.mUseFixedFont = useFixedFont; 0809 commandInfo.mOverrideFont = overrideEncoding; 0810 commandInfo.mShowSignatureDetails = 0811 mViewer->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails(); 0812 commandInfo.mShowEncryptionDetails = 0813 mViewer->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails(); 0814 0815 auto command = new KMPrintCommand(this, commandInfo); 0816 command->start(); 0817 } else { 0818 if (static_cast<KIO::Job *>(job)->uiDelegate()) { 0819 static_cast<KIO::Job *>(job)->uiDelegate()->showErrorMessage(); 0820 } else { 0821 qCWarning(KMAIL_LOG) << "Composer for printing failed:" << composer->errorString(); 0822 } 0823 } 0824 } 0825 0826 void KMReaderWin::clearContactItem() 0827 { 0828 mSearchedContact = Akonadi::Item(); 0829 mSearchedAddress = KContacts::Addressee(); 0830 mLoadExternalReference->setChecked(false); 0831 mViewAsHtml->setChecked(false); 0832 } 0833 0834 void KMReaderWin::setContactItem(const Akonadi::Item &contact, const KContacts::Addressee &address) 0835 { 0836 mSearchedContact = contact; 0837 mSearchedAddress = address; 0838 updateHtmlActions(); 0839 } 0840 0841 void KMReaderWin::updateHtmlActions() 0842 { 0843 if (!mSearchedContact.isValid()) { 0844 mLoadExternalReference->setChecked(false); 0845 mViewAsHtml->setChecked(false); 0846 } else { 0847 const QStringList customs = mSearchedAddress.customs(); 0848 for (const QString &custom : customs) { 0849 if (custom.contains(QLatin1StringView("MailPreferedFormatting"))) { 0850 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailPreferedFormatting")); 0851 mViewAsHtml->setChecked(value == QLatin1StringView("HTML")); 0852 } else if (custom.contains(QLatin1StringView("MailAllowToRemoteContent"))) { 0853 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailAllowToRemoteContent")); 0854 mLoadExternalReference->setChecked((value == QLatin1StringView("TRUE"))); 0855 } 0856 } 0857 } 0858 } 0859 0860 void KMReaderWin::slotContactHtmlOptions() 0861 { 0862 const QUrl url = urlClicked(); 0863 if (url.isEmpty()) { 0864 return; 0865 } 0866 const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower(); 0867 0868 auto job = new Akonadi::AddEmailDisplayJob(emailString, mMainWindow, this); 0869 job->setMessageId(mViewer->messageItem().id()); 0870 connect(job, &Akonadi::AddEmailDisplayJob::contactUpdated, this, &KMReaderWin::slotContactHtmlPreferencesUpdated); 0871 job->setRemoteContent(mLoadExternalReference->isChecked()); 0872 job->setShowAsHTML(mViewAsHtml->isChecked()); 0873 job->setContact(mSearchedContact); 0874 job->start(); 0875 } 0876 0877 void KMReaderWin::slotContactHtmlPreferencesUpdated(const Akonadi::Item &contact, Akonadi::Item::Id id, bool showAsHTML, bool remoteContent) 0878 { 0879 Q_UNUSED(contact) 0880 if (mViewer->messageItem().id() == id) { 0881 mViewer->slotChangeDisplayMail(showAsHTML ? Viewer::Html : Viewer::Text, remoteContent); 0882 } 0883 } 0884 0885 void KMReaderWin::slotEditContact() 0886 { 0887 if (mSearchedContact.isValid()) { 0888 QPointer<Akonadi::ContactEditorDialog> dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::EditMode, this); 0889 connect(dlg.data(), &Akonadi::ContactEditorDialog::contactStored, this, &KMReaderWin::contactStored); 0890 connect(dlg.data(), &Akonadi::ContactEditorDialog::error, this, &KMReaderWin::slotContactEditorError); 0891 dlg->setContact(mSearchedContact); 0892 dlg->exec(); 0893 delete dlg; 0894 } 0895 } 0896 0897 void KMReaderWin::slotContactEditorError(const QString &error) 0898 { 0899 KMessageBox::error(this, i18n("Contact cannot be stored: %1", error), i18nc("@title:window", "Failed to store contact")); 0900 } 0901 0902 void KMReaderWin::contactStored(const Akonadi::Item &item) 0903 { 0904 if (item.hasPayload<KContacts::Addressee>()) { 0905 const auto contact = item.payload<KContacts::Addressee>(); 0906 setContactItem(item, contact); 0907 mViewer->slotChangeDisplayMail(mViewAsHtml->isChecked() ? Viewer::Html : Viewer::Text, mLoadExternalReference->isChecked()); 0908 } 0909 PimCommon::BroadcastStatus::instance()->setStatusMsg(i18n("Contact modified successfully")); 0910 } 0911 0912 QAction *KMReaderWin::saveMessageDisplayFormatAction() const 0913 { 0914 return mViewer->saveMessageDisplayFormatAction(); 0915 } 0916 0917 QAction *KMReaderWin::resetMessageDisplayFormatAction() const 0918 { 0919 return mViewer->resetMessageDisplayFormatAction(); 0920 } 0921 0922 QAction *KMReaderWin::editContactAction() const 0923 { 0924 return mEditContactAction; 0925 } 0926 0927 QAction *KMReaderWin::developmentToolsAction() const 0928 { 0929 return mViewer->developmentToolsAction(); 0930 } 0931 0932 QMenu *KMReaderWin::viewHtmlOption() const 0933 { 0934 return mViewHtmlOptions; 0935 } 0936 0937 QAction *KMReaderWin::shareImage() const 0938 { 0939 return mShareImage; 0940 } 0941 0942 QAction *KMReaderWin::addToExistingContactAction() const 0943 { 0944 return mAddEmailToExistingContactAction; 0945 } 0946 0947 void KMReaderWin::slotShareImage() 0948 { 0949 KMCommand *command = new KMShareImageCommand(imageUrlClicked(), this); 0950 command->start(); 0951 } 0952 0953 QList<QAction *> KMReaderWin::interceptorUrlActions(const WebEngineViewer::WebHitTestResult &result) const 0954 { 0955 return mViewer->interceptorUrlActions(result); 0956 } 0957 0958 void KMReaderWin::slotPrintingFinished() 0959 { 0960 if (mViewer->printingMode()) { 0961 deleteLater(); 0962 } 0963 } 0964 0965 void KMReaderWin::sendMdnInfo(const Akonadi::Item &item) 0966 { 0967 auto job = new MessageComposer::MDNWarningWidgetJob(this); 0968 job->setItem(item); 0969 connect(job, &MessageComposer::MDNWarningWidgetJob::showMdnInfo, this, &KMReaderWin::slotShowMdnInfo); 0970 if (!job->start()) { 0971 qCWarning(KMAIL_LOG) << "Impossible to start MDNWarningWidgetJob"; 0972 } 0973 } 0974 0975 void KMReaderWin::slotShowMdnInfo(const QPair<QString, bool> &mdnInfo) 0976 { 0977 mViewer->showMdnInformations(mdnInfo); 0978 } 0979 0980 void KMReaderWin::slotSendMdnResponse(MessageViewer::MDNWarningWidget::ResponseType type, KMime::MDN::SendingMode sendingMode) 0981 { 0982 MailCommon::MDNWarningJob::ResponseMDN response = MailCommon::MDNWarningJob::ResponseMDN::Unknown; 0983 switch (type) { 0984 case MessageViewer::MDNWarningWidget::ResponseType::Ignore: 0985 response = MailCommon::MDNWarningJob::ResponseMDN::MDNIgnore; 0986 break; 0987 case MessageViewer::MDNWarningWidget::ResponseType::Send: 0988 response = MailCommon::MDNWarningJob::ResponseMDN::Send; 0989 break; 0990 case MessageViewer::MDNWarningWidget::ResponseType::SendDeny: 0991 response = MailCommon::MDNWarningJob::ResponseMDN::Denied; 0992 break; 0993 } 0994 0995 auto job = new MailCommon::MDNWarningJob(KMKernel::self(), this); 0996 job->setItem(mViewer->messageItem()); 0997 job->setResponse(response); 0998 job->setSendingMode(sendingMode); 0999 job->start(); 1000 connect(job, &MDNWarningJob::finished, this, [this]() { 1001 mViewer->mdnWarningAnimatedHide(); 1002 }); 1003 } 1004 1005 void KMReaderWin::slotItemModified(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers) 1006 { 1007 if (mViewer->messageItem().id() == item.id()) { 1008 if (partIdentifiers.contains("MDNStateAttribute")) { 1009 mViewer->mdnWarningAnimatedHide(); 1010 } 1011 } 1012 } 1013 1014 void KMReaderWin::addImageMenuActions(QMenu *menu) 1015 { 1016 menu->addSeparator(); 1017 menu->addAction(copyImageLocation()); 1018 menu->addAction(downloadImageToDiskAction()); 1019 menu->addAction(shareImage()); 1020 menu->addAction(openImageAction()); 1021 } 1022 1023 #include "moc_kmreaderwin.cpp"