1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "helpviewer.h"
35 #if !defined(QT_NO_WEBKIT)
37 #include "centralwidget.h"
38 #include "helpconstants.h"
39 #include "localhelpmanager.h"
40 #include "openpagesmanager.h"
42 #include <QtCore/QFileInfo>
43 #include <QtCore/QString>
44 #include <QtCore/QStringBuilder>
45 #include <QtCore/QTimer>
47 #include <QtGui/QApplication>
48 #include <QtGui/QWheelEvent>
50 #include <QtHelp/QHelpEngine>
52 #include <QtNetwork/QNetworkAccessManager>
53 #include <QtNetwork/QNetworkReply>
54 #include <QtNetwork/QNetworkRequest>
58 using namespace Help::Internal;
60 // -- HelpNetworkReply
62 class HelpNetworkReply : public QNetworkReply
65 HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData,
66 const QString &mimeType);
68 virtual void abort() {}
70 virtual qint64 bytesAvailable() const
71 { return data.length() + QNetworkReply::bytesAvailable(); }
74 virtual qint64 readData(char *data, qint64 maxlen);
81 HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request,
82 const QByteArray &fileData, const QString& mimeType)
84 , dataLength(fileData.length())
87 setOpenMode(QIODevice::ReadOnly);
89 setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
90 setHeader(QNetworkRequest::ContentLengthHeader, QByteArray::number(dataLength));
91 QTimer::singleShot(0, this, SIGNAL(metaDataChanged()));
92 QTimer::singleShot(0, this, SIGNAL(readyRead()));
95 qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen)
97 qint64 len = qMin(qint64(data.length()), maxlen);
99 qMemCopy(buffer, data.constData(), len);
103 QTimer::singleShot(0, this, SIGNAL(finished()));
107 // -- HelpNetworkAccessManager
109 class HelpNetworkAccessManager : public QNetworkAccessManager
112 HelpNetworkAccessManager(QObject *parent);
115 virtual QNetworkReply *createRequest(Operation op,
116 const QNetworkRequest &request, QIODevice *outgoingData = 0);
119 HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent)
120 : QNetworkAccessManager(parent)
124 QNetworkReply *HelpNetworkAccessManager::createRequest(Operation op,
125 const QNetworkRequest &request, QIODevice* outgoingData)
127 if (!HelpViewer::isLocalUrl(request.url()))
128 return QNetworkAccessManager::createRequest(op, request, outgoingData);
130 QString url = request.url().toString();
131 const QHelpEngineCore &engine = LocalHelpManager::helpEngine();
132 // TODO: For some reason the url to load is already wrong (passed from webkit)
133 // though the css file and the references inside should work that way. One
134 // possible problem might be that the css is loaded at the same level as the
135 // html, thus a path inside the css like (../images/foo.png) might cd out of
136 // the virtual folder
137 if (!engine.findFile(url).isValid()) {
138 if (url.startsWith(HelpViewer::NsNokia) || url.startsWith(HelpViewer::NsTrolltech)) {
139 QUrl newUrl = request.url();
140 if (!newUrl.path().startsWith(QLatin1String("/qdoc/"))) {
141 newUrl.setPath(QLatin1String("/qdoc/") + newUrl.path());
142 url = newUrl.toString();
147 const QString &mimeType = HelpViewer::mimeFromUrl(url);
148 const QByteArray &data = engine.findFile(url).isValid() ? engine.fileData(url)
149 : HelpViewer::PageNotFoundMessage.arg(url).toUtf8();
151 return new HelpNetworkReply(request, data, mimeType.isEmpty()
152 ? QLatin1String("application/octet-stream") : mimeType);
157 class HelpPage : public QWebPage
160 HelpPage(QObject *parent);
163 virtual QWebPage *createWindow(QWebPage::WebWindowType);
164 virtual void triggerAction(WebAction action, bool checked = false);
166 virtual bool acceptNavigationRequest(QWebFrame *frame,
167 const QNetworkRequest &request, NavigationType type);
170 bool closeNewTabIfNeeded;
172 friend class Help::Internal::HelpViewer;
173 Qt::MouseButtons m_pressedButtons;
174 Qt::KeyboardModifiers m_keyboardModifiers;
177 HelpPage::HelpPage(QObject *parent)
179 , closeNewTabIfNeeded(false)
180 , m_pressedButtons(Qt::NoButton)
181 , m_keyboardModifiers(Qt::NoModifier)
185 QWebPage *HelpPage::createWindow(QWebPage::WebWindowType)
187 HelpPage* newPage = static_cast<HelpPage*>(OpenPagesManager::instance()
188 .createPage()->page());
189 newPage->closeNewTabIfNeeded = closeNewTabIfNeeded;
190 closeNewTabIfNeeded = false;
194 void HelpPage::triggerAction(WebAction action, bool checked)
197 case OpenLinkInNewWindow:
198 closeNewTabIfNeeded = true;
199 default: // fall through
200 QWebPage::triggerAction(action, checked);
205 bool HelpPage::acceptNavigationRequest(QWebFrame *,
206 const QNetworkRequest &request, QWebPage::NavigationType type)
208 const bool closeNewTab = closeNewTabIfNeeded;
209 closeNewTabIfNeeded = false;
211 const QUrl &url = request.url();
212 if (HelpViewer::launchWithExternalApp(url)) {
214 QMetaObject::invokeMethod(&OpenPagesManager::instance(), "closeCurrentPage");
218 if (type == QWebPage::NavigationTypeLinkClicked
219 && (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton)) {
220 m_pressedButtons = Qt::NoButton;
221 m_keyboardModifiers = Qt::NoModifier;
222 OpenPagesManager::instance().createPage(url);
231 HelpViewer::HelpViewer(qreal zoom, QWidget *parent)
234 setAcceptDrops(false);
235 installEventFilter(this);
237 settings()->setAttribute(QWebSettings::JavaEnabled, false);
238 settings()->setAttribute(QWebSettings::PluginsEnabled, false);
240 setPage(new HelpPage(this));
241 HelpNetworkAccessManager *manager = new HelpNetworkAccessManager(this);
242 page()->setNetworkAccessManager(manager);
243 connect(manager, SIGNAL(finished(QNetworkReply*)), this,
244 SLOT(slotNetworkReplyFinished(QNetworkReply*)));
246 QAction* action = pageAction(QWebPage::OpenLinkInNewWindow);
247 action->setText(tr("Open Link as New Page"));
249 pageAction(QWebPage::DownloadLinkToDisk)->setVisible(false);
250 pageAction(QWebPage::DownloadImageToDisk)->setVisible(false);
251 pageAction(QWebPage::OpenImageInNewWindow)->setVisible(false);
253 connect(pageAction(QWebPage::Copy), SIGNAL(changed()), this,
254 SLOT(actionChanged()));
255 connect(pageAction(QWebPage::Back), SIGNAL(changed()), this,
256 SLOT(actionChanged()));
257 connect(pageAction(QWebPage::Forward), SIGNAL(changed()), this,
258 SLOT(actionChanged()));
259 connect(this, SIGNAL(urlChanged(QUrl)), this, SIGNAL(sourceChanged(QUrl)));
260 connect(this, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted()));
261 connect(this, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinished(bool)));
262 connect(this, SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged()));
263 connect(page(), SIGNAL(printRequested(QWebFrame*)), this, SIGNAL(printRequested()));
265 setFont(viewerFont());
266 setTextSizeMultiplier(zoom == 0.0 ? 1.0 : zoom);
269 HelpViewer::~HelpViewer()
273 QFont HelpViewer::viewerFont() const
275 QWebSettings* webSettings = QWebSettings::globalSettings();
276 QFont font(QApplication::font().family(),
277 webSettings->fontSize(QWebSettings::DefaultFontSize));
278 const QHelpEngineCore &engine = LocalHelpManager::helpEngine();
279 return qVariantValue<QFont>(engine.customValue(QLatin1String("font"),
283 void HelpViewer::setViewerFont(const QFont &font)
285 QWebSettings *webSettings = settings();
286 webSettings->setFontFamily(QWebSettings::StandardFont, font.family());
287 webSettings->setFontSize(QWebSettings::DefaultFontSize, font.pointSize());
290 void HelpViewer::scaleUp()
292 setTextSizeMultiplier(textSizeMultiplier() + 0.1);
295 void HelpViewer::scaleDown()
297 setTextSizeMultiplier(qMax(0.0, textSizeMultiplier() - 0.1));
300 void HelpViewer::resetScale()
302 setTextSizeMultiplier(1.0);
305 qreal HelpViewer::scale() const
307 return textSizeMultiplier();
310 QString HelpViewer::title() const
312 return QWebView::title();
315 void HelpViewer::setTitle(const QString &title)
320 QUrl HelpViewer::source() const
325 void HelpViewer::setSource(const QUrl &url)
330 QString HelpViewer::selectedText() const
332 return QWebView::selectedText();
335 bool HelpViewer::isForwardAvailable() const
337 return pageAction(QWebPage::Forward)->isEnabled();
340 bool HelpViewer::isBackwardAvailable() const
342 return pageAction(QWebPage::Back)->isEnabled();
345 bool HelpViewer::findText(const QString &text, Find::FindFlags flags,
346 bool incremental, bool fromSearch, bool *wrapped)
348 Q_UNUSED(incremental);
349 Q_UNUSED(fromSearch);
352 QWebPage::FindFlags options;
353 if (flags & Find::FindBackward)
354 options |= QWebPage::FindBackward;
355 if (flags & Find::FindCaseSensitively)
356 options |= QWebPage::FindCaseSensitively;
358 bool found = QWebView::findText(text, options);
360 options |= QWebPage::FindWrapsAroundDocument;
361 found = QWebView::findText(text, options);
362 if (found && wrapped)
365 options = QWebPage::HighlightAllOccurrences;
366 QWebView::findText(QLatin1String(""), options); // clear first
367 QWebView::findText(text, options); // force highlighting of all other matches
373 void HelpViewer::copy()
375 triggerPageAction(QWebPage::Copy);
378 void HelpViewer::forward()
383 void HelpViewer::backward()
390 void HelpViewer::keyPressEvent(QKeyEvent *e)
392 // TODO: remove this once we support multiple keysequences per command
393 if (e->key() == Qt::Key_Insert && e->modifiers() == Qt::CTRL) {
394 if (!selectedText().isEmpty())
397 QWebView::keyPressEvent(e);
400 void HelpViewer::wheelEvent(QWheelEvent *event)
402 if (event->modifiers()& Qt::ControlModifier) {
404 event->delta() > 0 ? scaleUp() : scaleDown();
406 QWebView::wheelEvent(event);
410 void HelpViewer::mousePressEvent(QMouseEvent *event)
413 if (handleForwardBackwardMouseButtons(event))
417 if (HelpPage *currentPage = static_cast<HelpPage*> (page())) {
418 currentPage->m_pressedButtons = event->buttons();
419 currentPage->m_keyboardModifiers = event->modifiers();
422 QWebView::mousePressEvent(event);
425 void HelpViewer::mouseReleaseEvent(QMouseEvent *event)
428 if (handleForwardBackwardMouseButtons(event))
432 QWebView::mouseReleaseEvent(event);
437 void HelpViewer::actionChanged()
439 QAction *a = qobject_cast<QAction *>(sender());
440 if (a == pageAction(QWebPage::Back))
441 emit backwardAvailable(a->isEnabled());
442 else if (a == pageAction(QWebPage::Forward))
443 emit forwardAvailable(a->isEnabled());
446 void HelpViewer::slotNetworkReplyFinished(QNetworkReply *reply)
448 if (reply && reply->error() != QNetworkReply::NoError) {
449 setSource(QUrl(Help::Constants::AboutBlank));
450 setHtml(HelpViewer::PageNotFoundMessage.arg(reply->url().toString()
451 + QString::fromLatin1("<br><br>Error: %1").arg(reply->errorString())));
457 bool HelpViewer::eventFilter(QObject *obj, QEvent *event)
459 if (event->type() == QEvent::KeyPress) {
460 if (QKeyEvent *keyEvent = static_cast<QKeyEvent*> (event)) {
461 if (keyEvent->key() == Qt::Key_Slash)
462 emit openFindToolBar();
465 return QWebView::eventFilter(obj, event);
468 void HelpViewer::contextMenuEvent(QContextMenuEvent *event)
470 QWebView::contextMenuEvent(event);
473 #endif // !QT_NO_WEBKIT