1 /*
2 This file is part of KMail, the KDE mail client.
3 SPDX-FileCopyrightText: 1997 Markus Wuebben <markus.wuebben@kde.org>
4 SPDX-FileCopyrightText: 2009-2021 Laurent Montel <montel@kde.org>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 // define this to copy all html that is written to the readerwindow to
10 // filehtmlwriter.out in the current working directory
11 #include "kmreaderwin.h"
12
13 #include "dialog/addemailtoexistingcontactdialog.h"
14 #include "job/addemailtoexistingcontactjob.h"
15 #include "kmmainwidget.h"
16 #include "kmreadermainwin.h"
17 #include "settings/kmailsettings.h"
18 #include <MailCommon/MailKernel>
19
20 #include "kmail-version.h"
21 #include "kmcommands.h"
22 #include <Akonadi/Contact/AddEmailAddressJob>
23 #include <Akonadi/Contact/AddEmailDisplayJob>
24 #include <Akonadi/Contact/OpenEmailAddressJob>
25 #include <KEmailAddress>
26 #include <MailCommon/MailUtil>
27 #include <MailCommon/SendMdnHandler>
28 #include <MessageViewer/CSSHelper>
29 #include <MessageViewer/HeaderStrategy>
30 #include <MessageViewer/MarkMessageReadHandler>
31 #include <MessageViewer/MessageViewerSettings>
32 #include <PimCommon/BroadcastStatus>
33 #include <QVBoxLayout>
34 using MessageViewer::CSSHelper;
35 #include "util.h"
36 #include <KMime/MDN>
37 #include <MessageCore/StringUtil>
38 #include <QCryptographicHash>
39 #include <akonadi/kmime/messageflags.h>
40
41 using namespace MessageViewer;
42 #include <MessageCore/MessageCoreSettings>
43
44 #include <MessageComposer/Composer>
45 #include <MessageComposer/InfoPart>
46 #include <MessageComposer/MessageSender>
47 #include <MessageComposer/TextPart>
48 #include <MessageViewer/AttachmentStrategy>
49
50 #include <KIO/JobUiDelegate>
51
52 #include <Akonadi/Contact/ContactEditorDialog>
53
54 #include "kmail_debug.h"
55 #include <KActionCollection>
56 #include <KLocalizedString>
57 #include <KMessageBox>
58 #include <KToggleAction>
59 #include <QAction>
60 #include <QMenu>
61
62 #include <QClipboard>
63
64 // X headers...
65 #undef Never
66 #undef Always
67
68 #include <MailCommon/MailUtil>
69
70 using namespace KMail;
71 using namespace MailCommon;
72
KMReaderWin(QWidget * aParent,QWidget * mainWindow,KActionCollection * actionCollection)73 KMReaderWin::KMReaderWin(QWidget *aParent, QWidget *mainWindow, KActionCollection *actionCollection)
74 : QWidget(aParent)
75 , mMainWindow(mainWindow)
76 , mActionCollection(actionCollection)
77 {
78 createActions();
79 auto vlay = new QVBoxLayout(this);
80 vlay->setContentsMargins(0, 4, 0, 0);
81 mViewer = new Viewer(this, mainWindow, mActionCollection);
82 mViewer->setIdentityManager(kmkernel->identityManager());
83 connect(mViewer, qOverload<const Akonadi::Item &, const QUrl &>(&Viewer::urlClicked), this, &KMReaderWin::slotUrlClicked);
84 connect(mViewer,
85 &Viewer::requestConfigSync,
86 kmkernel,
87 &KMKernel::slotRequestConfigSync,
88 Qt::QueuedConnection); // happens anyway on shutdown, so we can skip it there with using a queued connection
89 connect(mViewer, &Viewer::makeResourceOnline, kmkernel, &KMKernel::makeResourceOnline);
90 connect(mViewer, &MessageViewer::Viewer::showReader, this, &KMReaderWin::slotShowReader);
91 connect(mViewer, &MessageViewer::Viewer::showMessage, this, &KMReaderWin::slotShowMessage);
92 connect(mViewer, &MessageViewer::Viewer::showStatusBarMessage, this, &KMReaderWin::showStatusBarMessage);
93 connect(mViewer, &MessageViewer::Viewer::printingFinished, this, &KMReaderWin::slotPrintingFinished);
94 connect(mViewer, &MessageViewer::Viewer::zoomChanged, this, &KMReaderWin::zoomChanged);
95 connect(mViewer, qOverload<const Akonadi::Item &>(&Viewer::deleteMessage), this, &KMReaderWin::slotDeleteMessage);
96 connect(mViewer, &MessageViewer::Viewer::showNextMessage, this, &KMReaderWin::showNextMessage);
97 connect(mViewer, &MessageViewer::Viewer::showPreviousMessage, this, &KMReaderWin::showPreviousMessage);
98
99 mViewer->addMessageLoadedHandler(new MessageViewer::MarkMessageReadHandler(this));
100 mViewer->addMessageLoadedHandler(new MailCommon::SendMdnHandler(kmkernel, this));
101
102 vlay->addWidget(mViewer);
103 readConfig();
104 }
105
createActions()106 void KMReaderWin::createActions()
107 {
108 KActionCollection *ac = mActionCollection;
109 if (!ac) {
110 return;
111 }
112 //
113 // Message Menu
114 //
115 // new message to
116 mMailToComposeAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("New Message To..."), this);
117 ac->addAction(QStringLiteral("mail_new"), mMailToComposeAction);
118 ac->setShortcutsConfigurable(mMailToComposeAction, false);
119 connect(mMailToComposeAction, &QAction::triggered, this, &KMReaderWin::slotMailtoCompose);
120
121 // reply to
122 mMailToReplyAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply To..."), this);
123 ac->addAction(QStringLiteral("mailto_reply"), mMailToReplyAction);
124 ac->setShortcutsConfigurable(mMailToReplyAction, false);
125 connect(mMailToReplyAction, &QAction::triggered, this, &KMReaderWin::slotMailtoReply);
126
127 // forward to
128 mMailToForwardAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18n("Forward To..."), this);
129 ac->setShortcutsConfigurable(mMailToForwardAction, false);
130 ac->addAction(QStringLiteral("mailto_forward"), mMailToForwardAction);
131 connect(mMailToForwardAction, &QAction::triggered, this, &KMReaderWin::slotMailtoForward);
132
133 // add to addressbook
134 mAddAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Address Book"), this);
135 ac->setShortcutsConfigurable(mAddAddrBookAction, false);
136 ac->addAction(QStringLiteral("add_addr_book"), mAddAddrBookAction);
137 connect(mAddAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoAddAddrBook);
138
139 mAddEmailToExistingContactAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Existing Contact"), this);
140 ac->setShortcutsConfigurable(mAddEmailToExistingContactAction, false);
141 ac->addAction(QStringLiteral("add_to_existing_contact"), mAddAddrBookAction);
142 connect(mAddEmailToExistingContactAction, &QAction::triggered, this, &KMReaderWin::slotMailToAddToExistingContact);
143
144 // open in addressbook
145 mOpenAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Open in Address Book"), this);
146 ac->setShortcutsConfigurable(mOpenAddrBookAction, false);
147 ac->addAction(QStringLiteral("openin_addr_book"), mOpenAddrBookAction);
148 connect(mOpenAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoOpenAddrBook);
149 // bookmark message
150 mAddBookmarksAction = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Bookmark This Link"), this);
151 ac->setShortcutsConfigurable(mAddBookmarksAction, false);
152 ac->addAction(QStringLiteral("add_bookmarks"), mAddBookmarksAction);
153 connect(mAddBookmarksAction, &QAction::triggered, this, &KMReaderWin::slotAddBookmarks);
154
155 mEditContactAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Edit contact..."), this);
156 ac->setShortcutsConfigurable(mEditContactAction, false);
157 ac->addAction(QStringLiteral("edit_contact"), mOpenAddrBookAction);
158 connect(mEditContactAction, &QAction::triggered, this, &KMReaderWin::slotEditContact);
159
160 // save URL as
161 mUrlSaveAsAction = new QAction(i18n("Save Link As..."), this);
162 ac->addAction(QStringLiteral("saveas_url"), mUrlSaveAsAction);
163 ac->setShortcutsConfigurable(mUrlSaveAsAction, false);
164 connect(mUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotUrlSave);
165
166 // find text
167 auto action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("&Find in Message..."), this);
168 ac->addAction(QStringLiteral("find_in_messages"), action);
169 connect(action, &QAction::triggered, this, &KMReaderWin::slotFind);
170 ac->setDefaultShortcut(action, KStandardShortcut::find().first());
171
172 // save Image On Disk
173 mImageUrlSaveAsAction = new QAction(i18n("Save Image On Disk..."), this);
174 ac->addAction(QStringLiteral("saveas_imageurl"), mImageUrlSaveAsAction);
175 ac->setShortcutsConfigurable(mImageUrlSaveAsAction, false);
176 connect(mImageUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotSaveImageOnDisk);
177
178 // View html options
179 mViewHtmlOptions = new QMenu(i18n("Show HTML Format"), this);
180 mViewAsHtml = new QAction(i18n("Show HTML format when mail comes from this contact"), mViewHtmlOptions);
181 ac->setShortcutsConfigurable(mViewAsHtml, false);
182 connect(mViewAsHtml, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions);
183 mViewAsHtml->setCheckable(true);
184 mViewHtmlOptions->addAction(mViewAsHtml);
185
186 mLoadExternalReference = new QAction(i18n("Load external reference when mail comes for this contact"), mViewHtmlOptions);
187 ac->setShortcutsConfigurable(mLoadExternalReference, false);
188 connect(mLoadExternalReference, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions);
189 mLoadExternalReference->setCheckable(true);
190 mViewHtmlOptions->addAction(mLoadExternalReference);
191
192 mShareImage = new QAction(i18n("Share image..."), this);
193 ac->addAction(QStringLiteral("share_imageurl"), mShareImage);
194 ac->setShortcutsConfigurable(mShareImage, false);
195 connect(mShareImage, &QAction::triggered, this, &KMReaderWin::slotShareImage);
196 }
197
setUseFixedFont(bool useFixedFont)198 void KMReaderWin::setUseFixedFont(bool useFixedFont)
199 {
200 mViewer->setUseFixedFont(useFixedFont);
201 }
202
viewer() const203 Viewer *KMReaderWin::viewer() const
204 {
205 return mViewer;
206 }
207
isFixedFont() const208 bool KMReaderWin::isFixedFont() const
209 {
210 return mViewer->isFixedFont();
211 }
212
213 KMReaderWin::~KMReaderWin() = default;
214
readConfig()215 void KMReaderWin::readConfig()
216 {
217 mViewer->readConfig();
218 }
219
setAttachmentStrategy(const MessageViewer::AttachmentStrategy * strategy)220 void KMReaderWin::setAttachmentStrategy(const MessageViewer::AttachmentStrategy *strategy)
221 {
222 mViewer->setAttachmentStrategy(strategy);
223 }
224
setOverrideEncoding(const QString & encoding)225 void KMReaderWin::setOverrideEncoding(const QString &encoding)
226 {
227 mViewer->setOverrideEncoding(encoding);
228 }
229
clearCache()230 void KMReaderWin::clearCache()
231 {
232 clear();
233 }
234
updateShowMultiMessagesButton(bool enablePreviousButton,bool enableNextButton)235 void KMReaderWin::updateShowMultiMessagesButton(bool enablePreviousButton, bool enableNextButton)
236 {
237 mViewer->updateShowMultiMessagesButton(enablePreviousButton, enableNextButton);
238 }
239
hasMultiMessages(bool multi)240 void KMReaderWin::hasMultiMessages(bool multi)
241 {
242 mViewer->hasMultiMessages(multi);
243 }
244
245 // enter items for the "Important changes" list here:
246 static const char *const kmailChanges[] = {
247 I18N_NOOP("KMail is now based on the Akonadi Personal Information Management framework, which brings many "
248 "changes all around.")};
249 static const int numKMailChanges = sizeof kmailChanges / sizeof *kmailChanges;
250
251 // enter items for the "new features" list here, so the main body of
252 // the welcome page can be left untouched (probably much easier for
253 // the translators). Note that the <li>...</li> tags are added
254 // automatically below:
255 static const char *const kmailNewFeatures[] = {
256 I18N_NOOP("Push email (IMAP IDLE)"),
257 I18N_NOOP("Improved searches"),
258 I18N_NOOP("Support for adding notes (annotations) to mails"),
259 I18N_NOOP("Less GUI freezes, mail checks happen in the background"),
260 I18N_NOOP("Plugins support"),
261 I18N_NOOP("New HTML renderer (QtWebEngine)"),
262 I18N_NOOP("Added Check for Phishing URL"),
263 I18N_NOOP("Added Check DKIM"),
264 I18N_NOOP("Added Markdown Editing Support"),
265 I18N_NOOP("Added Grammar Plugin Support"),
266 I18N_NOOP("Added etesync resource support"),
267 I18N_NOOP("Added Microsoft Exchange Web Services resource support"),
268 };
269 static const int numKMailNewFeatures = sizeof kmailNewFeatures / sizeof *kmailNewFeatures;
270
271 // static
newFeaturesMD5()272 QString KMReaderWin::newFeaturesMD5()
273 {
274 QByteArray str;
275 for (int i = 0; i < numKMailChanges; ++i) {
276 str += kmailChanges[i];
277 }
278 for (int i = 0; i < numKMailNewFeatures; ++i) {
279 str += kmailNewFeatures[i];
280 }
281 QCryptographicHash md5(QCryptographicHash::Md5);
282 md5.addData(str);
283 return QLatin1String(md5.result().toBase64());
284 }
285
displaySplashPage(const QString & templateName,const QVariantHash & _data)286 void KMReaderWin::displaySplashPage(const QString &templateName, const QVariantHash &_data)
287 {
288 QVariantHash data = _data;
289 if (!data.contains(QStringLiteral("icon"))) {
290 data[QStringLiteral("icon")] = QStringLiteral("kmail");
291 }
292 if (!data.contains(QStringLiteral("name"))) {
293 data[QStringLiteral("name")] = i18n("KMail");
294 }
295 if (!data.contains(QStringLiteral("subtitle"))) {
296 data[QStringLiteral("subtitle")] = i18n("The KDE Mail Client");
297 }
298
299 mViewer->displaySplashPage(templateName, data, QByteArrayLiteral("kmail"));
300 }
301
displayBusyPage()302 void KMReaderWin::displayBusyPage()
303 {
304 displaySplashPage(QStringLiteral("status.html"),
305 {{QStringLiteral("title"), i18n("Retrieving Folder Contents")}, {QStringLiteral("subtext"), i18n("Please wait . . .")}});
306 }
307
displayOfflinePage()308 void KMReaderWin::displayOfflinePage()
309 {
310 displaySplashPage(QStringLiteral("status.html"),
311 {{QStringLiteral("title"), i18n("Offline")},
312 {QStringLiteral("subtext"),
313 i18n("KMail is currently in offline mode. "
314 "Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p>")}});
315 }
316
displayResourceOfflinePage()317 void KMReaderWin::displayResourceOfflinePage()
318 {
319 displaySplashPage(QStringLiteral("status.html"),
320 {{QStringLiteral("title"), i18n("Offline")},
321 {QStringLiteral("subtext"),
322 i18n("Account is currently in offline mode. "
323 "Click <a href=\"kmail:goResourceOnline\">here</a> to go online . . .</p>")}});
324 }
325
displayAboutPage()326 void KMReaderWin::displayAboutPage()
327 {
328 QVariantHash data;
329 data[QStringLiteral("version")] = QStringLiteral(KDEPIM_VERSION);
330 data[QStringLiteral("firstStart")] = kmkernel->firstStart();
331
332 QVariantList features;
333 features.reserve(numKMailNewFeatures);
334 for (int i = 0; i < numKMailNewFeatures; ++i) {
335 features.push_back(i18n(kmailNewFeatures[i]));
336 }
337 data[QStringLiteral("newFeatures")] = features;
338
339 QVariantList changes;
340 changes.reserve(numKMailChanges);
341 for (int i = 0; i < numKMailChanges; ++i) {
342 features.push_back(i18n(kmailChanges[i]));
343 }
344 data[QStringLiteral("importantChanges")] = changes;
345
346 displaySplashPage(QStringLiteral(":/about/introduction_kmail.html"), data);
347 }
348
slotFind()349 void KMReaderWin::slotFind()
350 {
351 mViewer->slotFind();
352 }
353
slotCopySelectedText()354 void KMReaderWin::slotCopySelectedText()
355 {
356 QString selection = mViewer->selectedText();
357 selection.replace(QChar::Nbsp, QLatin1Char(' '));
358 QApplication::clipboard()->setText(selection);
359 }
360
setMsgPart(KMime::Content * aMsgPart)361 void KMReaderWin::setMsgPart(KMime::Content *aMsgPart)
362 {
363 mViewer->setMessagePart(aMsgPart);
364 }
365
copyText() const366 QString KMReaderWin::copyText() const
367 {
368 return mViewer->selectedText();
369 }
370
displayFormatMessageOverwrite() const371 MessageViewer::Viewer::DisplayFormatMessage KMReaderWin::displayFormatMessageOverwrite() const
372 {
373 return mViewer->displayFormatMessageOverwrite();
374 }
375
setPrintElementBackground(bool printElementBackground)376 void KMReaderWin::setPrintElementBackground(bool printElementBackground)
377 {
378 mViewer->setPrintElementBackground(printElementBackground);
379 }
380
setDisplayFormatMessageOverwrite(MessageViewer::Viewer::DisplayFormatMessage format)381 void KMReaderWin::setDisplayFormatMessageOverwrite(MessageViewer::Viewer::DisplayFormatMessage format)
382 {
383 mViewer->setDisplayFormatMessageOverwrite(format);
384 }
385
setHtmlLoadExtDefault(bool loadExtDefault)386 void KMReaderWin::setHtmlLoadExtDefault(bool loadExtDefault)
387 {
388 mViewer->setHtmlLoadExtDefault(loadExtDefault);
389 }
390
setHtmlLoadExtOverride(bool loadExtOverride)391 void KMReaderWin::setHtmlLoadExtOverride(bool loadExtOverride)
392 {
393 mViewer->setHtmlLoadExtOverride(loadExtOverride);
394 }
395
htmlMail() const396 bool KMReaderWin::htmlMail() const
397 {
398 return mViewer->htmlMail();
399 }
400
htmlLoadExternal()401 bool KMReaderWin::htmlLoadExternal()
402 {
403 return mViewer->htmlLoadExternal();
404 }
405
messageItem() const406 Akonadi::Item KMReaderWin::messageItem() const
407 {
408 return mViewer->messageItem();
409 }
410
mainWindow() const411 QWidget *KMReaderWin::mainWindow() const
412 {
413 return mMainWindow;
414 }
415
slotMailtoCompose()416 void KMReaderWin::slotMailtoCompose()
417 {
418 KMCommand *command = new KMMailtoComposeCommand(urlClicked(), messageItem());
419 command->start();
420 }
421
slotMailtoForward()422 void KMReaderWin::slotMailtoForward()
423 {
424 KMCommand *command = new KMMailtoForwardCommand(mMainWindow, urlClicked(), messageItem());
425 command->start();
426 }
427
slotMailtoAddAddrBook()428 void KMReaderWin::slotMailtoAddAddrBook()
429 {
430 const QUrl url = urlClicked();
431 if (url.isEmpty()) {
432 return;
433 }
434 const QString emailString = KEmailAddress::decodeMailtoUrl(url);
435
436 auto job = new Akonadi::AddEmailAddressJob(emailString, mMainWindow, this);
437 job->setInteractive(true);
438 connect(job, &Akonadi::AddEmailAddressJob::successMessage, this, [](const QString &message) {
439 PimCommon::BroadcastStatus::instance()->setStatusMsg(message);
440 });
441 job->start();
442 }
443
slotMailToAddToExistingContact()444 void KMReaderWin::slotMailToAddToExistingContact()
445 {
446 const QUrl url = urlClicked();
447 if (url.isEmpty()) {
448 return;
449 }
450 const QString emailString = KEmailAddress::decodeMailtoUrl(url);
451 QPointer<AddEmailToExistingContactDialog> dlg = new AddEmailToExistingContactDialog(this);
452 if (dlg->exec()) {
453 Akonadi::Item item = dlg->selectedContact();
454 if (item.isValid()) {
455 auto job = new AddEmailToExistingContactJob(item, emailString, this);
456 job->start();
457 }
458 }
459 delete dlg;
460 }
461
slotMailtoOpenAddrBook()462 void KMReaderWin::slotMailtoOpenAddrBook()
463 {
464 const QUrl url = urlClicked();
465 if (url.isEmpty()) {
466 return;
467 }
468 const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower();
469
470 auto job = new Akonadi::OpenEmailAddressJob(emailString, mMainWindow, this);
471 job->start();
472 }
473
slotAddBookmarks()474 void KMReaderWin::slotAddBookmarks()
475 {
476 const QUrl url = urlClicked();
477 if (url.isEmpty()) {
478 return;
479 }
480 KMCommand *command = new KMAddBookmarksCommand(url, this);
481 command->start();
482 }
483
slotUrlSave()484 void KMReaderWin::slotUrlSave()
485 {
486 const QUrl url = urlClicked();
487 if (url.isEmpty()) {
488 return;
489 }
490 KMCommand *command = new KMUrlSaveCommand(url, mMainWindow);
491 command->start();
492 }
493
slotSaveImageOnDisk()494 void KMReaderWin::slotSaveImageOnDisk()
495 {
496 const QUrl url = imageUrlClicked();
497 if (url.isEmpty()) {
498 return;
499 }
500 KMCommand *command = new KMUrlSaveCommand(url, mMainWindow);
501 command->start();
502 }
503
slotMailtoReply()504 void KMReaderWin::slotMailtoReply()
505 {
506 auto command = new KMMailtoReplyCommand(mMainWindow, urlClicked(), messageItem(), copyText());
507 command->setReplyAsHtml(htmlMail());
508 command->start();
509 }
510
cssHelper() const511 CSSHelper *KMReaderWin::cssHelper() const
512 {
513 return mViewer->cssHelper();
514 }
515
htmlLoadExtOverride() const516 bool KMReaderWin::htmlLoadExtOverride() const
517 {
518 return mViewer->htmlLoadExtOverride();
519 }
520
setDecryptMessageOverwrite(bool overwrite)521 void KMReaderWin::setDecryptMessageOverwrite(bool overwrite)
522 {
523 mViewer->setDecryptMessageOverwrite(overwrite);
524 }
525
attachmentStrategy() const526 const MessageViewer::AttachmentStrategy *KMReaderWin::attachmentStrategy() const
527 {
528 return mViewer->attachmentStrategy();
529 }
530
overrideEncoding() const531 QString KMReaderWin::overrideEncoding() const
532 {
533 return mViewer->overrideEncoding();
534 }
535
toggleFixFontAction() const536 KToggleAction *KMReaderWin::toggleFixFontAction() const
537 {
538 return mViewer->toggleFixFontAction();
539 }
540
mailToComposeAction() const541 QAction *KMReaderWin::mailToComposeAction() const
542 {
543 return mMailToComposeAction;
544 }
545
mailToReplyAction() const546 QAction *KMReaderWin::mailToReplyAction() const
547 {
548 return mMailToReplyAction;
549 }
550
mailToForwardAction() const551 QAction *KMReaderWin::mailToForwardAction() const
552 {
553 return mMailToForwardAction;
554 }
555
addAddrBookAction() const556 QAction *KMReaderWin::addAddrBookAction() const
557 {
558 return mAddAddrBookAction;
559 }
560
openAddrBookAction() const561 QAction *KMReaderWin::openAddrBookAction() const
562 {
563 return mOpenAddrBookAction;
564 }
565
mimePartTreeIsEmpty() const566 bool KMReaderWin::mimePartTreeIsEmpty() const
567 {
568 return mViewer->mimePartTreeIsEmpty();
569 }
570
toggleMimePartTreeAction() const571 QAction *KMReaderWin::toggleMimePartTreeAction() const
572 {
573 return mViewer->toggleMimePartTreeAction();
574 }
575
shareServiceUrlMenu() const576 KActionMenu *KMReaderWin::shareServiceUrlMenu() const
577 {
578 return mViewer->shareServiceUrlMenu();
579 }
580
dkimViewerMenu() const581 DKIMViewerMenu *KMReaderWin::dkimViewerMenu() const
582 {
583 return mViewer->dkimViewerMenu();
584 }
585
remoteContentMenu() const586 RemoteContentMenu *KMReaderWin::remoteContentMenu() const
587 {
588 return mViewer->remoteContentMenu();
589 }
590
viewerPluginActionList(ViewerPluginInterface::SpecificFeatureTypes features)591 QList<QAction *> KMReaderWin::viewerPluginActionList(ViewerPluginInterface::SpecificFeatureTypes features)
592 {
593 return mViewer->viewerPluginActionList(features);
594 }
595
selectAllAction() const596 QAction *KMReaderWin::selectAllAction() const
597 {
598 return mViewer->selectAllAction();
599 }
600
copyURLAction() const601 QAction *KMReaderWin::copyURLAction() const
602 {
603 return mViewer->copyURLAction();
604 }
605
copyImageLocation() const606 QAction *KMReaderWin::copyImageLocation() const
607 {
608 return mViewer->copyImageLocation();
609 }
610
copyAction() const611 QAction *KMReaderWin::copyAction() const
612 {
613 return mViewer->copyAction();
614 }
615
viewSourceAction() const616 QAction *KMReaderWin::viewSourceAction() const
617 {
618 return mViewer->viewSourceAction();
619 }
620
saveAsAction() const621 QAction *KMReaderWin::saveAsAction() const
622 {
623 return mViewer->saveAsAction();
624 }
625
findInMessageAction() const626 QAction *KMReaderWin::findInMessageAction() const
627 {
628 return mViewer->findInMessageAction();
629 }
630
urlOpenAction() const631 QAction *KMReaderWin::urlOpenAction() const
632 {
633 return mViewer->urlOpenAction();
634 }
635
urlSaveAsAction() const636 QAction *KMReaderWin::urlSaveAsAction() const
637 {
638 return mUrlSaveAsAction;
639 }
640
addBookmarksAction() const641 QAction *KMReaderWin::addBookmarksAction() const
642 {
643 return mAddBookmarksAction;
644 }
645
setPrinting(bool enable)646 void KMReaderWin::setPrinting(bool enable)
647 {
648 mViewer->setPrinting(enable);
649 }
650
speakTextAction() const651 QAction *KMReaderWin::speakTextAction() const
652 {
653 return mViewer->speakTextAction();
654 }
655
shareTextAction() const656 QAction *KMReaderWin::shareTextAction() const
657 {
658 return mViewer->shareTextAction();
659 }
660
downloadImageToDiskAction() const661 QAction *KMReaderWin::downloadImageToDiskAction() const
662 {
663 return mImageUrlSaveAsAction;
664 }
665
clear(bool force)666 void KMReaderWin::clear(bool force)
667 {
668 mViewer->clear(force ? MimeTreeParser::Force : MimeTreeParser::Delayed);
669 }
670
setMessage(const Akonadi::Item & item,MimeTreeParser::UpdateMode updateMode)671 void KMReaderWin::setMessage(const Akonadi::Item &item, MimeTreeParser::UpdateMode updateMode)
672 {
673 qCDebug(KMAIL_LOG) << Q_FUNC_INFO << parentWidget();
674 mViewer->setFolderIdentity(MailCommon::Util::folderIdentity(item));
675 mViewer->setMessageItem(item, updateMode);
676 }
677
setMessage(const KMime::Message::Ptr & message)678 void KMReaderWin::setMessage(const KMime::Message::Ptr &message)
679 {
680 mViewer->setFolderIdentity(0);
681 mViewer->setMessage(message);
682 }
683
urlClicked() const684 QUrl KMReaderWin::urlClicked() const
685 {
686 return mViewer->urlClicked();
687 }
688
imageUrlClicked() const689 QUrl KMReaderWin::imageUrlClicked() const
690 {
691 return mViewer->imageUrlClicked();
692 }
693
update(bool force)694 void KMReaderWin::update(bool force)
695 {
696 mViewer->update(force ? MimeTreeParser::Force : MimeTreeParser::Delayed);
697 }
698
slotUrlClicked(const Akonadi::Item & item,const QUrl & url)699 void KMReaderWin::slotUrlClicked(const Akonadi::Item &item, const QUrl &url)
700 {
701 if (item.isValid() && item.parentCollection().isValid()) {
702 const auto col = CommonKernel->collectionFromId(item.parentCollection().id());
703 QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(col, false);
704 KMail::Util::handleClickedURL(url, fd, item.parentCollection());
705 return;
706 }
707 // No folder so we can't have identity and template.
708 KMail::Util::handleClickedURL(url);
709 }
710
slotShowReader(KMime::Content * msgPart,bool html,const QString & encoding)711 void KMReaderWin::slotShowReader(KMime::Content *msgPart, bool html, const QString &encoding)
712 {
713 const MessageViewer::Viewer::DisplayFormatMessage format = html ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text;
714 auto win = new KMReaderMainWin(msgPart, format, encoding);
715 win->show();
716 }
717
slotShowMessage(const KMime::Message::Ptr & message,const QString & encoding)718 void KMReaderWin::slotShowMessage(const KMime::Message::Ptr &message, const QString &encoding)
719 {
720 auto win = new KMReaderMainWin();
721 win->showMessage(encoding, message);
722 win->show();
723 }
724
slotDeleteMessage(const Akonadi::Item & item)725 void KMReaderWin::slotDeleteMessage(const Akonadi::Item &item)
726 {
727 if (!item.isValid()) {
728 return;
729 }
730 auto command = new KMTrashMsgCommand(item.parentCollection(), item, -1);
731 command->start();
732 }
733
printSelectedText(bool preview)734 bool KMReaderWin::printSelectedText(bool preview)
735 {
736 const QString str = mViewer->selectedText();
737 if (str.isEmpty()) {
738 return false;
739 }
740 auto composer = new ::MessageComposer::Composer;
741 composer->textPart()->setCleanPlainText(str);
742 composer->textPart()->setWrappedPlainText(str);
743 auto messagePtr = messageItem().payload<KMime::Message::Ptr>();
744 composer->infoPart()->setFrom(messagePtr->from()->asUnicodeString());
745 composer->infoPart()->setTo(QStringList() << messagePtr->to()->asUnicodeString());
746 composer->infoPart()->setCc(QStringList() << messagePtr->cc()->asUnicodeString());
747 composer->infoPart()->setSubject(messagePtr->subject()->asUnicodeString());
748 composer->setProperty("preview", preview);
749 connect(composer, &::MessageComposer::Composer::result, this, &KMReaderWin::slotPrintComposeResult);
750 composer->start();
751 return true;
752 }
753
slotPrintComposeResult(KJob * job)754 void KMReaderWin::slotPrintComposeResult(KJob *job)
755 {
756 const bool preview = job->property("preview").toBool();
757 auto composer = qobject_cast<::MessageComposer::Composer *>(job);
758 Q_ASSERT(composer);
759 if (composer->error() == ::MessageComposer::Composer::NoError) {
760 Q_ASSERT(composer->resultMessages().size() == 1);
761 Akonadi::Item printItem;
762 printItem.setPayload<KMime::Message::Ptr>(composer->resultMessages().constFirst());
763 Akonadi::MessageFlags::copyMessageFlags(*(composer->resultMessages().constFirst()), printItem);
764 const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont();
765 const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
766
767 KMPrintCommandInfo commandInfo;
768 commandInfo.mMsg = printItem;
769 commandInfo.mHeaderStylePlugin = mViewer->headerStylePlugin();
770 commandInfo.mFormat = mViewer->displayFormatMessageOverwrite();
771 commandInfo.mHtmlLoadExtOverride = mViewer->htmlLoadExternal();
772 commandInfo.mPrintPreview = preview;
773 commandInfo.mUseFixedFont = useFixedFont;
774 commandInfo.mOverrideFont = overrideEncoding;
775 commandInfo.mShowSignatureDetails =
776 mViewer->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
777 commandInfo.mShowEncryptionDetails =
778 mViewer->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
779
780 auto command = new KMPrintCommand(this, commandInfo);
781 command->start();
782 } else {
783 if (static_cast<KIO::Job *>(job)->uiDelegate()) {
784 static_cast<KIO::Job *>(job)->uiDelegate()->showErrorMessage();
785 } else {
786 qCWarning(KMAIL_LOG) << "Composer for printing failed:" << composer->errorString();
787 }
788 }
789 }
790
clearContactItem()791 void KMReaderWin::clearContactItem()
792 {
793 mSearchedContact = Akonadi::Item();
794 mSearchedAddress = KContacts::Addressee();
795 mLoadExternalReference->setChecked(false);
796 mViewAsHtml->setChecked(false);
797 }
798
setContactItem(const Akonadi::Item & contact,const KContacts::Addressee & address)799 void KMReaderWin::setContactItem(const Akonadi::Item &contact, const KContacts::Addressee &address)
800 {
801 mSearchedContact = contact;
802 mSearchedAddress = address;
803 updateHtmlActions();
804 }
805
updateHtmlActions()806 void KMReaderWin::updateHtmlActions()
807 {
808 if (!mSearchedContact.isValid()) {
809 mLoadExternalReference->setChecked(false);
810 mViewAsHtml->setChecked(false);
811 } else {
812 const QStringList customs = mSearchedAddress.customs();
813 for (const QString &custom : customs) {
814 if (custom.contains(QLatin1String("MailPreferedFormatting"))) {
815 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailPreferedFormatting"));
816 mViewAsHtml->setChecked(value == QLatin1String("HTML"));
817 } else if (custom.contains(QLatin1String("MailAllowToRemoteContent"))) {
818 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailAllowToRemoteContent"));
819 mLoadExternalReference->setChecked((value == QLatin1String("TRUE")));
820 }
821 }
822 }
823 }
824
slotContactHtmlOptions()825 void KMReaderWin::slotContactHtmlOptions()
826 {
827 const QUrl url = urlClicked();
828 if (url.isEmpty()) {
829 return;
830 }
831 const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower();
832
833 auto job = new Akonadi::AddEmailDisplayJob(emailString, mMainWindow, this);
834 job->setMessageId(mViewer->messageItem().id());
835 connect(job, &Akonadi::AddEmailDisplayJob::contactUpdated, this, &KMReaderWin::slotContactHtmlPreferencesUpdated);
836 job->setRemoteContent(mLoadExternalReference->isChecked());
837 job->setShowAsHTML(mViewAsHtml->isChecked());
838 job->setContact(mSearchedContact);
839 job->start();
840 }
841
slotContactHtmlPreferencesUpdated(const Akonadi::Item & contact,Akonadi::Item::Id id,bool showAsHTML,bool remoteContent)842 void KMReaderWin::slotContactHtmlPreferencesUpdated(const Akonadi::Item &contact, Akonadi::Item::Id id, bool showAsHTML, bool remoteContent)
843 {
844 Q_UNUSED(contact)
845 if (mViewer->messageItem().id() == id) {
846 mViewer->slotChangeDisplayMail(showAsHTML ? Viewer::Html : Viewer::Text, remoteContent);
847 }
848 }
849
slotEditContact()850 void KMReaderWin::slotEditContact()
851 {
852 if (mSearchedContact.isValid()) {
853 QPointer<Akonadi::ContactEditorDialog> dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::EditMode, this);
854 connect(dlg.data(), &Akonadi::ContactEditorDialog::contactStored, this, &KMReaderWin::contactStored);
855 connect(dlg.data(), &Akonadi::ContactEditorDialog::error, this, &KMReaderWin::slotContactEditorError);
856 dlg->setContact(mSearchedContact);
857 dlg->exec();
858 delete dlg;
859 }
860 }
861
slotContactEditorError(const QString & error)862 void KMReaderWin::slotContactEditorError(const QString &error)
863 {
864 KMessageBox::error(this, i18n("Contact cannot be stored: %1", error), i18n("Failed to store contact"));
865 }
866
contactStored(const Akonadi::Item & item)867 void KMReaderWin::contactStored(const Akonadi::Item &item)
868 {
869 if (item.hasPayload<KContacts::Addressee>()) {
870 const auto contact = item.payload<KContacts::Addressee>();
871 setContactItem(item, contact);
872 mViewer->slotChangeDisplayMail(mViewAsHtml->isChecked() ? Viewer::Html : Viewer::Text, mLoadExternalReference->isChecked());
873 }
874 PimCommon::BroadcastStatus::instance()->setStatusMsg(i18n("Contact modified successfully"));
875 }
876
saveMessageDisplayFormatAction() const877 QAction *KMReaderWin::saveMessageDisplayFormatAction() const
878 {
879 return mViewer->saveMessageDisplayFormatAction();
880 }
881
resetMessageDisplayFormatAction() const882 QAction *KMReaderWin::resetMessageDisplayFormatAction() const
883 {
884 return mViewer->resetMessageDisplayFormatAction();
885 }
886
editContactAction() const887 QAction *KMReaderWin::editContactAction() const
888 {
889 return mEditContactAction;
890 }
891
developmentToolsAction() const892 QAction *KMReaderWin::developmentToolsAction() const
893 {
894 return mViewer->developmentToolsAction();
895 }
896
viewHtmlOption() const897 QMenu *KMReaderWin::viewHtmlOption() const
898 {
899 return mViewHtmlOptions;
900 }
901
shareImage() const902 QAction *KMReaderWin::shareImage() const
903 {
904 return mShareImage;
905 }
906
addToExistingContactAction() const907 QAction *KMReaderWin::addToExistingContactAction() const
908 {
909 return mAddEmailToExistingContactAction;
910 }
911
slotShareImage()912 void KMReaderWin::slotShareImage()
913 {
914 KMCommand *command = new KMShareImageCommand(imageUrlClicked(), this);
915 command->start();
916 }
917
interceptorUrlActions(const WebEngineViewer::WebHitTestResult & result) const918 QList<QAction *> KMReaderWin::interceptorUrlActions(const WebEngineViewer::WebHitTestResult &result) const
919 {
920 return mViewer->interceptorUrlActions(result);
921 }
922
slotPrintingFinished()923 void KMReaderWin::slotPrintingFinished()
924 {
925 if (mViewer->printingMode()) {
926 deleteLater();
927 }
928 }
929