Add favorite tags editing
[trojita.git] / src / Gui / SimplePartWidget.cpp
blobc2d6fd77a12017972376a23a7151a4431a0dd9f1
1 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
3 This file is part of the Trojita Qt IMAP e-mail client,
4 http://trojita.flaska.net/
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <QApplication>
23 #include <QClipboard>
24 #include <QFileDialog>
25 #include <QFontDatabase>
26 #include <QMenu>
27 #include <QNetworkReply>
28 #include <QWebFrame>
29 #include <QWheelEvent>
31 #include "SimplePartWidget.h"
32 #include "Common/MetaTypes.h"
33 #include "Common/Paths.h"
34 #include "Composer/Mailto.h"
35 #include "Gui/MessageView.h" // so that the compiler knows that it's a QObject
36 #include "Gui/Util.h"
37 #include "Imap/Encoders.h"
38 #include "Imap/Model/ItemRoles.h"
39 #include "Imap/Model/MailboxTree.h"
40 #include "Imap/Model/Model.h"
41 #include "Imap/Network/FileDownloadManager.h"
42 #include "Imap/Model/Utils.h"
43 #include "UiUtils/Color.h"
44 #include "UiUtils/IconLoader.h"
46 namespace Gui
49 SimplePartWidget::SimplePartWidget(QWidget *parent, Imap::Network::MsgPartNetAccessManager *manager,
50 const QModelIndex &partIndex, MessageView *messageView):
51 EmbeddedWebView(parent, manager), m_partIndex(partIndex), m_messageView(messageView), m_netAccessManager(manager)
53 Q_ASSERT(partIndex.isValid());
55 if (m_messageView) {
56 connect(this, &QWebView::loadStarted, m_messageView, &MessageView::onWebViewLoadStarted);
57 connect(this, &QWebView::loadFinished, m_messageView, &MessageView::onWebViewLoadFinished);
60 QUrl url;
61 url.setScheme(QStringLiteral("trojita-imap"));
62 url.setHost(QStringLiteral("msg"));
63 url.setPath(partIndex.data(Imap::Mailbox::RolePartPathToPart).toString());
64 if (partIndex.data(Imap::Mailbox::RolePartMimeType).toString() == QLatin1String("text/plain")) {
65 if (partIndex.data(Imap::Mailbox::RolePartOctets).toULongLong() < 100 * 1024) {
66 connect(this, &QWebView::loadFinished, this, &SimplePartWidget::slotMarkupPlainText);
67 } else {
68 QFont font(QFontDatabase::systemFont(QFontDatabase::FixedFont));
69 setStaticWidth(QFontMetrics(font).maxWidth()*90);
70 addCustomStylesheet(QStringLiteral("pre{word-wrap:normal !important;white-space:pre !important;}"));
71 QWebSettings *s = settings();
72 s->setFontFamily(QWebSettings::StandardFont, font.family());
75 load(url);
77 m_savePart = new QAction(UiUtils::loadIcon(QStringLiteral("document-save")), tr("Save this message part..."), this);
78 connect(m_savePart, &QAction::triggered, this, &SimplePartWidget::slotDownloadPart);
79 this->addAction(m_savePart);
81 m_saveMessage = new QAction(UiUtils::loadIcon(QStringLiteral("document-save-all")), tr("Save whole message..."), this);
82 connect(m_saveMessage, &QAction::triggered, this, &SimplePartWidget::slotDownloadMessage);
83 this->addAction(m_saveMessage);
85 m_findAction = new QAction(UiUtils::loadIcon(QStringLiteral("edit-find")), tr("Search..."), this);
86 m_findAction->setShortcut(tr("Ctrl+F"));
87 connect(m_findAction, &QAction::triggered, this, &SimplePartWidget::searchDialogRequested);
88 addAction(m_findAction);
90 m_copyMail = new QAction(UiUtils::loadIcon(QStringLiteral("edit-copy")), tr("Copy e-mail address"), this);
91 connect(m_copyMail, &QAction::triggered, this, [this](){
92 QGuiApplication::clipboard()->setText(m_copyMail->data().toString());
93 });
94 this->addAction(m_copyMail);
96 setContextMenuPolicy(Qt::CustomContextMenu);
98 // It is actually OK to construct this widget without any connection to a messageView -- this is often used when
99 // displaying message source or message headers. Let's silence the QObject::connect warning.
100 if (m_messageView) {
101 connect(this, &QWidget::customContextMenuRequested, m_messageView, &MessageView::partContextMenuRequested);
102 connect(this, &SimplePartWidget::searchDialogRequested, m_messageView, &MessageView::triggerSearchDialog);
103 // The targets expect the sender() of the signal to be a SimplePartWidget, not a QWebPage,
104 // which means we have to do this indirection
105 connect(page(), &QWebPage::linkHovered, this, &SimplePartWidget::linkHovered);
106 connect(this, &SimplePartWidget::linkHovered, m_messageView, &MessageView::partLinkHovered);
107 connect(page(), &QWebPage::downloadRequested, this, &SimplePartWidget::slotDownloadImage);
109 installEventFilter(m_messageView);
113 void SimplePartWidget::slotMarkupPlainText()
115 // NOTICE "single shot", we get a recursion otherwise!
116 disconnect(this, &QWebView::loadFinished, this, &SimplePartWidget::slotMarkupPlainText);
118 // If there's no data, don't try to "fix it up"
119 if (!m_partIndex.isValid() || !m_partIndex.data(Imap::Mailbox::RoleIsFetched).toBool())
120 return;
122 QPalette palette = QApplication::palette();
124 // and finally set the marked up page.
125 page()->mainFrame()->setHtml(UiUtils::htmlizedTextPart(m_partIndex, QFontDatabase::systemFont(QFontDatabase::FixedFont),
126 palette.base().color(), palette.text().color(),
127 palette.link().color(), palette.linkVisited().color()));
130 void SimplePartWidget::slotFileNameRequested(QString *fileName)
132 *fileName = QFileDialog::getSaveFileName(this, tr("Save Attachment"),
133 *fileName, QString(),
134 0, QFileDialog::HideNameFilterDetails
138 QString SimplePartWidget::quoteMe() const
140 QString selection = selectedText();
141 if (selection.isEmpty())
142 return page()->mainFrame()->toPlainText();
143 else
144 return selection;
147 void SimplePartWidget::reloadContents()
149 EmbeddedWebView::reload();
152 const auto zoomConstant = 1.1;
154 void SimplePartWidget::zoomIn()
156 setZoomFactor(zoomFactor() * zoomConstant);
157 constrainSize();
160 void SimplePartWidget::zoomOut()
162 setZoomFactor(zoomFactor() / zoomConstant);
163 constrainSize();
166 void SimplePartWidget::zoomOriginal()
168 setZoomFactor(1);
169 constrainSize();
172 void SimplePartWidget::buildContextMenu(const QPoint &point, QMenu &menu) const
174 menu.addAction(m_findAction);
175 auto a = pageAction(QWebPage::Copy);
176 a->setIcon(UiUtils::loadIcon(QStringLiteral("edit-copy")));
177 menu.addAction(a);
178 a = pageAction(QWebPage::SelectAll);
179 a->setIcon(UiUtils::loadIcon(QStringLiteral("edit-select-all")));
180 menu.addAction(a);
181 auto linkUrl = page()->mainFrame()->hitTestContent(point).linkUrl();
182 if (!linkUrl.isEmpty()) {
183 menu.addSeparator();
184 auto oneMail = Composer::extractOneMailAddress(linkUrl);
185 if (!oneMail.isEmpty()) {
186 a = m_copyMail;
187 a->setData(oneMail);
188 } else {
189 a = pageAction(QWebPage::CopyLinkToClipboard);
190 a->setIcon(UiUtils::loadIcon(QStringLiteral("edit-copy")));
192 menu.addAction(a);
194 menu.addSeparator();
195 menu.addAction(m_savePart);
196 menu.addAction(m_saveMessage);
197 if (!page()->mainFrame()->hitTestContent(point).imageUrl().isEmpty()) {
198 a = pageAction(QWebPage::DownloadImageToDisk);
199 a->setIcon(UiUtils::loadIcon(QStringLiteral("download")));
200 menu.addAction(a);
202 menu.addSeparator();
203 QMenu *colorSchemeMenu = menu.addMenu(UiUtils::loadIcon(QStringLiteral("colorneg")), tr("Color scheme"));
204 QActionGroup *ag = new QActionGroup(colorSchemeMenu);
205 for (auto item: supportedColorSchemes()) {
206 QAction *a = colorSchemeMenu->addAction(item.second);
207 connect(a, &QAction::triggered, this, [this, item](){
208 const_cast<SimplePartWidget*>(this)->setColorScheme(item.first);
210 a->setCheckable(true);
211 if (item.first == m_colorScheme) {
212 a->setChecked(true);
214 a->setActionGroup(ag);
217 auto zoomMenu = menu.addMenu(UiUtils::loadIcon(QStringLiteral("zoom")), tr("Zoom"));
218 if (m_messageView) {
219 zoomMenu->addAction(m_messageView->m_zoomIn);
220 zoomMenu->addAction(m_messageView->m_zoomOut);
221 zoomMenu->addAction(m_messageView->m_zoomOriginal);
222 } else {
223 auto zoomIn = zoomMenu->addAction(UiUtils::loadIcon(QStringLiteral("zoom-in")), tr("Zoom In"));
224 zoomIn->setShortcut(QKeySequence::ZoomIn);
225 connect(zoomIn, &QAction::triggered, this, &SimplePartWidget::zoomIn);
227 auto zoomOut = zoomMenu->addAction(UiUtils::loadIcon(QStringLiteral("zoom-out")), tr("Zoom Out"));
228 zoomOut->setShortcut(QKeySequence::ZoomOut);
229 connect(zoomOut, &QAction::triggered, this, &SimplePartWidget::zoomOut);
231 auto zoomOriginal = zoomMenu->addAction(UiUtils::loadIcon(QStringLiteral("zoom-original")), tr("Original Size"));
232 connect(zoomOriginal, &QAction::triggered, this, &SimplePartWidget::zoomOriginal);
236 void SimplePartWidget::slotDownloadPart()
238 Imap::Network::FileDownloadManager *manager = new Imap::Network::FileDownloadManager(this, m_netAccessManager, m_partIndex);
239 connect(manager, &Imap::Network::FileDownloadManager::fileNameRequested, this, &SimplePartWidget::slotFileNameRequested);
240 connect(manager, &Imap::Network::FileDownloadManager::transferError, m_messageView, &MessageView::transferError);
241 connect(manager, &Imap::Network::FileDownloadManager::transferError, manager, &QObject::deleteLater);
242 connect(manager, &Imap::Network::FileDownloadManager::succeeded, manager, &QObject::deleteLater);
243 manager->downloadPart();
246 void SimplePartWidget::slotDownloadMessage()
248 QModelIndex index = m_partIndex.data(Imap::Mailbox::RolePartMessageIndex).toModelIndex();
249 Imap::Network::FileDownloadManager *manager = new Imap::Network::FileDownloadManager(this, m_netAccessManager, index);
250 connect(manager, &Imap::Network::FileDownloadManager::fileNameRequested, this, &SimplePartWidget::slotFileNameRequested);
251 connect(manager, &Imap::Network::FileDownloadManager::transferError, m_messageView, &MessageView::transferError);
252 connect(manager, &Imap::Network::FileDownloadManager::transferError, manager, &QObject::deleteLater);
253 connect(manager, &Imap::Network::FileDownloadManager::succeeded, manager, &QObject::deleteLater);
254 manager->downloadMessage();
257 void SimplePartWidget::slotDownloadImage(const QNetworkRequest &req)
259 Imap::Network::FileDownloadManager *manager = new Imap::Network::FileDownloadManager(this, m_netAccessManager, req.url(), m_partIndex.parent());
260 connect(manager, &Imap::Network::FileDownloadManager::fileNameRequested, this, &SimplePartWidget::slotFileNameRequested);
261 connect(manager, &Imap::Network::FileDownloadManager::transferError, m_messageView, &MessageView::transferError);
262 connect(manager, &Imap::Network::FileDownloadManager::transferError, manager, &QObject::deleteLater);
263 connect(manager, &Imap::Network::FileDownloadManager::succeeded, manager, &QObject::deleteLater);
264 manager->downloadPart();