Mantid
Loading...
Searching...
No Matches
pqHelpWindow.cxx
Go to the documentation of this file.
1/*=========================================================================
2
3 Program: ParaView
4 Module: pqHelpWindow.cxx
5
6 Copyright (c) 2005,2006 Sandia Corporation, Kitware Inc.
7 All rights reserved.
8
9 ParaView is a free software; you can redistribute it and/or modify it
10 under the terms of the ParaView license version 1.2.
11
12 See License_v1.2.txt for the full ParaView license.
13 A copy of this license can be obtained by contacting
14 Kitware Inc.
15 28 Corporate Drive
16 Clifton Park, NY 12065
17 USA
18
19THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
23CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31========================================================================*/
34#include "ui_pqHelpWindow.h"
35
36#include <QBuffer>
37#include <QFileInfo>
38#include <QHash>
39#include <QHelpEngine>
40#include <QHelpIndexWidget>
41#include <QHelpSearchQueryWidget>
42#include <QHelpSearchResultWidget>
43#include <QMimeDatabase>
44#include <QMimeType>
45#include <QNetworkProxy>
46#include <QPointer>
47#include <QPrintDialog>
48#include <QPrinter>
49#include <QPushButton>
50#include <QTextBrowser>
51#include <QTextStream>
52#include <QTimer>
53#include <QToolBar>
54#include <QToolButton>
55#include <QUrl>
56
57namespace {
59constexpr auto QTHELP_SCHEME = "qthelp";
60} // namespace
61
62#if defined(USE_QTWEBKIT)
63#include <QNetworkReply>
64#include <QWebHistory>
65#include <QWebView>
66
67// ****************************************************************************
68// CLASS pqHelpWindowNetworkReply
69// ****************************************************************************
72class pqHelpWindowNetworkReply : public QNetworkReply {
73 using Superclass = QNetworkReply;
74
75public:
76 pqHelpWindowNetworkReply(const QUrl &url, QHelpEngineCore *helpEngine, QObject *parent = nullptr);
77
78 void abort() override {}
79
80 qint64 bytesAvailable() const override {
81 return (this->RawData.size() - this->Offset) + this->Superclass::bytesAvailable();
82 }
83 bool isSequential() const override { return true; }
84
85protected:
86 qint64 readData(char *data, qint64 maxSize) override;
87
88 QByteArray RawData;
89 qint64 Offset;
90
91private:
92 Q_DISABLE_COPY(pqHelpWindowNetworkReply)
93};
94
95//-----------------------------------------------------------------------------
96pqHelpWindowNetworkReply::pqHelpWindowNetworkReply(const QUrl &my_url, QHelpEngineCore *engine, QObject *parent)
97 : Superclass(parent), Offset(0) {
98 Q_ASSERT(engine);
99
100 this->RawData = engine->fileData(my_url);
101
102 QString content_type = "text/plain";
103 QString extension = QFileInfo(my_url.path()).suffix().toLower();
104 QMap<QString, QString> extension_type_map;
105 extension_type_map["jpg"] = "image/jpeg";
106 extension_type_map["jpeg"] = "image/jpeg";
107 extension_type_map["png"] = "image/png";
108 extension_type_map["gif"] = "image/gif";
109 extension_type_map["tiff"] = "image/tiff";
110 extension_type_map["htm"] = "text/html";
111 extension_type_map["html"] = "text/html";
112 extension_type_map["css"] = "text/css";
113 extension_type_map["xml"] = "text/xml";
114
115 if (extension_type_map.contains(extension)) {
116 content_type = extension_type_map[extension];
117 }
118
119 this->setHeader(QNetworkRequest::ContentLengthHeader, QVariant(this->RawData.size()));
120 this->setHeader(QNetworkRequest::ContentTypeHeader, content_type);
121 this->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
122 this->setUrl(my_url);
123 QTimer::singleShot(0, this, SIGNAL(readyRead()));
124 QTimer::singleShot(0, this, SLOT(finished()));
125}
126
127//-----------------------------------------------------------------------------
128qint64 pqHelpWindowNetworkReply::readData(char *data, qint64 maxSize) {
129 if (this->Offset <= this->RawData.size()) {
130 qint64 end = qMin(this->Offset + maxSize, static_cast<qint64>(this->RawData.size()));
131 qint64 delta = end - this->Offset;
132 memcpy(data, this->RawData.constData() + this->Offset, delta);
133 this->Offset += delta;
134 return delta;
135 }
136 return -1;
137}
138
139// ****************************************************************************
140// CLASS pqHelpWindow::pqNetworkAccessManager
141// ****************************************************************************
142//-----------------------------------------------------------------------------
143class pqHelpWindow::pqNetworkAccessManager : public QNetworkAccessManager {
144 using Superclass = QNetworkAccessManager;
145 QPointer<QHelpEngineCore> Engine;
146
147public:
148 pqNetworkAccessManager(QHelpEngineCore *helpEngine, QNetworkAccessManager *manager, QObject *parentObject)
149 : Superclass(parentObject), Engine(helpEngine) {
150 Q_ASSERT(manager != nullptr && helpEngine != nullptr);
151
152 this->setCache(manager->cache());
153 this->setCookieJar(manager->cookieJar());
154 this->setProxy(manager->proxy());
155 this->setProxyFactory(manager->proxyFactory());
156 }
157
158protected:
159 QNetworkReply *createRequest(Operation operation, const QNetworkRequest &request, QIODevice *device) override {
160 if (request.url().scheme() == QTHELP_SCHEME && operation == GetOperation) {
161 return new pqHelpWindowNetworkReply(request.url(), this->Engine, this);
162 } else {
163 return this->Superclass::createRequest(operation, request, device);
164 }
165 }
166
167private:
168 Q_DISABLE_COPY(pqNetworkAccessManager)
169};
170
171#else // !USE_WEBKIT
172#include <QWebEngineHistory>
173#include <QWebEnginePage>
174#include <QWebEngineProfile>
175#include <QWebEngineUrlRequestJob>
176#include <QWebEngineUrlSchemeHandler>
177#include <QWebEngineView>
178
179#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
180#include <QWebEngineUrlScheme>
181
186 auto scheme = QWebEngineUrlScheme(QTHELP_SCHEME);
187 scheme.setFlags(QWebEngineUrlScheme::LocalScheme | QWebEngineUrlScheme::LocalAccessAllowed);
188 QWebEngineUrlScheme::registerScheme(scheme);
189 }
190};
191
193#endif // end QT_VERSION >= 5.12
194
196class QtHelpUrlHandler : public QWebEngineUrlSchemeHandler {
197public:
198 QtHelpUrlHandler(QHelpEngineCore *helpEngine, QObject *parent = nullptr)
199 : QWebEngineUrlSchemeHandler(parent), m_helpEngine(helpEngine) {}
200
201protected:
202 void requestStarted(QWebEngineUrlRequestJob *request) override {
203 const auto url = request->requestUrl();
204 const auto resourceType = contentType(url);
205 const auto array = m_helpEngine->fileData(url);
206 QBuffer *buffer = new QBuffer;
207 buffer->setData(array);
208 buffer->open(QIODevice::ReadOnly);
209 connect(buffer, &QIODevice::aboutToClose, buffer, &QObject::deleteLater);
210 request->reply(resourceType.toLocal8Bit(), buffer);
211 }
212
213private:
218 QString contentType(const QUrl &url) {
219 QMimeDatabase mimeTypes;
220 return mimeTypes.mimeTypeForFile(url.path(), QMimeDatabase::MatchExtension).name();
221 }
222
223private:
224 QHelpEngineCore *m_helpEngine;
225};
226
227#endif
228
229// ****************************************************************************
230// CLASS pqHelpWindow
231// ****************************************************************************
232
233//-----------------------------------------------------------------------------
234pqHelpWindow::pqHelpWindow(QHelpEngine *engine, QWidget *parentObject, const Qt::WindowFlags &parentFlags)
235 : Superclass(parentObject, parentFlags), m_helpEngine(engine) {
236 Q_ASSERT(engine != nullptr);
237 // Take ownership of the engine
238 m_helpEngine->setParent(this);
239
240 Ui::pqHelpWindow ui;
241 ui.setupUi(this);
242
243 // all warnings from the help engine get logged
244 QObject::connect(this->m_helpEngine, SIGNAL(warning(const QString &)), this, SIGNAL(helpWarnings(const QString &)));
245
246 // add a navigation toolbar
247 auto *navigation = new QToolBar("Navigation");
248 auto *home = new QPushButton("Home");
249 auto *print = new QPushButton("Print...");
250 print->setToolTip("Print the current page");
251
252 m_forward = new QToolButton();
253 m_forward->setArrowType(Qt::RightArrow);
254 m_forward->setToolTip("next");
255 m_forward->setEnabled(false);
256 m_forward->setAutoRaise(true);
257
258 m_backward = new QToolButton();
259 m_backward->setArrowType(Qt::LeftArrow);
260 m_backward->setToolTip("previous");
261 m_backward->setEnabled(false);
262 m_backward->setAutoRaise(true);
263
264 navigation->addWidget(home);
265 navigation->addWidget(print);
266 navigation->addWidget(m_backward);
267 navigation->addWidget(m_forward);
268 navigation->setAllowedAreas(Qt::TopToolBarArea | Qt::RightToolBarArea);
269 this->addToolBar(navigation);
270
271 this->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
272
273 // create index and search dock tabs
274 this->tabifyDockWidget(ui.indexDock, ui.searchDock);
275 ui.indexDock->setWidget(this->m_helpEngine->indexWidget());
276
277 // setup the search tab
278 auto *searchPane = new QWidget(this);
279 auto *vbox = new QVBoxLayout();
280 searchPane->setLayout(vbox);
281 vbox->addWidget(this->m_helpEngine->searchEngine()->queryWidget());
282 vbox->addWidget(this->m_helpEngine->searchEngine()->resultWidget());
283 connect(this->m_helpEngine->searchEngine()->resultWidget(), SIGNAL(requestShowLink(QUrl)), this,
284 SLOT(showPage(QUrl)));
285
286 // set the search connection
287 ui.searchDock->setWidget(searchPane);
288 connect(this->m_helpEngine->searchEngine()->queryWidget(), SIGNAL(search()), this, SLOT(search()));
289
290 // connect the index page to the content pane
291 connect(this->m_helpEngine->indexWidget(), SIGNAL(linkActivated(QUrl, QString)), this, SLOT(showPage(QUrl)));
292
293// setup the content pane
294#if defined(USE_QTWEBKIT)
295 m_browser = new QWebView(this);
296 QNetworkAccessManager *oldManager = m_browser->page()->networkAccessManager();
297 auto *newManager = new pqNetworkAccessManager(m_helpEngine, oldManager, this);
298 m_browser->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
299 m_browser->page()->setNetworkAccessManager(newManager);
300 m_browser->page()->setForwardUnsupportedContent(false);
301 connect(m_browser, SIGNAL(linkClicked(QUrl)), this, SLOT(showPage(QUrl)));
302 // set up the status bar
303 connect(m_browser->page(), SIGNAL(linkHovered(QString, QString, QString)), this,
304 SLOT(linkHovered(QString, QString, QString)));
305#else
306 QWebEngineProfile::defaultProfile()->installUrlSchemeHandler(QTHELP_SCHEME, new QtHelpUrlHandler(engine, this));
307 m_browser = new QWebEngineView(this);
309 connect(m_browser->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(showLinkedPage(QUrl)));
310 // set up the status bar
311 connect(m_browser->page(), SIGNAL(linkHovered(QString)), this, SLOT(linkHovered(QString)));
312#endif
313 this->setCentralWidget(this->m_browser);
314
315 // connect the navigation buttons
316 connect(home, SIGNAL(clicked()), this, SLOT(showHomePage()));
317 connect(print, SIGNAL(clicked()), this, SLOT(printPage()));
318 connect(m_forward, SIGNAL(clicked()), m_browser, SLOT(forward()));
319 connect(m_backward, SIGNAL(clicked()), m_browser, SLOT(back()));
320 connect(m_forward, SIGNAL(clicked()), this, SLOT(updateNavButtons()));
321 connect(m_backward, SIGNAL(clicked()), this, SLOT(updateNavButtons()));
322
323 // setup the search engine to do its job
324 m_helpEngine->searchEngine()->reindexDocumentation();
325}
326
327//-----------------------------------------------------------------------------
328
333void pqHelpWindow::errorMissingPage(const QUrl &url) {
334 QString htmlDoc =
335 QString(QLatin1String("<html><head><title>Invalid Url - %1</title></head><body>")).arg(url.toString());
336
337 htmlDoc += QString(QLatin1String("<center><h1>Missing page - %1</h1></center>")).arg(url.toString());
338
339 htmlDoc += QLatin1String("</body></html>");
340
341 m_browser->setHtml(htmlDoc);
342}
343
344//-----------------------------------------------------------------------------
345void pqHelpWindow::showPage(const QString &url, bool linkClicked /* = false */) {
346 this->showPage(QUrl::fromUserInput(url), linkClicked);
347}
348
349//-----------------------------------------------------------------------------
350void pqHelpWindow::showPage(const QUrl &url, bool linkClicked /* = false */) {
351 if (url.scheme() == QTHELP_SCHEME) {
352 if (this->isExistingPage(url)) {
353 if (!linkClicked)
354 this->m_browser->setUrl(url);
355 } else {
356 errorMissingPage(url);
357 }
358 if (m_browser->history()->count() > 0)
359 m_backward->setEnabled(true);
360 m_forward->setEnabled(false);
361 } else {
363 MantidDesktopServices::openUrl(url);
364 }
365}
366
367//-----------------------------------------------------------------------------
368void pqHelpWindow::showLinkedPage(const QUrl &url) { this->showPage(url, true); }
369
370//-----------------------------------------------------------------------------
372 QPrinter printer;
373 QPrintDialog dialog(&printer, this);
374 dialog.setWindowTitle(tr("Print Document"));
375 if (dialog.exec() != QDialog::Accepted)
376 return;
377#if defined(USE_QTWEBKIT)
378 m_browser->print(&printer);
379#else
380 m_browser->page()->print(&printer, [](bool) {});
381#endif
382}
383
384//-----------------------------------------------------------------------------
386 m_forward->setEnabled(m_browser->history()->canGoForward());
387 m_backward->setEnabled(m_browser->history()->canGoBack());
388}
389
390//-----------------------------------------------------------------------------
392 auto search = this->m_helpEngine->searchEngine()->queryWidget()->searchInput();
393 this->m_helpEngine->searchEngine()->search(search);
394}
395
396//-----------------------------------------------------------------------------
397void pqHelpWindow::linkHovered(const QString &link, const QString &title, const QString &textContent) {
398 (void)title;
399 (void)textContent;
400 this->statusBar()->showMessage(link);
401}
402
403void pqHelpWindow::showHomePage() { showPage(QString("qthelp://org.mantidproject/doc/index.html")); }
404
405//-----------------------------------------------------------------------------
406void pqHelpWindow::showHomePage(const QString &namespace_name) {
407 QList<QUrl> html_pages = this->m_helpEngine->files(namespace_name, QStringList(), "html");
408 // now try to locate a file named index.html in this collection.
409 foreach (QUrl url, html_pages) {
410 if (url.path().endsWith("index.html")) {
411 this->showPage(url.toString());
412 return;
413 }
414 }
415 errorMissingPage(QUrl("Could not locate index.html"));
416}
417
418//-----------------------------------------------------------------------------
419bool pqHelpWindow::isExistingPage(const QUrl &url) {
420 return (this->m_helpEngine->findFile(url).isValid() && (this->m_helpEngine->fileData(url).size() > 0));
421}
Mimic the WebKit class to emit linkClicked signal from the page.
Definition: pqHelpWindow.h:48
This class provides a wrapper around QDesktopServices to fix a bug in opening URLs in firefox when tc...
Adds support for qthelp scheme links that load content from them QHelpEngine.
QString contentType(const QUrl &url)
Given a url return the content type of the resource based on the extension.
QtHelpUrlHandler(QHelpEngineCore *helpEngine, QObject *parent=nullptr)
void requestStarted(QWebEngineUrlRequestJob *request) override
QHelpEngineCore * m_helpEngine
virtual void showPage(const QString &url, bool linkClicked=false)
Requests showing of a particular page.
QWebEngineView * m_browser
Definition: pqHelpWindow.h:110
QHelpEngine * m_helpEngine
Definition: pqHelpWindow.h:109
pqHelpWindow(QHelpEngine *engine, QWidget *parent=nullptr, const Qt::WindowFlags &flags=Qt::WindowFlags())
virtual void showHomePage(const QString &namespace_name)
Tries to locate a file name index.html in the given namespace and then shows that page.
void updateNavButtons()
QMainWindow Superclass
Definition: pqHelpWindow.h:75
virtual void printPage()
Prints the current open page.
QToolButton * m_forward
Definition: pqHelpWindow.h:111
void helpWarnings(const QString &)
fired to relay warning messages from the help system.
bool isExistingPage(const QUrl &url)
Check if the url is an existing page.
void errorMissingPage(const QUrl &url)
Set the contents of the browser to show an error message.
QToolButton * m_backward
Definition: pqHelpWindow.h:112
virtual void showLinkedPage(const QUrl &url)
Show a page linked to by another page in the help window.
friend class pqNetworkAccessManager
Definition: pqHelpWindow.h:122
void linkHovered(const QString &link, const QString &title="", const QString &textContent="")
const QtHelpSchemeRegistration QTHELP_REGISTRATION
Register on the scheme on library load as it must be done before QApplication is created.