1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "helpviewer.h"
36 #if !defined(QT_NO_WEBKIT)
38 #include "centralwidget.h"
39 #include "helpconstants.h"
40 #include "localhelpmanager.h"
41 #include "openpagesmanager.h"
43 #include <QtCore/QFileInfo>
44 #include <QtCore/QString>
45 #include <QtCore/QStringBuilder>
46 #include <QtCore/QTimer>
48 #include <QtGui/QApplication>
49 #include <QtGui/QWheelEvent>
51 #include <QtHelp/QHelpEngine>
53 #include <QtNetwork/QNetworkAccessManager>
54 #include <QtNetwork/QNetworkReply>
55 #include <QtNetwork/QNetworkRequest>
59 using namespace Help::Internal;
61 // -- HelpNetworkReply
63 class HelpNetworkReply : public QNetworkReply
66 HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData,
67 const QString &mimeType);
69 virtual void abort() {}
71 virtual qint64 bytesAvailable() const
72 { return data.length() + QNetworkReply::bytesAvailable(); }
75 virtual qint64 readData(char *data, qint64 maxlen);
82 HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request,
83 const QByteArray &fileData, const QString& mimeType)
85 , dataLength(fileData.length())
88 setOpenMode(QIODevice::ReadOnly);
90 setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
91 setHeader(QNetworkRequest::ContentLengthHeader, QByteArray::number(dataLength));
92 QTimer::singleShot(0, this, SIGNAL(metaDataChanged()));
93 QTimer::singleShot(0, this, SIGNAL(readyRead()));
96 qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen)
98 qint64 len = qMin(qint64(data.length()), maxlen);
100 qMemCopy(buffer, data.constData(), len);
104 QTimer::singleShot(0, this, SIGNAL(finished()));
108 // -- HelpNetworkAccessManager
110 class HelpNetworkAccessManager : public QNetworkAccessManager
113 HelpNetworkAccessManager(QObject *parent);
116 virtual QNetworkReply *createRequest(Operation op,
117 const QNetworkRequest &request, QIODevice *outgoingData = 0);
120 HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent)
121 : QNetworkAccessManager(parent)
125 QNetworkReply *HelpNetworkAccessManager::createRequest(Operation op,
126 const QNetworkRequest &request, QIODevice* outgoingData)
128 if (!HelpViewer::isLocalUrl(request.url()))
129 return QNetworkAccessManager::createRequest(op, request, outgoingData);
131 QString url = request.url().toString();
132 const QHelpEngineCore &engine = LocalHelpManager::helpEngine();
133 // TODO: For some reason the url to load is already wrong (passed from webkit)
134 // though the css file and the references inside should work that way. One
135 // possible problem might be that the css is loaded at the same level as the
136 // html, thus a path inside the css like (../images/foo.png) might cd out of
137 // the virtual folder
138 if (!engine.findFile(url).isValid()) {
139 if (url.startsWith(HelpViewer::NsNokia) || url.startsWith(HelpViewer::NsTrolltech)) {
140 QUrl newUrl = request.url();
141 if (!newUrl.path().startsWith(QLatin1String("/qdoc/"))) {
142 newUrl.setPath(QLatin1String("/qdoc/") + newUrl.path());
143 url = newUrl.toString();
148 const QString &mimeType = HelpViewer::mimeFromUrl(url);
149 const QByteArray &data = engine.findFile(url).isValid() ? engine.fileData(url)
150 : HelpViewer::PageNotFoundMessage.arg(url).toUtf8();
152 return new HelpNetworkReply(request, data, mimeType.isEmpty()
153 ? QLatin1String("application/octet-stream") : mimeType);
158 class HelpPage : public QWebPage
161 HelpPage(QObject *parent);
164 virtual QWebPage *createWindow(QWebPage::WebWindowType);
165 virtual void triggerAction(WebAction action, bool checked = false);
167 virtual bool acceptNavigationRequest(QWebFrame *frame,
168 const QNetworkRequest &request, NavigationType type);
171 bool closeNewTabIfNeeded;
173 friend class Help::Internal::HelpViewer;
174 Qt::MouseButtons m_pressedButtons;
175 Qt::KeyboardModifiers m_keyboardModifiers;
178 HelpPage::HelpPage(QObject *parent)
180 , closeNewTabIfNeeded(false)
181 , m_pressedButtons(Qt::NoButton)
182 , m_keyboardModifiers(Qt::NoModifier)
186 QWebPage *HelpPage::createWindow(QWebPage::WebWindowType)
188 HelpPage* newPage = static_cast<HelpPage*>(OpenPagesManager::instance()
189 .createPage()->page());
190 newPage->closeNewTabIfNeeded = closeNewTabIfNeeded;
191 closeNewTabIfNeeded = false;
195 void HelpPage::triggerAction(WebAction action, bool checked)
198 case OpenLinkInNewWindow:
199 closeNewTabIfNeeded = true;
200 default: // fall through
201 QWebPage::triggerAction(action, checked);
206 bool HelpPage::acceptNavigationRequest(QWebFrame *,
207 const QNetworkRequest &request, QWebPage::NavigationType type)
209 const bool closeNewTab = closeNewTabIfNeeded;
210 closeNewTabIfNeeded = false;
212 const QUrl &url = request.url();
213 if (HelpViewer::launchWithExternalApp(url)) {
215 QMetaObject::invokeMethod(&OpenPagesManager::instance(), "closeCurrentPage");
219 if (type == QWebPage::NavigationTypeLinkClicked
220 && (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton)) {
221 m_pressedButtons = Qt::NoButton;
222 m_keyboardModifiers = Qt::NoModifier;
223 OpenPagesManager::instance().createPage(url);
232 HelpViewer::HelpViewer(qreal zoom, QWidget *parent)
235 setAcceptDrops(false);
236 installEventFilter(this);
238 settings()->setAttribute(QWebSettings::JavaEnabled, false);
239 settings()->setAttribute(QWebSettings::PluginsEnabled, false);
241 setPage(new HelpPage(this));
242 HelpNetworkAccessManager *manager = new HelpNetworkAccessManager(this);
243 page()->setNetworkAccessManager(manager);
244 connect(manager, SIGNAL(finished(QNetworkReply*)), this,
245 SLOT(slotNetworkReplyFinished(QNetworkReply*)));
247 QAction* action = pageAction(QWebPage::OpenLinkInNewWindow);
248 action->setText(tr("Open Link as New Page"));
250 pageAction(QWebPage::DownloadLinkToDisk)->setVisible(false);
251 pageAction(QWebPage::DownloadImageToDisk)->setVisible(false);
252 pageAction(QWebPage::OpenImageInNewWindow)->setVisible(false);
254 connect(pageAction(QWebPage::Copy), SIGNAL(changed()), this,
255 SLOT(actionChanged()));
256 connect(pageAction(QWebPage::Back), SIGNAL(changed()), this,
257 SLOT(actionChanged()));
258 connect(pageAction(QWebPage::Forward), SIGNAL(changed()), this,
259 SLOT(actionChanged()));
260 connect(this, SIGNAL(urlChanged(QUrl)), this, SIGNAL(sourceChanged(QUrl)));
261 connect(this, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted()));
262 connect(this, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinished(bool)));
263 connect(this, SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged()));
264 connect(page(), SIGNAL(printRequested(QWebFrame*)), this, SIGNAL(printRequested()));
266 setFont(viewerFont());
267 setTextSizeMultiplier(zoom == 0.0 ? 1.0 : zoom);
270 HelpViewer::~HelpViewer()
274 QFont HelpViewer::viewerFont() const
276 QWebSettings* webSettings = QWebSettings::globalSettings();
277 QFont font(QApplication::font().family(),
278 webSettings->fontSize(QWebSettings::DefaultFontSize));
279 const QHelpEngineCore &engine = LocalHelpManager::helpEngine();
280 return qVariantValue<QFont>(engine.customValue(QLatin1String("font"),
284 void HelpViewer::setViewerFont(const QFont &font)
286 QWebSettings *webSettings = settings();
287 webSettings->setFontFamily(QWebSettings::StandardFont, font.family());
288 webSettings->setFontSize(QWebSettings::DefaultFontSize, font.pointSize());
291 void HelpViewer::scaleUp()
293 setTextSizeMultiplier(textSizeMultiplier() + 0.1);
296 void HelpViewer::scaleDown()
298 setTextSizeMultiplier(qMax(0.0, textSizeMultiplier() - 0.1));
301 void HelpViewer::resetScale()
303 setTextSizeMultiplier(1.0);
306 qreal HelpViewer::scale() const
308 return textSizeMultiplier();
311 QString HelpViewer::title() const
313 return QWebView::title();
316 void HelpViewer::setTitle(const QString &title)
321 QUrl HelpViewer::source() const
326 void HelpViewer::setSource(const QUrl &url)
331 QString HelpViewer::selectedText() const
333 return QWebView::selectedText();
336 bool HelpViewer::isForwardAvailable() const
338 return pageAction(QWebPage::Forward)->isEnabled();
341 bool HelpViewer::isBackwardAvailable() const
343 return pageAction(QWebPage::Back)->isEnabled();
346 bool HelpViewer::findText(const QString &text, Find::FindFlags flags,
347 bool incremental, bool fromSearch, bool *wrapped)
349 Q_UNUSED(incremental);
350 Q_UNUSED(fromSearch);
353 QWebPage::FindFlags options;
354 if (flags & Find::FindBackward)
355 options |= QWebPage::FindBackward;
356 if (flags & Find::FindCaseSensitively)
357 options |= QWebPage::FindCaseSensitively;
359 bool found = QWebView::findText(text, options);
361 options |= QWebPage::FindWrapsAroundDocument;
362 found = QWebView::findText(text, options);
363 if (found && wrapped)
366 options = QWebPage::HighlightAllOccurrences;
367 QWebView::findText(QLatin1String(""), options); // clear first
368 QWebView::findText(text, options); // force highlighting of all other matches
374 void HelpViewer::copy()
376 triggerPageAction(QWebPage::Copy);
379 void HelpViewer::forward()
384 void HelpViewer::backward()
391 void HelpViewer::keyPressEvent(QKeyEvent *e)
393 // TODO: remove this once we support multiple keysequences per command
394 if (e->key() == Qt::Key_Insert && e->modifiers() == Qt::CTRL) {
395 if (!selectedText().isEmpty())
398 QWebView::keyPressEvent(e);
401 void HelpViewer::wheelEvent(QWheelEvent *event)
403 if (event->modifiers()& Qt::ControlModifier) {
405 event->delta() > 0 ? scaleUp() : scaleDown();
407 QWebView::wheelEvent(event);
411 void HelpViewer::mousePressEvent(QMouseEvent *event)
414 if (handleForwardBackwardMouseButtons(event))
418 if (HelpPage *currentPage = static_cast<HelpPage*> (page())) {
419 currentPage->m_pressedButtons = event->buttons();
420 currentPage->m_keyboardModifiers = event->modifiers();
423 QWebView::mousePressEvent(event);
426 void HelpViewer::mouseReleaseEvent(QMouseEvent *event)
429 if (handleForwardBackwardMouseButtons(event))
433 QWebView::mouseReleaseEvent(event);
438 void HelpViewer::actionChanged()
440 QAction *a = qobject_cast<QAction *>(sender());
441 if (a == pageAction(QWebPage::Back))
442 emit backwardAvailable(a->isEnabled());
443 else if (a == pageAction(QWebPage::Forward))
444 emit forwardAvailable(a->isEnabled());
447 void HelpViewer::slotNetworkReplyFinished(QNetworkReply *reply)
449 if (reply && reply->error() != QNetworkReply::NoError) {
450 setSource(QUrl(Help::Constants::AboutBlank));
451 setHtml(HelpViewer::PageNotFoundMessage.arg(reply->url().toString()
452 + QString::fromLatin1("<br><br>Error: %1").arg(reply->errorString())));
458 bool HelpViewer::eventFilter(QObject *obj, QEvent *event)
460 if (event->type() == QEvent::KeyPress) {
461 if (QKeyEvent *keyEvent = static_cast<QKeyEvent*> (event)) {
462 if (keyEvent->key() == Qt::Key_Slash)
463 emit openFindToolBar();
466 return QWebView::eventFilter(obj, event);
469 void HelpViewer::contextMenuEvent(QContextMenuEvent *event)
471 QWebView::contextMenuEvent(event);
474 #endif // !QT_NO_WEBKIT