From 6ea66ca3bd92d55ab1b0495d4844fcb5786c7845 Mon Sep 17 00:00:00 2001
From: kh1 <qt-info@nokia.com>
Date: Mon, 29 Mar 2010 19:16:23 +0200
Subject: [PATCH] Implement a common interface for help viewers.

Both implementations are now split into two source files, based on the
base class type. They share also a commen implementation, but the class
interface looks now clean to the outside. Adopt other classes for use.
---
 src/plugins/help/helpviewer.cpp     | 129 ++++++++
 src/plugins/help/helpviewer.h       | 135 +++++++++
 src/plugins/help/helpviewer_p.h     | 101 +++++++
 src/plugins/help/helpviewer_qtb.cpp | 334 +++++++++++++++++++++
 src/plugins/help/helpviewer_qwv.cpp | 441 ++++++++++++++++++++++++++++
 src/plugins/help/openpagesmanager.h |   3 +-
 src/plugins/help/openpagesmodel.h   |   3 +-
 7 files changed, 1143 insertions(+), 3 deletions(-)
 create mode 100644 src/plugins/help/helpviewer.cpp
 create mode 100644 src/plugins/help/helpviewer.h
 create mode 100644 src/plugins/help/helpviewer_p.h
 create mode 100644 src/plugins/help/helpviewer_qtb.cpp
 create mode 100644 src/plugins/help/helpviewer_qwv.cpp

