2 Copyright (c) 2015-2016 Montel Laurent <montel@kde.org>
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include "articleviewerwebengine.h"
19 #include "akregator_debug.h"
20 #include "articleviewerwebenginepage.h"
21 #include "webengine/urlhandlerwebenginemanager.h"
22 #include "actionmanager.h"
23 #include "akregatorconfig.h"
24 #include "actions/actions.h"
25 #include "urlhandler/webengine/urlhandlerwebengine.h"
27 #include <MessageViewer/NetworkAccessManagerWebEngine>
28 #include <KPIMTextEdit/TextToSpeech>
29 #include <KActionMenu>
31 #include <KParts/BrowserRun>
32 #include <grantleethememanager.h>
33 #include <KActionCollection>
34 #include <KLocalizedString>
37 #include <openurlrequest.h>
38 #include <KPimPrintPreviewDialog>
39 #include <KMessageBox>
41 #include <QWebEngineSettings>
42 #include <QMouseEvent>
43 #include <QPrintDialog>
44 #include <QApplication>
47 #include <viewerplugintoolmanager.h>
48 #include <MessageViewer/WebHitTestResult>
49 #include <MessageViewer/WebHitTest>
51 #include <KIO/KUriFilterSearchProviderActions>
53 using namespace Akregator
;
55 ArticleViewerWebEngine::ArticleViewerWebEngine(KActionCollection
*ac
, QWidget
*parent
)
56 : QWebEngineView(parent
),
57 mActionCollection(ac
),
58 mLastButtonClicked(LeftButton
),
59 mViewerPluginToolManager(Q_NULLPTR
)
61 new MessageViewer::NetworkAccessManagerWebEngine(ac
, this);
62 mPageEngine
= new ArticleViewerWebEnginePage(this);
65 connect(this, &ArticleViewerWebEngine::showContextMenu
, this, &ArticleViewerWebEngine::slotShowContextMenu
);
67 setFocusPolicy(Qt::WheelFocus
);
68 connect(mPageEngine
, &ArticleViewerWebEnginePage::urlClicked
, this, &ArticleViewerWebEngine::slotLinkClicked
);
70 //mWebViewAccessKey = new MessageViewer::WebViewAccessKey(this, this);
71 //mWebViewAccessKey->setActionCollection(mActionCollection);
73 connect(this, &ArticleViewerWebEngine::loadStarted
, this, &ArticleViewerWebEngine::slotLoadStarted
);
74 connect(this, &ArticleViewerWebEngine::loadFinished
, this, &ArticleViewerWebEngine::slotLoadFinished
);
75 //connect(page(), &QWebEnginePage::scrollRequested, mWebViewAccessKey, &MessageViewer::WebViewAccessKey::hideAccessKeys);
76 connect(page(), &QWebEnginePage::linkHovered
, this, &ArticleViewerWebEngine::slotLinkHovered
);
78 setContextMenuPolicy(Qt::DefaultContextMenu
);
79 mWebShortcutMenuManager
= new KIO::KUriFilterSearchProviderActions(this);
80 mShareServiceManager
= new PimCommon::ShareServiceUrlManager(this);
81 connect(mShareServiceManager
, &PimCommon::ShareServiceUrlManager::serviceUrlSelected
, this, &ArticleViewerWebEngine::slotServiceUrlSelected
);
84 ArticleViewerWebEngine::~ArticleViewerWebEngine()
89 QVariantHash
ArticleViewerWebEngine::introductionData() const
92 data
[QStringLiteral("icon")] = QStringLiteral("akregator");
93 data
[QStringLiteral("name")] = i18n("Akregator");
94 data
[QStringLiteral("subtitle")] = i18n("Akregator is a KDE news feed reader.");
95 data
[QStringLiteral("version")] = KAboutData::applicationData().version();
99 void ArticleViewerWebEngine::showAboutPage()
101 paintAboutScreen(QStringLiteral("introduction_akregator.html"), introductionData());
104 void ArticleViewerWebEngine::paintAboutScreen(const QString
&templateName
, const QVariantHash
&data
)
106 GrantleeTheme::ThemeManager
manager(QStringLiteral("splashPage"),
107 QStringLiteral("splash.theme"),
109 QStringLiteral("messageviewer/about/"));
110 GrantleeTheme::Theme theme
= manager
.theme(QStringLiteral("default"));
111 if (theme
.isValid()) {
112 setHtml(theme
.render(templateName
, data
),
113 QUrl::fromLocalFile(theme
.absolutePath() + QLatin1Char('/')));
115 qCDebug(AKREGATOR_LOG
) << "Theme error: failed to find splash theme";
119 void ArticleViewerWebEngine::slotServiceUrlSelected(PimCommon::ShareServiceUrlManager::ServiceType type
)
121 const QUrl url
= mShareServiceManager
->generateServiceUrl(mCurrentUrl
.url(), QString(), type
);
122 mShareServiceManager
->openUrl(url
);
125 void ArticleViewerWebEngine::slotSaveLinkAs()
127 QUrl
tmp(mCurrentUrl
);
129 if (tmp
.fileName().isEmpty()) {
130 tmp
= tmp
.adjusted(QUrl::RemoveFilename
);
131 tmp
.setPath(tmp
.path() + QLatin1String("index.html"));
133 KParts::BrowserRun::simpleSave(tmp
, tmp
.fileName());
136 void ArticleViewerWebEngine::slotSaveImageOnDiskInFrame()
141 void ArticleViewerWebEngine::slotCopyImageLocationInFrame()
143 slotCopyLinkAddress();
146 void ArticleViewerWebEngine::slotCopyLinkAddress()
148 if (mCurrentUrl
.isEmpty()) {
151 QClipboard
*cb
= QApplication::clipboard();
152 cb
->setText(mCurrentUrl
.toString(), QClipboard::Clipboard
);
153 // don't set url to selection as it's a no-no according to a fd.o spec
154 // which spec? Nobody seems to care (tested Firefox (3.5.10) Konqueror,and KMail (4.2.3)), so I re-enable the following line unless someone gives
155 // a good reason to remove it again (bug 183022) --Frank
156 cb
->setText(mCurrentUrl
.toString(), QClipboard::Selection
);
159 void ArticleViewerWebEngine::contextMenuEvent(QContextMenuEvent
*e
)
161 displayContextMenu(e
->pos());
164 void ArticleViewerWebEngine::slotShowContextMenu(const QPoint
&pos
)
166 displayContextMenu(pos
);
169 void ArticleViewerWebEngine::slotPrintPreview()
171 PimCommon::KPimPrintPreviewDialog
previewdlg(this);
172 connect(&previewdlg
, &QPrintPreviewDialog::paintRequested
, this, [this](QPrinter
* printer
) {
178 void ArticleViewerWebEngine::slotPrint()
182 QScopedPointer
<QPrintDialog
> dlg(new QPrintDialog(&printer
));
184 if (dlg
&& dlg
->exec() == QDialog::Accepted
) {
189 void ArticleViewerWebEngine::slotCopy()
191 triggerPageAction(QWebEnginePage::Copy
);
194 void ArticleViewerWebEngine::slotLoadFinished()
199 void ArticleViewerWebEngine::slotLoadStarted()
201 //mWebViewAccessKey->hideAccessKeys();
202 setCursor(Qt::WaitCursor
);
205 void ArticleViewerWebEngine::slotWebHitFinished(const MessageViewer::WebHitTestResult
&result
)
207 mCurrentUrl
= result
.linkUrl();
208 if (URLHandlerWebEngineManager::instance()->handleContextMenuRequest(mCurrentUrl
, mapToGlobal(result
.pos()), this)) {
213 const bool contentSelected
= !selectedText().isEmpty();
214 if (!contentSelected
) {
215 if (!mCurrentUrl
.isEmpty()) {
216 popup
.addAction(createOpenLinkInNewTabAction(mCurrentUrl
, this, SLOT(slotOpenLinkInForegroundTab()), &popup
));
217 popup
.addAction(createOpenLinkInExternalBrowserAction(mCurrentUrl
, this, SLOT(slotOpenLinkInBrowser()), &popup
));
218 popup
.addSeparator();
219 popup
.addAction(mActionCollection
->action(QStringLiteral("savelinkas")));
220 popup
.addAction(mActionCollection
->action(QStringLiteral("copylinkaddress")));
222 if (!result
.imageUrl().isEmpty()) {
223 popup
.addSeparator();
224 popup
.addAction(mActionCollection
->action(QStringLiteral("copy_image_location")));
225 popup
.addAction(mActionCollection
->action(QStringLiteral("saveas_imageurl")));
226 if (adblockEnabled()) {
227 popup
.addSeparator();
228 popup
.addAction(mActionCollection
->action(QStringLiteral("adblock_image")));
231 popup
.addSeparator();
232 popup
.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl
));
233 popup
.addSeparator();
234 popup
.addAction(mShareServiceManager
->menu());
236 popup
.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_copy")));
237 popup
.addSeparator();
238 mWebShortcutMenuManager
->setSelectedText(page()->selectedText());
239 mWebShortcutMenuManager
->addWebShortcutsToMenu(&popup
);
240 popup
.addSeparator();
241 popup
.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection
));
243 #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
244 popup
.addSeparator();
245 popup
.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_print")));
246 popup
.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_printpreview")));
248 popup
.addSeparator();
249 popup
.addAction(ActionManager::getInstance()->action(QStringLiteral("find_in_messages")));
250 if (KPIMTextEdit::TextToSpeech::self()->isReady()) {
251 popup
.addSeparator();
252 popup
.addAction(ActionManager::getInstance()->action(QStringLiteral("speak_text")));
254 popup
.exec(mapToGlobal(result
.pos()));
257 void ArticleViewerWebEngine::displayContextMenu(const QPoint
&pos
)
259 MessageViewer::WebHitTest
*webHit
= mPageEngine
->hitTestContent(pos
);
260 connect(webHit
, &MessageViewer::WebHitTest::finished
, this, &ArticleViewerWebEngine::slotWebHitFinished
);
263 void ArticleViewerWebEngine::slotLinkHovered(const QString
&link
)
265 QString msg
= URLHandlerWebEngineManager::instance()->statusBarMessage(QUrl(link
), this);
270 Q_EMIT
showStatusBarMessage(msg
);
273 void ArticleViewerWebEngine::keyReleaseEvent(QKeyEvent
*e
)
275 if (Settings::self()->accessKeyEnabled()) {
276 //mWebViewAccessKey->keyReleaseEvent(e);
278 QWebEngineView::keyReleaseEvent(e
);
281 void ArticleViewerWebEngine::keyPressEvent(QKeyEvent
*e
)
283 if (e
&& hasFocus()) {
284 if (Settings::self()->accessKeyEnabled()) {
285 //mWebViewAccessKey->keyPressEvent(e);
288 QWebEngineView::keyPressEvent(e
);
291 void ArticleViewerWebEngine::wheelEvent(QWheelEvent
*e
)
293 if (Settings::self()->accessKeyEnabled()) {
294 //mWebViewAccessKey->wheelEvent(e);
296 QWebEngineView::wheelEvent(e
);
299 void ArticleViewerWebEngine::resizeEvent(QResizeEvent
*e
)
301 if (Settings::self()->accessKeyEnabled()) {
302 //mWebViewAccessKey->resizeEvent(e);
304 QWebEngineView::resizeEvent(e
);
307 void ArticleViewerWebEngine::disableIntroduction()
309 KGuiItem
yesButton(KStandardGuiItem::yes());
310 yesButton
.setText(i18n("Disable"));
311 KGuiItem
noButton(KStandardGuiItem::no());
312 noButton
.setText(i18n("Keep Enabled"));
313 if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to disable this introduction page?"),
314 i18n("Disable Introduction Page"), yesButton
, noButton
) == KMessageBox::Yes
) {
315 Settings::self()->setDisableIntroduction(true);
316 Settings::self()->save();
320 void ArticleViewerWebEngine::setArticleAction(ArticleViewerWebEngine::ArticleAction type
, const QString
&articleId
, const QString
&feed
)
322 Q_EMIT
articleAction(type
, articleId
, feed
);
325 void ArticleViewerWebEngine::mouseReleaseEvent(QMouseEvent
*event
)
327 if (event
->button() & Qt::RightButton
) {
328 Q_EMIT
showContextMenu(event
->pos());
329 mLastButtonClicked
= RightButton
;
330 } else if (event
->button() & Qt::MiddleButton
) {
331 mLastButtonClicked
= MiddleButton
;
332 } else if (event
->button() & Qt::LeftButton
) {
333 mLastButtonClicked
= LeftButton
;
335 QWebEngineView::mouseReleaseEvent(event
);
338 void ArticleViewerWebEngine::slotLinkClicked(const QUrl
&url
)
340 if (URLHandlerWebEngineManager::instance()->handleClick(url
, this)) {
344 OpenUrlRequest
req(mCurrentUrl
);
345 if (mLastButtonClicked
== LeftButton
) {
346 switch (Settings::lMBBehaviour()) {
347 case Settings::EnumLMBBehaviour::OpenInExternalBrowser
:
348 req
.setOptions(OpenUrlRequest::ExternalBrowser
);
350 case Settings::EnumLMBBehaviour::OpenInBackground
:
351 req
.setOpenInBackground(true);
352 req
.setOptions(OpenUrlRequest::NewTab
);
357 } else if (mLastButtonClicked
== MiddleButton
) {
358 switch (Settings::mMBBehaviour()) {
359 case Settings::EnumMMBBehaviour::OpenInExternalBrowser
:
360 req
.setOptions(OpenUrlRequest::ExternalBrowser
);
362 case Settings::EnumMMBBehaviour::OpenInBackground
:
363 req
.setOpenInBackground(true);
364 req
.setOptions(OpenUrlRequest::NewTab
);
370 Q_EMIT
signalOpenUrlRequest(req
);
373 void ArticleViewerWebEngine::slotOpenLinkInForegroundTab()
375 OpenUrlRequest
req(mCurrentUrl
);
376 req
.setOptions(OpenUrlRequest::NewTab
);
377 Q_EMIT
signalOpenUrlRequest(req
);
380 void ArticleViewerWebEngine::slotOpenLinkInBackgroundTab()
382 OpenUrlRequest
req(mCurrentUrl
);
383 req
.setOptions(OpenUrlRequest::NewTab
);
384 req
.setOpenInBackground(true);
385 Q_EMIT
signalOpenUrlRequest(req
);
388 void ArticleViewerWebEngine::slotOpenLinkInBrowser()
390 OpenUrlRequest
req(mCurrentUrl
);
391 req
.setOptions(OpenUrlRequest::ExternalBrowser
);
392 Q_EMIT
signalOpenUrlRequest(req
);
395 bool ArticleViewerWebEngine::zoomTextOnlyInFrame() const
398 //return settings()->testAttribute(QWebEngineSettings::ZoomTextOnly);
401 void ArticleViewerWebEngine::createViewerPluginToolManager(KActionCollection
*ac
, QWidget
*parent
)
403 mViewerPluginToolManager
= new MessageViewer::ViewerPluginToolManager(parent
, this);
404 mViewerPluginToolManager
->setActionCollection(ac
);
405 mViewerPluginToolManager
->setPluginName(QStringLiteral("akregator"));
406 mViewerPluginToolManager
->setServiceTypeName(QStringLiteral("Akregator/ViewerPlugin"));
407 if (!mViewerPluginToolManager
->initializePluginList()) {
408 qDebug() << " Impossible to initialize plugins";
410 mViewerPluginToolManager
->createView();
411 connect(mViewerPluginToolManager
, &MessageViewer::ViewerPluginToolManager::activatePlugin
, this, &ArticleViewerWebEngine::slotActivatePlugin
);
414 QList
<QAction
*> ArticleViewerWebEngine::viewerPluginActionList(MessageViewer::ViewerPluginInterface::SpecificFeatureTypes features
)
416 if (mViewerPluginToolManager
) {
417 return mViewerPluginToolManager
->viewerPluginActionList(features
);
419 return QList
<QAction
*>();
422 void ArticleViewerWebEngine::slotActivatePlugin(MessageViewer::ViewerPluginInterface
*interface
)
424 const QString text
= selectedText();
425 if (!text
.isEmpty()) {
426 interface
->setText(text
);
428 interface
->setUrl(mCurrentUrl
);
429 interface
->execute();
432 void ArticleViewerWebEngine::slotZoomTextOnlyInFrame(bool textOnlyInFrame
)
434 //settings()->setAttribute(QWebEngineSettings::ZoomTextOnly, textOnlyInFrame);
437 void ArticleViewerWebEngine::slotBlockImage()
439 if (mCurrentUrl
.isEmpty()) {
442 //MessageViewer::AdBlockManager::self()->addCustomRule(mCurrentUrl.url(), true);
445 bool ArticleViewerWebEngine::adblockEnabled() const
448 //return MessageViewer::AdBlockManager::self()->isEnabled();
451 void Akregator::ArticleViewerWebEngine::slotExpandUrl()