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: 2002 Don Sanders <sanders@kde.org> 0004 SPDX-FileCopyrightText: 2011-2024 Laurent Montel <montel@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 // 0010 // A toplevel KMainWindow derived class for displaying 0011 // single messages or single message parts. 0012 // 0013 // Could be extended to include support for normal main window 0014 // widgets like a toolbar. 0015 0016 #include "kmreadermainwin.h" 0017 #include "historyclosedreader/historyclosedreadermanager.h" 0018 #include "job/composenewmessagejob.h" 0019 #include "kmmainwidget.h" 0020 #include "kmreaderwin.h" 0021 #include "widgets/zoomlabelwidget.h" 0022 0023 #include "kmcommands.h" 0024 #include "messageactions.h" 0025 #include "util.h" 0026 #include <KAcceleratorManager> 0027 #include <KActionMenu> 0028 #include <KEditToolBar> 0029 #include <KLocalizedString> 0030 #include <KMessageBox> 0031 #include <KStandardAction> 0032 #include <KStandardShortcut> 0033 #include <KToolBar> 0034 #include <MailCommon/FolderSettings> 0035 #include <MailCommon/MailKernel> 0036 #include <MessageViewer/HeaderStyle> 0037 #include <MessageViewer/HeaderStylePlugin> 0038 #include <MessageViewer/MessageViewerSettings> 0039 #include <QAction> 0040 #include <QMenu> 0041 #include <QMenuBar> 0042 #include <QStatusBar> 0043 #include <TemplateParser/CustomTemplatesMenu> 0044 0045 #include "tag/tagactionmanager.h" 0046 #include "tag/tagselectdialog.h" 0047 #include <Akonadi/ContactSearchJob> 0048 #include <KActionCollection> 0049 #include <KEmailAddress> 0050 #include <KMime/KMimeMessage> 0051 #include <WebEngineViewer/WebHitTestResult> 0052 0053 #include <Akonadi/ItemCopyJob> 0054 #include <Akonadi/ItemCreateJob> 0055 #include <Akonadi/ItemMoveJob> 0056 #include <Akonadi/MessageFlags> 0057 #include <MailCommon/MailUtil> 0058 #include <MessageViewer/DKIMViewerMenu> 0059 #include <MessageViewer/DKIMWidgetInfo> 0060 #include <MessageViewer/RemoteContentMenu> 0061 using namespace MailCommon; 0062 0063 KMReaderMainWin::KMReaderMainWin(MessageViewer::Viewer::DisplayFormatMessage format, bool htmlLoadExtDefault, const QString &name) 0064 : KMail::SecondaryWindow(!name.isEmpty() ? name : QStringLiteral("readerwindow#")) 0065 { 0066 mReaderWin = new KMReaderWin(this, this, actionCollection()); 0067 mReaderWin->setDisplayFormatMessageOverwrite(format); 0068 mReaderWin->setHtmlLoadExtDefault(htmlLoadExtDefault); 0069 mReaderWin->setDecryptMessageOverwrite(true); 0070 initKMReaderMainWin(); 0071 } 0072 0073 KMReaderMainWin::KMReaderMainWin(const QString &name) 0074 : KMail::SecondaryWindow(!name.isEmpty() ? name : QStringLiteral("readerwindow#")) 0075 { 0076 mReaderWin = new KMReaderWin(this, this, actionCollection()); 0077 initKMReaderMainWin(); 0078 } 0079 0080 KMReaderMainWin::KMReaderMainWin(KMime::Content *aMsgPart, MessageViewer::Viewer::DisplayFormatMessage format, const QString &encoding, const QString &name) 0081 : KMail::SecondaryWindow(!name.isEmpty() ? name : QStringLiteral("readerwindow#")) 0082 { 0083 mReaderWin = new KMReaderWin(this, this, actionCollection()); 0084 mReaderWin->setOverrideEncoding(encoding); 0085 mReaderWin->setDisplayFormatMessageOverwrite(format); 0086 mReaderWin->setMsgPart(aMsgPart); 0087 initKMReaderMainWin(); 0088 } 0089 0090 void KMReaderMainWin::initKMReaderMainWin() 0091 { 0092 setCentralWidget(mReaderWin); 0093 setupAccel(); 0094 setupGUI(Keys | StatusBar | Create, QStringLiteral("kmreadermainwin.rc")); 0095 mMsgActions->setupForwardingActionsList(this); 0096 applyMainWindowSettings(KMKernel::self()->config()->group(QStringLiteral("Separate Reader Window"))); 0097 mZoomLabelIndicator = new ZoomLabelWidget(statusBar()); 0098 statusBar()->addPermanentWidget(mZoomLabelIndicator); 0099 setZoomChanged(mReaderWin->viewer()->webViewZoomFactor()); 0100 statusBar()->addPermanentWidget(mReaderWin->viewer()->dkimWidgetInfo()); 0101 if (!mReaderWin->messageItem().isValid()) { 0102 menuBar()->hide(); 0103 toolBar(QStringLiteral("mainToolBar"))->hide(); 0104 } else { 0105 slotToggleMenubar(true); 0106 } 0107 connect(kmkernel, &KMKernel::configChanged, this, &KMReaderMainWin::slotConfigChanged); 0108 connect(mReaderWin, &KMReaderWin::showStatusBarMessage, this, &KMReaderMainWin::slotShowMessageStatusBar); 0109 connect(mReaderWin, &KMReaderWin::zoomChanged, this, &KMReaderMainWin::setZoomChanged); 0110 connect(mReaderWin, &KMReaderWin::showPreviousMessage, this, &KMReaderMainWin::showPreviousMessage); 0111 connect(mReaderWin, &KMReaderWin::showNextMessage, this, &KMReaderMainWin::showNextMessage); 0112 } 0113 0114 KMReaderMainWin::~KMReaderMainWin() 0115 { 0116 KConfigGroup grp(KSharedConfig::openConfig(QStringLiteral("kmail2rc"))->group(QStringLiteral("Separate Reader Window"))); 0117 saveMainWindowSettings(grp); 0118 if (mMsg.isValid()) { 0119 HistoryClosedReaderInfo info; 0120 info.setItem(mMsg.id()); 0121 KMime::Message::Ptr message = MessageComposer::Util::message(mMsg); 0122 if (message) { 0123 if (auto subject = message->subject(false)) { 0124 info.setSubject(subject->asUnicodeString()); 0125 } 0126 } 0127 HistoryClosedReaderManager::self()->addInfo(std::move(info)); 0128 } 0129 } 0130 0131 void KMReaderMainWin::setZoomChanged(qreal zoomFactor) 0132 { 0133 if (mZoomLabelIndicator) { 0134 mZoomLabelIndicator->setZoom(zoomFactor); 0135 } 0136 } 0137 0138 void KMReaderMainWin::slotShowMessageStatusBar(const QString &msg) 0139 { 0140 statusBar()->showMessage(msg); 0141 } 0142 0143 void KMReaderMainWin::setUseFixedFont(bool useFixedFont) 0144 { 0145 mReaderWin->setUseFixedFont(useFixedFont); 0146 } 0147 0148 MessageViewer::Viewer *KMReaderMainWin::viewer() const 0149 { 0150 return mReaderWin->viewer(); 0151 } 0152 0153 void KMReaderMainWin::showMessage(const QString &encoding, const Akonadi::Item &msg, const Akonadi::Collection &parentCollection) 0154 { 0155 mParentCollection = parentCollection; 0156 mReaderWin->setOverrideEncoding(encoding); 0157 mReaderWin->setMessage(msg, MimeTreeParser::Force); 0158 KMime::Message::Ptr message = MessageComposer::Util::message(msg); 0159 QString caption; 0160 if (message) { 0161 if (auto subject = message->subject(false)) { 0162 caption = subject->asUnicodeString(); 0163 } 0164 } 0165 if (mParentCollection.isValid()) { 0166 caption += QLatin1StringView(" - "); 0167 caption += MailCommon::Util::fullCollectionPath(mParentCollection); 0168 } 0169 if (!caption.isEmpty()) { 0170 setCaption(caption); 0171 } 0172 mMsg = msg; 0173 mMsgActions->setCurrentMessage(msg); 0174 mAkonadiStandardActionManager->setItems({mMsg}); 0175 0176 const bool canChange = mParentCollection.isValid() ? static_cast<bool>(mParentCollection.rights() & Akonadi::Collection::CanDeleteItem) : false; 0177 mTrashAction->setEnabled(canChange); 0178 0179 if (mReaderWin->viewer() && mReaderWin->viewer()->headerStylePlugin() && mReaderWin->viewer()->headerStylePlugin()->headerStyle()) { 0180 mReaderWin->viewer()->headerStylePlugin()->headerStyle()->setReadOnlyMessage(!canChange); 0181 } 0182 0183 const bool isInTrashFolder = mParentCollection.isValid() ? CommonKernel->folderIsTrash(mParentCollection) : false; 0184 QAction *moveToTrash = actionCollection()->action(QStringLiteral("move_to_trash")); 0185 KMail::Util::setActionTrashOrDelete(moveToTrash, isInTrashFolder); 0186 updateActions(); 0187 } 0188 0189 void KMReaderMainWin::updateButtons() 0190 { 0191 if (mListMessage.count() <= 1) { 0192 return; 0193 } 0194 mReaderWin->updateShowMultiMessagesButton((mCurrentMessageIndex > 0), (mCurrentMessageIndex < (mListMessage.count() - 1))); 0195 } 0196 0197 void KMReaderMainWin::showNextMessage() 0198 { 0199 if (mCurrentMessageIndex >= (mListMessage.count() - 1)) { 0200 return; 0201 } 0202 mCurrentMessageIndex++; 0203 initializeMessage(mListMessage.at(mCurrentMessageIndex)); 0204 updateButtons(); 0205 } 0206 0207 void KMReaderMainWin::showPreviousMessage() 0208 { 0209 if (mCurrentMessageIndex <= 0) { 0210 return; 0211 } 0212 mCurrentMessageIndex--; 0213 initializeMessage(mListMessage.at(mCurrentMessageIndex)); 0214 updateButtons(); 0215 } 0216 0217 void KMReaderMainWin::showMessage(const QString &encoding, const QList<KMime::Message::Ptr> &message) 0218 { 0219 if (message.isEmpty()) { 0220 return; 0221 } 0222 0223 mListMessage = message; 0224 mReaderWin->setOverrideEncoding(encoding); 0225 mCurrentMessageIndex = 0; 0226 initializeMessage(mListMessage.at(mCurrentMessageIndex)); 0227 mReaderWin->hasMultiMessages(message.count() > 1); 0228 mAkonadiStandardActionManager->setItems({}); 0229 updateButtons(); 0230 } 0231 0232 void KMReaderMainWin::initializeMessage(const KMime::Message::Ptr &message) 0233 { 0234 Akonadi::Item item; 0235 item.setPayload<KMime::Message::Ptr>(message); 0236 Akonadi::MessageFlags::copyMessageFlags(*message, item); 0237 item.setMimeType(KMime::Message::mimeType()); 0238 0239 mMsg = item; 0240 mMsgActions->setCurrentMessage(item); 0241 0242 mReaderWin->setMessage(message); 0243 if (auto subject = message->subject(false)) { 0244 setCaption(subject->asUnicodeString()); 0245 } 0246 mTrashAction->setEnabled(false); 0247 mAkonadiStandardActionManager->setItems({mMsg}); 0248 updateActions(); 0249 } 0250 0251 void KMReaderMainWin::showMessage(const QString &encoding, const KMime::Message::Ptr &message) 0252 { 0253 if (!message) { 0254 return; 0255 } 0256 const QList<KMime::Message::Ptr> lst{message}; 0257 showMessage(encoding, lst); 0258 } 0259 0260 void KMReaderMainWin::updateActions() 0261 { 0262 if (mHideMenuBarAction->isChecked()) { 0263 menuBar()->show(); 0264 } 0265 toolBar(QStringLiteral("mainToolBar"))->show(); 0266 if (mMsg.isValid()) { 0267 mTagActionManager->updateActionStates(1, mMsg); 0268 } 0269 } 0270 0271 void KMReaderMainWin::slotReplyOrForwardFinished() 0272 { 0273 if (MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForward()) { 0274 close(); 0275 } 0276 } 0277 0278 void KMReaderMainWin::slotSelectMoreMessageTagList() 0279 { 0280 const Akonadi::Item::List selectedMessages = {mMsg}; 0281 if (selectedMessages.isEmpty()) { 0282 return; 0283 } 0284 0285 QPointer<TagSelectDialog> dlg = new TagSelectDialog(this, selectedMessages.count(), selectedMessages.first()); 0286 dlg->setActionCollection(QList<KActionCollection *>{actionCollection()}); 0287 if (dlg->exec()) { 0288 const Akonadi::Tag::List lst = dlg->selectedTag(); 0289 KMCommand *command = new KMSetTagCommand(lst, selectedMessages, KMSetTagCommand::CleanExistingAndAddNew); 0290 command->start(); 0291 } 0292 delete dlg; 0293 } 0294 0295 void KMReaderMainWin::slotUpdateMessageTagList(const Akonadi::Tag &tag) 0296 { 0297 // Create a persistent set from the current thread. 0298 const Akonadi::Item::List selectedMessages = {mMsg}; 0299 if (selectedMessages.isEmpty()) { 0300 return; 0301 } 0302 toggleMessageSetTag(selectedMessages, tag); 0303 } 0304 0305 void KMReaderMainWin::toggleMessageSetTag(const Akonadi::Item::List &select, const Akonadi::Tag &tag) 0306 { 0307 if (select.isEmpty()) { 0308 return; 0309 } 0310 KMCommand *command = new KMSetTagCommand(Akonadi::Tag::List() << tag, select, KMSetTagCommand::Toggle); 0311 command->start(); 0312 } 0313 0314 Akonadi::Collection KMReaderMainWin::parentCollection() const 0315 { 0316 if (mParentCollection.isValid()) { 0317 return mParentCollection; 0318 } else { 0319 return mMsg.parentCollection(); 0320 } 0321 } 0322 0323 void KMReaderMainWin::slotTrashMessage() 0324 { 0325 if (!mMsg.isValid()) { 0326 return; 0327 } 0328 auto command = new KMTrashMsgCommand(parentCollection(), mMsg, -1); 0329 command->start(); 0330 close(); 0331 } 0332 0333 void KMReaderMainWin::slotForwardInlineMsg() 0334 { 0335 if (!mReaderWin->messageItem().hasPayload<KMime::Message::Ptr>()) { 0336 return; 0337 } 0338 KMCommand *command = nullptr; 0339 const Akonadi::Collection parentCol = mReaderWin->messageItem().parentCollection(); 0340 if (parentCol.isValid()) { 0341 QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(parentCol, false); 0342 if (!fd.isNull()) { 0343 command = new KMForwardCommand(this, mReaderWin->messageItem(), fd->identity(), QString(), mReaderWin->copyText()); 0344 } else { 0345 command = new KMForwardCommand(this, mReaderWin->messageItem(), 0, QString(), mReaderWin->copyText()); 0346 } 0347 } else { 0348 command = new KMForwardCommand(this, mReaderWin->messageItem(), 0, QString(), mReaderWin->copyText()); 0349 } 0350 connect(command, &KMTrashMsgCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); 0351 command->start(); 0352 } 0353 0354 void KMReaderMainWin::slotForwardAttachedMessage() 0355 { 0356 if (!mReaderWin->messageItem().hasPayload<KMime::Message::Ptr>()) { 0357 return; 0358 } 0359 KMCommand *command = nullptr; 0360 const Akonadi::Collection parentCol = mReaderWin->messageItem().parentCollection(); 0361 if (parentCol.isValid()) { 0362 QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(parentCol, false); 0363 if (!fd.isNull()) { 0364 command = new KMForwardAttachedCommand(this, mReaderWin->messageItem(), fd->identity()); 0365 } else { 0366 command = new KMForwardAttachedCommand(this, mReaderWin->messageItem()); 0367 } 0368 } else { 0369 command = new KMForwardAttachedCommand(this, mReaderWin->messageItem()); 0370 } 0371 0372 connect(command, &KMForwardAttachedCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); 0373 command->start(); 0374 } 0375 0376 void KMReaderMainWin::slotNewMessageToRecipients() 0377 { 0378 auto job = new ComposeNewMessageJob; 0379 0380 const Akonadi::Collection parentCol = mReaderWin->messageItem().parentCollection(); 0381 if (parentCol.isValid()) { 0382 job->setCurrentCollection(parentCol); 0383 0384 QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(parentCol, false); 0385 if (!fd.isNull()) { 0386 job->setFolderSettings(fd); 0387 } 0388 } 0389 0390 job->setRecipientsFromMessage(mReaderWin->messageItem()); 0391 job->start(); 0392 } 0393 0394 void KMReaderMainWin::slotRedirectMessage() 0395 { 0396 const Akonadi::Item currentItem = mReaderWin->messageItem(); 0397 if (!currentItem.hasPayload<KMime::Message::Ptr>()) { 0398 return; 0399 } 0400 auto command = new KMRedirectCommand(this, currentItem); 0401 connect(command, &KMRedirectCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); 0402 command->start(); 0403 } 0404 0405 void KMReaderMainWin::slotCustomReplyToMsg(const QString &tmpl) 0406 { 0407 const Akonadi::Item currentItem = mReaderWin->messageItem(); 0408 if (!currentItem.hasPayload<KMime::Message::Ptr>()) { 0409 return; 0410 } 0411 auto command = new KMReplyCommand(this, currentItem, MessageComposer::ReplySmart, mReaderWin->copyText(), false, tmpl); 0412 command->setReplyAsHtml(mReaderWin->htmlMail()); 0413 connect(command, &KMReplyCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); 0414 command->start(); 0415 } 0416 0417 void KMReaderMainWin::slotCustomReplyAllToMsg(const QString &tmpl) 0418 { 0419 const Akonadi::Item currentItem = mReaderWin->messageItem(); 0420 if (!currentItem.hasPayload<KMime::Message::Ptr>()) { 0421 return; 0422 } 0423 auto command = new KMReplyCommand(this, currentItem, MessageComposer::ReplyAll, mReaderWin->copyText(), false, tmpl); 0424 command->setReplyAsHtml(mReaderWin->htmlMail()); 0425 connect(command, &KMReplyCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); 0426 0427 command->start(); 0428 } 0429 0430 void KMReaderMainWin::slotCustomForwardMsg(const QString &tmpl) 0431 { 0432 const Akonadi::Item currentItem = mReaderWin->messageItem(); 0433 if (!currentItem.hasPayload<KMime::Message::Ptr>()) { 0434 return; 0435 } 0436 auto command = new KMForwardCommand(this, currentItem, 0, tmpl, mReaderWin->copyText()); 0437 connect(command, &KMForwardCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); 0438 0439 command->start(); 0440 } 0441 0442 void KMReaderMainWin::slotConfigChanged() 0443 { 0444 // readConfig(); 0445 mMsgActions->setupForwardActions(actionCollection()); 0446 mMsgActions->setupForwardingActionsList(this); 0447 } 0448 0449 void KMReaderMainWin::initializeAkonadiStandardAction() 0450 { 0451 const auto mailActions = {Akonadi::StandardMailActionManager::MarkAllMailAsRead, 0452 Akonadi::StandardMailActionManager::MarkMailAsRead, 0453 Akonadi::StandardMailActionManager::MarkMailAsUnread, 0454 Akonadi::StandardMailActionManager::MarkMailAsImportant, 0455 Akonadi::StandardMailActionManager::MarkMailAsActionItem}; 0456 0457 for (const Akonadi::StandardMailActionManager::Type mailAction : mailActions) { 0458 QAction *act = mAkonadiStandardActionManager->createAction(mailAction); 0459 mAkonadiStandardActionManager->interceptAction(mailAction); 0460 connect(act, &QAction::triggered, this, &KMReaderMainWin::slotMarkMailAs); 0461 } 0462 } 0463 0464 void KMReaderMainWin::slotMarkMailAs() 0465 { 0466 const QAction *action = qobject_cast<QAction *>(sender()); 0467 Q_ASSERT(action); 0468 0469 const QByteArray typeStr = action->data().toByteArray(); 0470 0471 mAkonadiStandardActionManager->markItemsAs(typeStr, {mMsgActions->currentItem()}, false); 0472 } 0473 0474 void KMReaderMainWin::setupAccel() 0475 { 0476 if (!kmkernel->xmlGuiInstanceName().isEmpty()) { 0477 setComponentName(kmkernel->xmlGuiInstanceName(), i18n("KMail2")); 0478 } 0479 mMsgActions = new KMail::MessageActions(actionCollection(), this); 0480 mAkonadiStandardActionManager = new Akonadi::StandardMailActionManager(actionCollection(), this); 0481 initializeAkonadiStandardAction(); 0482 mMsgActions->fillAkonadiStandardAction(mAkonadiStandardActionManager); 0483 mMsgActions->setMessageView(mReaderWin); 0484 connect(mMsgActions, &KMail::MessageActions::replyActionFinished, this, &KMReaderMainWin::slotReplyOrForwardFinished); 0485 0486 //----- File Menu 0487 0488 mSaveAtmAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Save A&ttachments..."), actionCollection()); 0489 connect(mSaveAtmAction, &QAction::triggered, mReaderWin->viewer(), &MessageViewer::Viewer::slotAttachmentSaveAll); 0490 0491 mTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("&Move to Trash"), this); 0492 mTrashAction->setIconText(i18nc("@action:intoolbar Move to Trash", "Trash")); 0493 KMail::Util::addQActionHelpText(mTrashAction, i18n("Move message to trashcan")); 0494 actionCollection()->addAction(QStringLiteral("move_to_trash"), mTrashAction); 0495 actionCollection()->setDefaultShortcut(mTrashAction, QKeySequence(Qt::Key_Delete)); 0496 connect(mTrashAction, &QAction::triggered, this, &KMReaderMainWin::slotTrashMessage); 0497 0498 QAction *closeAction = KStandardAction::close(this, &KMReaderMainWin::close, actionCollection()); 0499 QList<QKeySequence> closeShortcut = closeAction->shortcuts(); 0500 closeAction->setShortcuts(closeShortcut << QKeySequence(Qt::Key_Escape)); 0501 0502 mTagActionManager = new KMail::TagActionManager(this, actionCollection(), mMsgActions, this); 0503 connect(mTagActionManager, &KMail::TagActionManager::tagActionTriggered, this, &KMReaderMainWin::slotUpdateMessageTagList); 0504 0505 connect(mTagActionManager, &KMail::TagActionManager::tagMoreActionClicked, this, &KMReaderMainWin::slotSelectMoreMessageTagList); 0506 0507 mTagActionManager->createActions(); 0508 if (mReaderWin->messageItem().isValid()) { 0509 mTagActionManager->updateActionStates(1, mReaderWin->messageItem()); 0510 } 0511 mHideMenuBarAction = KStandardAction::showMenubar(this, &KMReaderMainWin::slotToggleMenubar, actionCollection()); 0512 mHideMenuBarAction->setChecked(KMailSettings::self()->readerShowMenuBar()); 0513 0514 //----- Message Menu 0515 connect(mReaderWin->viewer(), &MessageViewer::Viewer::displayPopupMenu, this, &KMReaderMainWin::slotMessagePopup); 0516 0517 connect(mReaderWin->viewer(), &MessageViewer::Viewer::itemRemoved, this, &QWidget::close); 0518 0519 setStandardToolBarMenuEnabled(true); 0520 KStandardAction::configureToolbars(this, &KMReaderMainWin::slotEditToolbars, actionCollection()); 0521 connect(mReaderWin->viewer(), &MessageViewer::Viewer::moveMessageToTrash, this, &KMReaderMainWin::slotTrashMessage); 0522 } 0523 0524 void KMReaderMainWin::slotToggleMenubar(bool dontShowWarning) 0525 { 0526 if (!mReaderWin->messageItem().isValid()) { 0527 return; 0528 } 0529 if (menuBar()) { 0530 if (mHideMenuBarAction->isChecked()) { 0531 menuBar()->show(); 0532 } else { 0533 if (!dontShowWarning) { 0534 const QString accel = mHideMenuBarAction->shortcut().toString(); 0535 KMessageBox::information(this, 0536 i18n("<qt>This will hide the menu bar completely." 0537 " You can show it again by typing %1.</qt>", 0538 accel), 0539 i18n("Hide menu bar"), 0540 QStringLiteral("HideMenuBarWarning")); 0541 } 0542 menuBar()->hide(); 0543 } 0544 KMailSettings::self()->setReaderShowMenuBar(mHideMenuBarAction->isChecked()); 0545 } 0546 } 0547 0548 QAction *KMReaderMainWin::copyActionMenu(QMenu *menu) 0549 { 0550 KMMainWidget *mainwin = kmkernel->getKMMainWidget(); 0551 if (mainwin) { 0552 Akonadi::StandardActionManager *manager = mainwin->standardMailActionManager()->standardActionManager(); 0553 const auto mainWinAction = manager->action(Akonadi::StandardActionManager::CopyItemToMenu); 0554 auto action = new KActionMenu(menu); 0555 action->setIcon(mainWinAction->icon()); 0556 action->setText(mainWinAction->text()); 0557 manager->createActionFolderMenu(action->menu(), Akonadi::StandardActionManager::CopyItemToMenu); 0558 connect(action->menu(), &QMenu::triggered, this, &KMReaderMainWin::slotCopyItem); 0559 return action; 0560 } 0561 return nullptr; 0562 } 0563 0564 QAction *KMReaderMainWin::moveActionMenu(QMenu *menu) 0565 { 0566 KMMainWidget *mainwin = kmkernel->getKMMainWidget(); 0567 if (mainwin) { 0568 Akonadi::StandardActionManager *manager = mainwin->standardMailActionManager()->standardActionManager(); 0569 const auto mainWinAction = manager->action(Akonadi::StandardActionManager::MoveItemToMenu); 0570 auto action = new KActionMenu(menu); 0571 action->setIcon(mainWinAction->icon()); 0572 action->setText(mainWinAction->text()); 0573 manager->createActionFolderMenu(action->menu(), Akonadi::StandardActionManager::MoveItemToMenu); 0574 connect(action->menu(), &QMenu::triggered, this, &KMReaderMainWin::slotMoveItem); 0575 return action; 0576 } 0577 return nullptr; 0578 } 0579 0580 void KMReaderMainWin::slotMoveItem(QAction *action) 0581 { 0582 if (action) { 0583 const QModelIndex index = action->data().toModelIndex(); 0584 const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0585 copyOrMoveItem(collection, true); 0586 } 0587 } 0588 0589 void KMReaderMainWin::copyOrMoveItem(const Akonadi::Collection &collection, bool move) 0590 { 0591 if (mMsg.isValid()) { 0592 if (move) { 0593 auto job = new Akonadi::ItemMoveJob(mMsg, collection, this); 0594 connect(job, &KJob::result, this, &KMReaderMainWin::slotCopyMoveResult); 0595 } else { 0596 auto job = new Akonadi::ItemCopyJob(mMsg, collection, this); 0597 connect(job, &KJob::result, this, &KMReaderMainWin::slotCopyMoveResult); 0598 } 0599 } else { 0600 auto job = new Akonadi::ItemCreateJob(mMsg, collection, this); 0601 connect(job, &KJob::result, this, &KMReaderMainWin::slotCopyMoveResult); 0602 } 0603 0604 KMMainWidget *mainwin = kmkernel->getKMMainWidget(); 0605 if (mainwin) { 0606 mainwin->standardMailActionManager()->standardActionManager()->addRecentCollection(collection.id()); 0607 } 0608 } 0609 0610 void KMReaderMainWin::slotCopyItem(QAction *action) 0611 { 0612 if (action) { 0613 const QModelIndex index = action->data().toModelIndex(); 0614 const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0615 copyOrMoveItem(collection, false); 0616 } 0617 } 0618 0619 void KMReaderMainWin::slotCopyMoveResult(KJob *job) 0620 { 0621 if (job->error()) { 0622 KMessageBox::error(this, i18n("Cannot copy item. %1", job->errorString())); 0623 } 0624 } 0625 0626 void KMReaderMainWin::slotMessagePopup(const Akonadi::Item &aMsg, const WebEngineViewer::WebHitTestResult &result, const QPoint &aPoint) 0627 { 0628 QUrl aUrl = result.linkUrl(); 0629 QUrl imageUrl = result.imageUrl(); 0630 mMsg = aMsg; 0631 0632 const QString email = KEmailAddress::firstEmailAddress(aUrl.path()).toLower(); 0633 if (aUrl.scheme() == QLatin1StringView("mailto") && !email.isEmpty()) { 0634 auto job = new Akonadi::ContactSearchJob(this); 0635 job->setLimit(1); 0636 job->setQuery(Akonadi::ContactSearchJob::Email, email, Akonadi::ContactSearchJob::ExactMatch); 0637 job->setProperty("msg", QVariant::fromValue(mMsg)); 0638 job->setProperty("point", aPoint); 0639 job->setProperty("imageUrl", imageUrl); 0640 job->setProperty("url", aUrl); 0641 job->setProperty("webhitresult", QVariant::fromValue(result)); 0642 connect(job, &Akonadi::ItemCopyJob::result, this, &KMReaderMainWin::slotContactSearchJobForMessagePopupDone); 0643 } else { 0644 showMessagePopup(mMsg, aUrl, imageUrl, aPoint, false, false, result); 0645 } 0646 } 0647 0648 void KMReaderMainWin::slotContactSearchJobForMessagePopupDone(KJob *job) 0649 { 0650 const Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob *>(job); 0651 const bool contactAlreadyExists = !searchJob->contacts().isEmpty(); 0652 0653 const Akonadi::Item::List listContact = searchJob->items(); 0654 const bool uniqueContactFound = (listContact.count() == 1); 0655 if (uniqueContactFound) { 0656 mReaderWin->setContactItem(listContact.first(), searchJob->contacts().at(0)); 0657 } else { 0658 mReaderWin->clearContactItem(); 0659 } 0660 0661 const auto msg = job->property("msg").value<Akonadi::Item>(); 0662 const QPoint aPoint = job->property("point").toPoint(); 0663 const QUrl imageUrl = job->property("imageUrl").toUrl(); 0664 const QUrl url = job->property("url").toUrl(); 0665 const auto result = job->property("webhitresult").value<WebEngineViewer::WebHitTestResult>(); 0666 0667 showMessagePopup(msg, url, imageUrl, aPoint, contactAlreadyExists, uniqueContactFound, result); 0668 } 0669 0670 void KMReaderMainWin::showMessagePopup(const Akonadi::Item &msg, 0671 const QUrl &url, 0672 const QUrl &imageUrl, 0673 const QPoint &aPoint, 0674 bool contactAlreadyExists, 0675 bool uniqueContactFound, 0676 const WebEngineViewer::WebHitTestResult &result) 0677 { 0678 QMenu *menu = nullptr; 0679 0680 bool urlMenuAdded = false; 0681 bool copyAdded = false; 0682 const bool messageHasPayload = msg.hasPayload<KMime::Message::Ptr>(); 0683 if (!url.isEmpty()) { 0684 if (url.scheme() == QLatin1StringView("mailto")) { 0685 // popup on a mailto URL 0686 menu = new QMenu(this); 0687 menu->addAction(mReaderWin->mailToComposeAction()); 0688 if (messageHasPayload) { 0689 menu->addAction(mReaderWin->mailToReplyAction()); 0690 menu->addAction(mReaderWin->mailToForwardAction()); 0691 menu->addSeparator(); 0692 } 0693 0694 if (contactAlreadyExists) { 0695 if (uniqueContactFound) { 0696 menu->addAction(mReaderWin->editContactAction()); 0697 } else { 0698 menu->addAction(mReaderWin->openAddrBookAction()); 0699 } 0700 } else { 0701 menu->addAction(mReaderWin->addAddrBookAction()); 0702 menu->addAction(mReaderWin->addToExistingContactAction()); 0703 } 0704 menu->addSeparator(); 0705 menu->addMenu(mReaderWin->viewHtmlOption()); 0706 menu->addSeparator(); 0707 menu->addAction(mReaderWin->copyURLAction()); 0708 copyAdded = true; 0709 urlMenuAdded = true; 0710 } else if (url.scheme() != QLatin1StringView("attachment")) { 0711 // popup on a not-mailto URL 0712 menu = new QMenu(this); 0713 menu->addAction(mReaderWin->urlOpenAction()); 0714 menu->addAction(mReaderWin->addUrlToBookmarkAction()); 0715 menu->addAction(mReaderWin->urlSaveAsAction()); 0716 menu->addAction(mReaderWin->copyURLAction()); 0717 menu->addSeparator(); 0718 menu->addAction(mReaderWin->shareServiceUrlMenu()); 0719 menu->addSeparator(); 0720 menu->addActions(mReaderWin->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl)); 0721 if (!imageUrl.isEmpty()) { 0722 menu->addSeparator(); 0723 mReaderWin->addImageMenuActions(menu); 0724 } 0725 urlMenuAdded = true; 0726 } 0727 } 0728 const QString selectedText(mReaderWin->copyText()); 0729 if (!selectedText.isEmpty()) { 0730 if (!menu) { 0731 menu = new QMenu(this); 0732 } 0733 if (urlMenuAdded) { 0734 menu->addSeparator(); 0735 } 0736 if (messageHasPayload) { 0737 menu->addAction(mMsgActions->replyMenu()); 0738 menu->addSeparator(); 0739 menu->addAction(mMsgActions->mailingListActionMenu()); 0740 menu->addSeparator(); 0741 } 0742 if (!copyAdded) { 0743 menu->addAction(mReaderWin->copyAction()); 0744 } 0745 menu->addAction(mReaderWin->selectAllAction()); 0746 menu->addSeparator(); 0747 mMsgActions->addWebShortcutsMenu(menu, selectedText); 0748 menu->addSeparator(); 0749 menu->addActions(mReaderWin->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection)); 0750 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT 0751 menu->addSeparator(); 0752 menu->addAction(mReaderWin->speakTextAction()); 0753 #endif 0754 menu->addSeparator(); 0755 menu->addAction(mReaderWin->shareTextAction()); 0756 } else if (!urlMenuAdded) { 0757 if (!menu) { 0758 menu = new QMenu(this); 0759 } 0760 0761 // popup somewhere else (i.e., not a URL) on the message 0762 if (messageHasPayload) { 0763 bool replyForwardMenu = false; 0764 Akonadi::Collection col = parentCollection(); 0765 if (col.isValid()) { 0766 if (!(CommonKernel->folderIsSentMailFolder(col) || CommonKernel->folderIsDrafts(col) || CommonKernel->folderIsTemplates(col))) { 0767 replyForwardMenu = true; 0768 } 0769 } else if (messageHasPayload) { 0770 replyForwardMenu = true; 0771 } 0772 0773 if (replyForwardMenu) { 0774 // add the reply and forward actions only if we are not in a sent-mail, 0775 // templates or drafts folder 0776 menu->addAction(mMsgActions->replyMenu()); 0777 menu->addAction(mMsgActions->forwardMenu()); 0778 menu->addSeparator(); 0779 } else if (col.isValid() && CommonKernel->folderIsTemplates(col)) { 0780 menu->addAction(mMsgActions->newMessageFromTemplateAction()); 0781 } 0782 0783 if (col.isValid() && CommonKernel->folderIsSentMailFolder(col)) { 0784 menu->addAction(mMsgActions->sendAgainAction()); 0785 menu->addSeparator(); 0786 } 0787 0788 menu->addAction(copyActionMenu(menu)); 0789 if (col.isValid()) { 0790 menu->addAction(moveActionMenu(menu)); 0791 } 0792 menu->addSeparator(); 0793 menu->addAction(mMsgActions->mailingListActionMenu()); 0794 0795 menu->addSeparator(); 0796 if (!imageUrl.isEmpty()) { 0797 menu->addSeparator(); 0798 mReaderWin->addImageMenuActions(menu); 0799 menu->addSeparator(); 0800 } 0801 0802 menu->addAction(mMsgActions->printPreviewAction()); 0803 menu->addAction(mMsgActions->printAction()); 0804 menu->addSeparator(); 0805 menu->addAction(mReaderWin->saveAsAction()); 0806 menu->addSeparator(); 0807 menu->addAction(mMsgActions->exportToPdfAction()); 0808 menu->addSeparator(); 0809 menu->addAction(mSaveAtmAction); 0810 if (msg.isValid()) { 0811 if (mReaderWin->dkimViewerMenu()) { 0812 menu->addSeparator(); 0813 menu->addMenu(mReaderWin->dkimViewerMenu()->menu()); 0814 } 0815 menu->addSeparator(); 0816 if (mReaderWin->remoteContentMenu()) { 0817 menu->addMenu(mReaderWin->remoteContentMenu()); 0818 menu->addSeparator(); 0819 } 0820 menu->addActions(mReaderWin->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedMessage)); 0821 } 0822 } else { 0823 menu->addAction(mReaderWin->toggleFixFontAction()); 0824 if (!mReaderWin->mimePartTreeIsEmpty()) { 0825 menu->addAction(mReaderWin->toggleMimePartTreeAction()); 0826 } 0827 } 0828 if (msg.isValid()) { 0829 menu->addSeparator(); 0830 menu->addAction(mMsgActions->addFollowupReminderAction()); 0831 } 0832 if (kmkernel->allowToDebug()) { 0833 menu->addSeparator(); 0834 menu->addAction(mMsgActions->debugAkonadiSearchAction()); 0835 menu->addSeparator(); 0836 menu->addAction(mReaderWin->developmentToolsAction()); 0837 } 0838 } 0839 const QList<QAction *> interceptorUrlActions = mReaderWin->interceptorUrlActions(result); 0840 if (!interceptorUrlActions.isEmpty()) { 0841 menu->addSeparator(); 0842 menu->addActions(interceptorUrlActions); 0843 } 0844 0845 if (menu) { 0846 KAcceleratorManager::manage(menu); 0847 menu->exec(aPoint, nullptr); 0848 delete menu; 0849 } 0850 } 0851 0852 void KMReaderMainWin::showAndActivateWindow() 0853 { 0854 show(); 0855 raise(); 0856 activateWindow(); 0857 } 0858 0859 void KMReaderMainWin::slotEditToolbars() 0860 { 0861 KConfigGroup grp(KMKernel::self()->config(), QStringLiteral("ReaderWindow")); 0862 saveMainWindowSettings(grp); 0863 QPointer<KEditToolBar> dlg = new KEditToolBar(guiFactory(), this); 0864 connect(dlg.data(), &KEditToolBar::newToolBarConfig, this, &KMReaderMainWin::slotUpdateToolbars); 0865 dlg->exec(); 0866 delete dlg; 0867 } 0868 0869 void KMReaderMainWin::slotUpdateToolbars() 0870 { 0871 createGUI(QStringLiteral("kmreadermainwin.rc")); 0872 applyMainWindowSettings(KConfigGroup(KMKernel::self()->config(), QStringLiteral("ReaderWindow"))); 0873 } 0874 0875 #include "moc_kmreadermainwin.cpp"