diff --git a/src/plugins/help/helpviewer.cpp b/src/plugins/help/helpviewer.cpp
new file mode 100644
index 00000000000..a3a7bd2ecf6
--- /dev/null
+++ b/src/plugins/help/helpviewer.cpp
@@ -0,0 +1,129 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "helpviewer.h"
+#include "helpconstants.h"
+#include "helpmanager.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFileInfo>
+#include <QtCore/QStringBuilder>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QUrl>
+
+#include <QtGui/QDesktopServices>
+#include <QtGui/QMouseEvent>
+
+#include <QtHelp/QHelpEngineCore>
+
+using namespace Help::Internal;
+
+QString HelpViewer::AboutBlankPage =
+    QCoreApplication::translate("HelpViewer", "<title>about:blank</title>");
+
+QString HelpViewer::PageNotFoundMessage =
+    QCoreApplication::translate("HelpViewer", "<title>Error 404...</title><div "
+    "align=\"center\"><br><br><h1>The page could not be found</h1><br><h3>'%1'"
+    "</h3></div>");
+
+bool HelpViewer::isLocalUrl(const QUrl &url)
+{
+    const QString &scheme = url.scheme();
+    return scheme.isEmpty()
+        || scheme == QLatin1String("file")
+        || scheme == QLatin1String("qrc")
+        || scheme == QLatin1String("data")
+        || scheme == QLatin1String("qthelp")
+        || scheme == QLatin1String("about");
+}
+
+bool HelpViewer::canOpenPage(const QString &url)
+{
+    return url.endsWith(QLatin1String(".html"), Qt::CaseInsensitive)
+        || url.endsWith(QLatin1String(".htm"), Qt::CaseInsensitive)
+        || url == Help::Constants::AboutBlank;
+}
+
+bool HelpViewer::launchWithExternalApp(const QUrl &url)
+{
+    if (isLocalUrl(url)) {
+        const QHelpEngineCore &helpEngine = Help::HelpManager::helpEngineCore();
+        const QUrl &resolvedUrl = helpEngine.findFile(url);
+        if (!resolvedUrl.isValid())
+            return false;
+
+        const QString& path = resolvedUrl.path();
+        if (!canOpenPage(path)) {
+            QTemporaryFile tmpTmpFile;
+            if (!tmpTmpFile.open())
+                return false;
+
+            const QString &extension = QFileInfo(path).completeSuffix();
+            QFile actualTmpFile(tmpTmpFile.fileName() % QLatin1String(".")
+                % extension);
+            if (!actualTmpFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
+                return false;
+
+            actualTmpFile.write(helpEngine.fileData(resolvedUrl));
+            actualTmpFile.close();
+            return QDesktopServices::openUrl(QUrl(actualTmpFile.fileName()));
+        }
+    } else if (url.scheme() == QLatin1String("http")) {
+        return QDesktopServices::openUrl(url);
+    }
+    return false;
+}
+
+void HelpViewer::home()
+{
+    const QHelpEngineCore &engine = Help::HelpManager::helpEngineCore();
+    QString homepage = engine.customValue(QLatin1String("HomePage"),
+        QLatin1String("")).toString();
+
+    if (homepage.isEmpty()) {
+        homepage = engine.customValue(QLatin1String("DefaultHomePage"),
+            Help::Constants::AboutBlank).toString();
+    }
+
+    setSource(homepage);
+}
+
+bool HelpViewer::handleForwardBackwardMouseButtons(QMouseEvent *event)
+{
+    if (event->button() == Qt::XButton1) {
+        backward();
+        return true;
+    }
+    if (event->button() == Qt::XButton2) {
+        forward();
+        return true;
+    }
+
+    return false;
+}
diff --git a/src/plugins/help/helpviewer.h b/src/plugins/help/helpviewer.h
new file mode 100644
index 00000000000..489185d8efa
--- /dev/null
+++ b/src/plugins/help/helpviewer.h
@@ -0,0 +1,135 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef HELPVIEWER_H
+#define HELPVIEWER_H
+
+#include <find/ifindsupport.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/QString>
+#include <QtCore/QUrl>
+#include <QtCore/QVariant>
+
+#include <QtGui/QAction>
+#include <QtGui/QFont>
+
+#if defined(QT_NO_WEBKIT)
+#include <QtGui/QTextBrowser>
+#else
+#include <QtWebKit/QWebView>
+#endif
+
+namespace Help {
+    namespace Internal {
+
+#if !defined(QT_NO_WEBKIT)
+class HelpViewer : public QWebView
+#else
+class HelpViewer : public QTextBrowser
+#endif
+{
+    Q_OBJECT
+    class HelpViewerPrivate;
+    Q_DISABLE_COPY(HelpViewer)
+
+public:
+    HelpViewer(qreal zoom, QWidget *parent = 0);
+    ~HelpViewer();
+
+    QFont viewerFont() const;
+    void setViewerFont(const QFont &font);
+
+    void scaleUp();
+    void scaleDown();
+
+    void resetScale();
+    qreal scale() const;
+
+    QString title() const;
+    void setTitle(const QString &title);
+
+    QUrl source() const;
+    void setSource(const QUrl &url);
+
+    QString selectedText() const;
+    bool isForwardAvailable() const;
+    bool isBackwardAvailable() const;
+
+    bool findText(const QString &text, Find::IFindSupport::FindFlags flags,
+        bool incremental);
+
+    static QString AboutBlankPage;
+    static QString PageNotFoundMessage;
+
+    static bool isLocalUrl(const QUrl &url);
+    static bool canOpenPage(const QString &url);
+    static bool launchWithExternalApp(const QUrl &url);
+
+public slots:
+    void copy();
+    void home();
+
+    void forward();
+    void backward();
+
+signals:
+    void titleChanged();
+#if !defined(QT_NO_WEBKIT)
+    void sourceChanged(const QUrl &);
+    void forwardAvailable(bool enabled);
+    void backwardAvailable(bool enabled);
+#else
+    void loadFinished(bool finished);
+#endif
+
+protected:
+    void keyPressEvent(QKeyEvent *e);
+    void wheelEvent(QWheelEvent *event);
+    void mousePressEvent(QMouseEvent *event);
+    void mouseReleaseEvent(QMouseEvent *event);
+
+private slots:
+    void actionChanged();
+    void setLoadFinished(bool ok);
+
+private:
+    bool eventFilter(QObject *obj, QEvent *event);
+    void contextMenuEvent(QContextMenuEvent *event);
+    QVariant loadResource(int type, const QUrl &name);
+    bool handleForwardBackwardMouseButtons(QMouseEvent *e);
+
+private:
+    HelpViewerPrivate *d;
+};
+
+    }   // namespace Help
+}   // namespace Internal
+
+#endif  // HELPVIEWER_H
diff --git a/src/plugins/help/helpviewer_p.h b/src/plugins/help/helpviewer_p.h
new file mode 100644
index 00000000000..7ba7e95fd7c
--- /dev/null
+++ b/src/plugins/help/helpviewer_p.h
@@ -0,0 +1,101 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef HELPVIEWERPRIVATE_H
+#define HELPVIEWERPRIVATE_H
+
+#include "centralwidget.h"
+#include "helpviewer.h"
+#include "openpagesmanager.h"
+
+#include <QtCore/QObject>
+#include <QtGui/QTextBrowser>
+
+namespace Help {
+    namespace Internal {
+
+class HelpViewer::HelpViewerPrivate : public QObject
+{
+    Q_OBJECT
+
+public:
+    HelpViewerPrivate(int zoom)
+        : zoomCount(zoom)
+        , forceFont(false)
+        , lastAnchor(QString())
+        
+    {}
+
+    bool hasAnchorAt(QTextBrowser *browser, const QPoint& pos)
+    {
+        lastAnchor = browser->anchorAt(pos);
+        if (lastAnchor.isEmpty())
+            return false;
+
+        lastAnchor = browser->source().resolved(lastAnchor).toString();
+        if (lastAnchor.at(0) == QLatin1Char('#')) {
+            QString src = browser->source().toString();
+            int hsh = src.indexOf(QLatin1Char('#'));
+            lastAnchor = (hsh >= 0 ? src.left(hsh) : src) + lastAnchor;
+        }
+        return true;
+    }
+
+    void openLink(bool newPage)
+    {
+        if(lastAnchor.isEmpty())
+            return;
+        if (newPage)
+            OpenPagesManager::instance().createPage(lastAnchor);
+        else
+            CentralWidget::instance()->setSource(lastAnchor);
+        lastAnchor.clear();
+    }
+
+public slots:
+    void openLink()
+    {
+        openLink(false);
+    }
+
+    void openLinkInNewPage()
+    {
+        openLink(true);
+    }
+
+public:
+    int zoomCount;
+    bool forceFont;
+    QString lastAnchor;
+};
+
+    }   // namespace Help
+}   // namespace Internal
+
+#endif  // HELPVIEWERPRIVATE_H
diff --git a/src/plugins/help/helpviewer_qtb.cpp b/src/plugins/help/helpviewer_qtb.cpp
new file mode 100644
index 00000000000..b56a2bed5d2
--- /dev/null
+++ b/src/plugins/help/helpviewer_qtb.cpp
@@ -0,0 +1,334 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "helpviewer.h"
+
+#if defined(QT_NO_WEBKIT)
+
+#include "helpconstants.h"
+#include "helpviewer_p.h"
+#include "helpmanager.h"
+
+#include <QtGui/QApplication>
+#include <QtGui/QClipboard>
+#include <QtGui/QContextMenuEvent>
+#include <QtGui/QKeyEvent>
+#include <QtGui/QMenu>
+
+#include <QtHelp/QHelpEngineCore>
+
+using namespace Find;
+using namespace Help;
+using namespace Help::Internal;
+
+// -- HelpViewer
+
+HelpViewer::HelpViewer(qreal zoom, QWidget *parent)
+    : QTextBrowser(parent)
+    , d(new HelpViewerPrivate(zoom))
+{
+    installEventFilter(this);
+    document()->setDocumentMargin(8);
+
+    QFont font = viewerFont();
+    font.setPointSize(int(font.pointSize() + zoom));
+    setViewerFont(font);
+
+    connect(this, SIGNAL(sourceChanged(QUrl)), this, SIGNAL(titleChanged()));
+    connect(this, SIGNAL(loadFinished(bool)), this, SLOT(setLoadFinished(bool)));
+}
+
+HelpViewer::~HelpViewer()
+{
+    delete d;
+}
+
+QFont HelpViewer::viewerFont() const
+{
+    const QHelpEngineCore &engine = HelpManager::helpEngineCore();
+    return qVariantValue<QFont>(engine.customValue(QLatin1String("font"),
+        qApp->font()));
+}
+
+void HelpViewer::setViewerFont(const QFont &newFont)
+{
+    if (font() != newFont) {
+        d->forceFont = true;
+        setFont(newFont);
+        d->forceFont = false;
+    }
+}
+
+void HelpViewer::scaleUp()
+{
+    if (d->zoomCount < 10) {
+        d->zoomCount++;
+        d->forceFont = true;
+        zoomIn();
+        d->forceFont = false;
+    }
+}
+
+void HelpViewer::scaleDown()
+{
+    if (d->zoomCount > -5) {
+        d->zoomCount--;
+        d->forceFont = true;
+        zoomOut();
+        d->forceFont = false;
+    }
+}
+
+void HelpViewer::resetScale()
+{
+    if (d->zoomCount != 0) {
+        d->forceFont = true;
+        zoomOut(d->zoomCount);
+        d->forceFont = false;
+    }
+    d->zoomCount = 0;
+}
+
+qreal HelpViewer::scale() const
+{
+    return d->zoomCount;
+}
+
+QString HelpViewer::title() const
+{
+    return documentTitle();
+}
+
+void HelpViewer::setTitle(const QString &title)
+{
+    setDocumentTitle(title);
+}
+
+QUrl HelpViewer::source() const
+{
+    return QTextBrowser::source();
+}
+
+void HelpViewer::setSource(const QUrl &url)
+{
+    const QString &string = url.toString();
+    if (url.isValid() && string != QLatin1String("help")) {
+        if (launchWithExternalApp(url))
+            return;
+
+        const QHelpEngineCore &engine = HelpManager::instance().helpEngineCore();
+        const QUrl &resolvedUrl = engine.findFile(url);
+        if (resolvedUrl.isValid()) {
+            QTextBrowser::setSource(resolvedUrl);
+            emit loadFinished(true);
+            return;
+        }
+    }
+
+    QTextBrowser::setSource(url);
+    setHtml(string == Help::Constants::AboutBlank ? AboutBlankPage
+        : PageNotFoundMessage.arg(url.toString()));
+    emit loadFinished(true);
+}
+
+QString HelpViewer::selectedText() const
+{
+    return textCursor().selectedText();
+}
+
+bool HelpViewer::isForwardAvailable() const
+{
+    return QTextBrowser::isForwardAvailable();
+}
+
+bool HelpViewer::isBackwardAvailable() const
+{
+    return QTextBrowser::isBackwardAvailable();
+}
+
+bool HelpViewer::findText(const QString &text, IFindSupport::FindFlags flags,
+    bool incremental)
+{
+    QTextDocument *doc = document();
+    QTextCursor cursor = textCursor();
+
+    if (!doc || cursor.isNull())
+        return false;
+
+    if (incremental)
+        cursor.setPosition(cursor.selectionStart());
+
+    QTextDocument::FindFlags f = IFindSupport::textDocumentFlagsForFindFlags(flags);
+    QTextCursor found = doc->find(text, cursor, f);
+    if (found.isNull()) {
+        if ((flags & Find::IFindSupport::FindBackward) == 0)
+            cursor.movePosition(QTextCursor::Start);
+        else
+            cursor.movePosition(QTextCursor::End);
+        found = doc->find(text, cursor, f);
+        if (found.isNull()) {
+            return false;
+        }
+    }
+    if (!found.isNull()) {
+        setTextCursor(found);
+    }
+    return true;
+}
+
+// -- public slots
+
+void HelpViewer::copy()
+{
+    QTextBrowser::copy();
+}
+
+void HelpViewer::forward()
+{
+    QTextBrowser::forward();
+}
+
+void HelpViewer::backward()
+{
+    QTextBrowser::backward();
+}
+
+// -- protected
+
+void HelpViewer::keyPressEvent(QKeyEvent *e)
+{
+    if ((e->key() == Qt::Key_Home && e->modifiers() != Qt::NoModifier)
+        || (e->key() == Qt::Key_End && e->modifiers() != Qt::NoModifier)) {
+        QKeyEvent* event = new QKeyEvent(e->type(), e->key(), Qt::NoModifier,
+            e->text(), e->isAutoRepeat(), e->count());
+        e = event;
+    }
+    QTextBrowser::keyPressEvent(e);
+}
+
+void HelpViewer::wheelEvent(QWheelEvent *e)
+{
+    if (e->modifiers() == Qt::ControlModifier) {
+        e->accept();
+        e->delta() > 0 ? scaleUp() : scaleDown();
+    } else {
+        QTextBrowser::wheelEvent(e);
+    }
+}
+
+void HelpViewer::mousePressEvent(QMouseEvent *e)
+{
+#ifdef Q_OS_LINUX
+    if (handleForwardBackwardMouseButtons(e))
+        return;
+#endif
+    QTextBrowser::mousePressEvent(e);
+}
+
+void HelpViewer::mouseReleaseEvent(QMouseEvent *e)
+{
+#ifndef Q_OS_LINUX
+    if (handleForwardBackwardMouseButtons(e))
+        return;
+#endif
+
+    bool controlPressed = e->modifiers() & Qt::ControlModifier;
+    if ((controlPressed && d->hasAnchorAt(this, e->pos())) ||
+        (e->button() == Qt::MidButton && d->hasAnchorAt(this, e->pos()))) {
+        d->openLinkInNewPage();
+        return;
+    }
+
+    QTextBrowser::mouseReleaseEvent(e);
+}
+
+// -- private slots
+
+void HelpViewer::actionChanged()
+{
+    // stub
+}
+
+void HelpViewer::setLoadFinished(bool ok)
+{
+    Q_UNUSED(ok)
+    emit sourceChanged(source());
+}
+
+// -- private
+
+bool HelpViewer::eventFilter(QObject *obj, QEvent *event)
+{
+    if (event->type() == QEvent::FontChange && !d->forceFont)
+        return true;
+    return QTextBrowser::eventFilter(obj, event);
+}
+
+void HelpViewer::contextMenuEvent(QContextMenuEvent *event)
+{
+    QMenu menu(QLatin1String(""), 0);
+
+    QUrl link;
+    QAction *copyAnchorAction = 0;
+    if (d->hasAnchorAt(this, event->pos())) {
+        link = anchorAt(event->pos());
+        if (link.isRelative())
+            link = source().resolved(link);
+        menu.addAction(tr("Open Link"), d, SLOT(openLink()));
+        menu.addAction(tr("Open Link as New Page"), d, SLOT(openLinkInNewPage()));
+
+        if (!link.isEmpty() && link.isValid())
+            copyAnchorAction = menu.addAction(tr("Copy Link"));
+    } else if (!selectedText().isEmpty()) {
+        menu.addAction(tr("Copy"), this, SLOT(copy()));
+    } else {
+        menu.addAction(tr("Reload"), this, SLOT(reload()));
+    }
+
+    if (copyAnchorAction == menu.exec(event->globalPos()))
+        QApplication::clipboard()->setText(link.toString());
+}
+
+QVariant HelpViewer::loadResource(int type, const QUrl &name)
+{
+    QByteArray ba;
+    if (type < 4) {
+        const QHelpEngineCore &engine = HelpManager::instance().helpEngineCore();
+        ba = engine.fileData(name);
+        if (name.toString().endsWith(QLatin1String(".svg"), Qt::CaseInsensitive)) {
+            QImage image;
+            image.loadFromData(ba, "svg");
+            if (!image.isNull())
+                return image;
+        }
+    }
+    return ba;
+}
+
+#endif  // QT_NO_WEBKIT
diff --git a/src/plugins/help/helpviewer_qwv.cpp b/src/plugins/help/helpviewer_qwv.cpp
new file mode 100644
index 00000000000..5e463719a41
--- /dev/null
+++ b/src/plugins/help/helpviewer_qwv.cpp
@@ -0,0 +1,441 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "helpviewer.h"
+
+#if !defined(QT_NO_WEBKIT)
+
+#include "centralwidget.h"
+#include "helpconstants.h"
+#include "helpmanager.h"
+#include "openpagesmanager.h"
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QString>
+#include <QtCore/QStringBuilder>
+#include <QtCore/QTimer>
+
+#include <QtGui/QApplication>
+#include <QtGui/QWheelEvent>
+
+#include <QtHelp/QHelpEngineCore>
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkReply>
+#include <QtNetwork/QNetworkRequest>
+
+using namespace Find;
+using namespace Help;
+using namespace Help::Internal;
+
+// -- HelpNetworkReply
+
+class HelpNetworkReply : public QNetworkReply
+{
+public:
+    HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData,
+        const QString &mimeType);
+
+    virtual void abort();
+
+    virtual qint64 bytesAvailable() const
+        { return data.length() + QNetworkReply::bytesAvailable(); }
+
+protected:
+    virtual qint64 readData(char *data, qint64 maxlen);
+
+private:
+    QByteArray data;
+    qint64 origLen;
+};
+
+HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request,
+        const QByteArray &fileData, const QString& mimeType)
+    : data(fileData), origLen(fileData.length())
+{
+    setRequest(request);
+    setOpenMode(QIODevice::ReadOnly);
+
+    setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
+    setHeader(QNetworkRequest::ContentLengthHeader, QByteArray::number(origLen));
+    QTimer::singleShot(0, this, SIGNAL(metaDataChanged()));
+    QTimer::singleShot(0, this, SIGNAL(readyRead()));
+}
+
+void HelpNetworkReply::abort()
+{
+}
+
+qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen)
+{
+    qint64 len = qMin(qint64(data.length()), maxlen);
+    if (len) {
+        qMemCopy(buffer, data.constData(), len);
+        data.remove(0, len);
+    }
+    if (!data.length())
+        QTimer::singleShot(0, this, SIGNAL(finished()));
+    return len;
+}
+
+// -- HelpNetworkAccessManager
+
+class HelpNetworkAccessManager : public QNetworkAccessManager
+{
+public:
+    HelpNetworkAccessManager(QObject *parent);
+
+protected:
+    virtual QNetworkReply *createRequest(Operation op,
+        const QNetworkRequest &request, QIODevice *outgoingData = 0);
+};
+
+HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent)
+    : QNetworkAccessManager(parent)
+{
+}
+
+QNetworkReply *HelpNetworkAccessManager::createRequest(Operation /*op*/,
+    const QNetworkRequest &request, QIODevice* /*outgoingData*/)
+{
+    const QUrl& url = request.url();
+    QString mimeType = url.toString();
+    if (mimeType.endsWith(QLatin1String(".svg"))
+        || mimeType.endsWith(QLatin1String(".svgz"))) {
+            mimeType = QLatin1String("image/svg+xml");
+    } else if (mimeType.endsWith(QLatin1String(".css"))) {
+        mimeType = QLatin1String("text/css");
+    } else if (mimeType.endsWith(QLatin1String(".js"))) {
+        mimeType = QLatin1String("text/javascript");
+    } else if (mimeType.endsWith(QLatin1String(".txt"))) {
+        mimeType = QLatin1String("text/plain");
+    } else {
+        mimeType = QLatin1String("text/html");
+    }
+
+    const QHelpEngineCore &engine = HelpManager::helpEngineCore();
+    const QByteArray &data = engine.findFile(url).isValid()
+        ? engine.fileData(url) : HelpViewer::PageNotFoundMessage.arg(url.toString()).toUtf8();
+    return new HelpNetworkReply(request, data, mimeType);
+}
+
+// -- HelpPage
+
+class HelpPage : public QWebPage
+{
+public:
+    HelpPage(QObject *parent);
+
+protected:
+    virtual QWebPage *createWindow(QWebPage::WebWindowType);
+    virtual void triggerAction(WebAction action, bool checked = false);
+
+    virtual bool acceptNavigationRequest(QWebFrame *frame,
+        const QNetworkRequest &request, NavigationType type);
+
+private:
+    bool closeNewTabIfNeeded;
+
+    friend class HelpViewer;
+    Qt::MouseButtons m_pressedButtons;
+    Qt::KeyboardModifiers m_keyboardModifiers;
+};
+
+HelpPage::HelpPage(QObject *parent)
+    : QWebPage(parent)
+    , closeNewTabIfNeeded(false)
+    , m_pressedButtons(Qt::NoButton)
+    , m_keyboardModifiers(Qt::NoModifier)
+{
+}
+
+QWebPage *HelpPage::createWindow(QWebPage::WebWindowType)
+{
+    HelpPage* newPage = static_cast<HelpPage*>(OpenPagesManager::instance()
+        .createPage()->page());
+    newPage->closeNewTabIfNeeded = closeNewTabIfNeeded;
+    closeNewTabIfNeeded = false;
+    return newPage;
+}
+
+void HelpPage::triggerAction(WebAction action, bool checked)
+{
+    switch (action) {
+        case OpenLinkInNewWindow:
+            closeNewTabIfNeeded = true;
+        default:        // fall through
+            QWebPage::triggerAction(action, checked);
+            break;
+    }
+}
+
+bool HelpPage::acceptNavigationRequest(QWebFrame *,
+    const QNetworkRequest &request, QWebPage::NavigationType type)
+{
+    const bool closeNewTab = closeNewTabIfNeeded;
+    closeNewTabIfNeeded = false;
+
+    const QUrl &url = request.url();
+    if (HelpViewer::launchWithExternalApp(url)) {
+        if (closeNewTab)
+            QMetaObject::invokeMethod(&OpenPagesManager::instance(), "closeCurrentPage");
+        return false;
+    }
+
+    if (type == QWebPage::NavigationTypeLinkClicked
+        && (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton)) {
+            m_pressedButtons = Qt::NoButton;
+            m_keyboardModifiers = Qt::NoModifier;
+            OpenPagesManager::instance().createPage(url);
+            return false;
+    }
+
+    return true;
+}
+
+// -- HelpViewer
+
+HelpViewer::HelpViewer(qreal zoom, QWidget *parent)
+    : QWebView(parent)
+{
+    setAcceptDrops(false);
+    settings()->setAttribute(QWebSettings::JavaEnabled, false);
+    settings()->setAttribute(QWebSettings::PluginsEnabled, false);
+
+    setPage(new HelpPage(this));
+    page()->setNetworkAccessManager(new HelpNetworkAccessManager(this));
+
+    QAction* action = pageAction(QWebPage::OpenLinkInNewWindow);
+    action->setText(tr("Open Link as New Page"));
+
+    pageAction(QWebPage::DownloadLinkToDisk)->setVisible(false);
+    pageAction(QWebPage::DownloadImageToDisk)->setVisible(false);
+    pageAction(QWebPage::OpenImageInNewWindow)->setVisible(false);
+
+    connect(pageAction(QWebPage::Copy), SIGNAL(changed()), this,
+        SLOT(actionChanged()));
+    connect(pageAction(QWebPage::Back), SIGNAL(changed()), this,
+        SLOT(actionChanged()));
+    connect(pageAction(QWebPage::Forward), SIGNAL(changed()), this,
+        SLOT(actionChanged()));
+    connect(this, SIGNAL(urlChanged(QUrl)), this, SIGNAL(sourceChanged(QUrl)));
+    connect(this, SIGNAL(loadFinished(bool)), this, SLOT(setLoadFinished(bool)));
+    connect(this, SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged()));
+
+    setFont(viewerFont());
+    setTextSizeMultiplier(zoom == 0.0 ? 1.0 : zoom);
+}
+
+HelpViewer::~HelpViewer()
+{
+}
+
+QFont HelpViewer::viewerFont() const
+{
+    QWebSettings* webSettings = QWebSettings::globalSettings();
+    QFont font(QApplication::font().family(),
+        webSettings->fontSize(QWebSettings::DefaultFontSize));
+    const QHelpEngineCore &engine = HelpManager::helpEngineCore();
+    return qVariantValue<QFont>(engine.customValue(QLatin1String("font"),
+        font));
+}
+
+void HelpViewer::setViewerFont(const QFont &font)
+{
+    QWebSettings *webSettings = settings();
+    webSettings->setFontFamily(QWebSettings::StandardFont, font.family());
+    webSettings->setFontSize(QWebSettings::DefaultFontSize, font.pointSize());
+}
+
+void HelpViewer::scaleUp()
+{
+    setTextSizeMultiplier(textSizeMultiplier() + 0.1);
+}
+
+void HelpViewer::scaleDown()
+{
+    setTextSizeMultiplier(qMax(0.0, textSizeMultiplier() - 0.1));
+}
+
+void HelpViewer::resetScale()
+{
+    setTextSizeMultiplier(1.0);
+}
+
+qreal HelpViewer::scale() const
+{
+    return textSizeMultiplier();
+}
+
+QString HelpViewer::title() const
+{
+    return QWebView::title();
+}
+
+void HelpViewer::setTitle(const QString &title)
+{
+    Q_UNUSED(title)
+}
+
+QUrl HelpViewer::source() const
+{
+    return url();
+}
+
+void HelpViewer::setSource(const QUrl &url)
+{
+    load(url);
+}
+
+QString HelpViewer::selectedText() const
+{
+    return QWebView::selectedText();
+}
+
+bool HelpViewer::isForwardAvailable() const
+{
+    return pageAction(QWebPage::Forward)->isEnabled();
+}
+
+bool HelpViewer::isBackwardAvailable() const
+{
+    return pageAction(QWebPage::Back)->isEnabled();
+}
+
+bool HelpViewer::findText(const QString &text, IFindSupport::FindFlags flags,
+    bool incremental)
+{
+    Q_UNUSED(incremental)
+    QWebPage::FindFlags options = QWebPage::FindWrapsAroundDocument;
+    if (flags & Find::IFindSupport::FindBackward)
+        options |= QWebPage::FindBackward;
+    if (flags & Find::IFindSupport::FindCaseSensitively)
+        options |= QWebPage::FindCaseSensitively;
+
+    bool found = QWebView::findText(text, options);
+    options = QWebPage::HighlightAllOccurrences;
+    QWebView::findText(QLatin1String(""), options); // clear first
+    QWebView::findText(text, options); // force highlighting of all other matches
+    return found;
+}
+
+// -- public slots
+
+void HelpViewer::copy()
+{
+    triggerPageAction(QWebPage::Copy);
+}
+
+void HelpViewer::forward()
+{
+    QWebView::forward();
+}
+
+void HelpViewer::backward()
+{
+    back();
+}
+
+// -- protected
+
+void HelpViewer::keyPressEvent(QKeyEvent *e)
+{
+    // TODO: remove this once we support multiple keysequences per command
+    if (e->key() == Qt::Key_Insert && e->modifiers() == Qt::CTRL) {
+        if (!selectedText().isEmpty())
+            copy();
+    }
+    QWebView::keyPressEvent(e);
+}
+
+void HelpViewer::wheelEvent(QWheelEvent *event)
+{
+    if (event->modifiers()& Qt::ControlModifier) {
+        event->accept();
+        event->delta() > 0 ? scaleUp() : scaleDown();
+    } else {
+        QWebView::wheelEvent(event);
+    }
+}
+
+void HelpViewer::mousePressEvent(QMouseEvent *event)
+{
+#ifdef Q_OS_LINUX
+    if (handleForwardBackwardMouseButtons(event))
+        return;
+#endif
+
+    if (HelpPage *currentPage = static_cast<HelpPage*> (page())) {
+        currentPage->m_pressedButtons = event->buttons();
+        currentPage->m_keyboardModifiers = event->modifiers();
+    }
+
+    QWebView::mousePressEvent(event);
+}
+
+void HelpViewer::mouseReleaseEvent(QMouseEvent *event)
+{
+#ifndef Q_OS_LINUX
+    if (handleForwardBackwardMouseButtons(event))
+        return;
+#endif
+
+    QWebView::mouseReleaseEvent(event);
+}
+
+// -- private slots
+
+void HelpViewer::actionChanged()
+{
+    QAction *a = qobject_cast<QAction *>(sender());
+    if (a == pageAction(QWebPage::Back))
+        emit backwardAvailable(a->isEnabled());
+    else if (a == pageAction(QWebPage::Forward))
+        emit forwardAvailable(a->isEnabled());
+}
+
+void HelpViewer::setLoadFinished(bool ok)
+{
+    Q_UNUSED(ok)
+    emit sourceChanged(source());
+}
+
+// -- private
+
+bool HelpViewer::eventFilter(QObject *obj, QEvent *event)
+{
+    return QWebView::eventFilter(obj, event);
+}
+
+void HelpViewer::contextMenuEvent(QContextMenuEvent *event)
+{
+    QWebView::contextMenuEvent(event);
+}
+
+#endif  // !QT_NO_WEBKIT
diff --git a/src/plugins/help/openpagesmanager.h b/src/plugins/help/openpagesmanager.h
index a20332c860b..aa1303a2ce9 100644
--- a/src/plugins/help/openpagesmanager.h
+++ b/src/plugins/help/openpagesmanager.h
@@ -39,11 +39,10 @@ QT_FORWARD_DECLARE_CLASS(QTreeView)
 QT_FORWARD_DECLARE_CLASS(QUrl)
 QT_FORWARD_DECLARE_CLASS(QWidget)
 
-class HelpViewer;
-
 namespace Help {
     namespace Internal {
 
+class HelpViewer;
 class OpenPagesModel;
 
 class OpenPagesManager : public QObject
diff --git a/src/plugins/help/openpagesmodel.h b/src/plugins/help/openpagesmodel.h
index 2dc7c878819..a35b273963e 100644
--- a/src/plugins/help/openpagesmodel.h
+++ b/src/plugins/help/openpagesmodel.h
@@ -34,11 +34,12 @@
 #include <QtCore/QAbstractTableModel>
 
 QT_FORWARD_DECLARE_CLASS(QUrl)
-class HelpViewer;
 
 namespace Help {
     namespace Internal {
 
+class HelpViewer;
+
 class OpenPagesModel : public QAbstractTableModel
 {
     Q_OBJECT
-- 
GitLab