OSDN Git Service

425cb6ac47dcce0f119103e16c9247efe98f374d
[qt-creator-jp/qt-creator-jp.git] / src / plugins / help / helpviewer_qwv.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "helpviewer.h"
35
36 #if !defined(QT_NO_WEBKIT)
37
38 #include "centralwidget.h"
39 #include "helpconstants.h"
40 #include "localhelpmanager.h"
41 #include "openpagesmanager.h"
42
43 #include <QtCore/QFileInfo>
44 #include <QtCore/QString>
45 #include <QtCore/QStringBuilder>
46 #include <QtCore/QTimer>
47
48 #include <QtGui/QApplication>
49 #include <QtGui/QWheelEvent>
50
51 #include <QtHelp/QHelpEngine>
52
53 #include <QtNetwork/QNetworkAccessManager>
54 #include <QtNetwork/QNetworkReply>
55 #include <QtNetwork/QNetworkRequest>
56
57 using namespace Find;
58 using namespace Help;
59 using namespace Help::Internal;
60
61 // -- HelpNetworkReply
62
63 class HelpNetworkReply : public QNetworkReply
64 {
65 public:
66     HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData,
67         const QString &mimeType);
68
69     virtual void abort() {}
70
71     virtual qint64 bytesAvailable() const
72         { return data.length() + QNetworkReply::bytesAvailable(); }
73
74 protected:
75     virtual qint64 readData(char *data, qint64 maxlen);
76
77 private:
78     QByteArray data;
79     qint64 dataLength;
80 };
81
82 HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request,
83         const QByteArray &fileData, const QString& mimeType)
84     : data(fileData)
85     , dataLength(fileData.length())
86 {
87     setRequest(request);
88     setOpenMode(QIODevice::ReadOnly);
89
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()));
94 }
95
96 qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen)
97 {
98     qint64 len = qMin(qint64(data.length()), maxlen);
99     if (len) {
100         qMemCopy(buffer, data.constData(), len);
101         data.remove(0, len);
102     }
103     if (!data.length())
104         QTimer::singleShot(0, this, SIGNAL(finished()));
105     return len;
106 }
107
108 // -- HelpNetworkAccessManager
109
110 class HelpNetworkAccessManager : public QNetworkAccessManager
111 {
112 public:
113     HelpNetworkAccessManager(QObject *parent);
114
115 protected:
116     virtual QNetworkReply *createRequest(Operation op,
117         const QNetworkRequest &request, QIODevice *outgoingData = 0);
118 };
119
120 HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent)
121     : QNetworkAccessManager(parent)
122 {
123 }
124
125 QNetworkReply *HelpNetworkAccessManager::createRequest(Operation op,
126     const QNetworkRequest &request, QIODevice* outgoingData)
127 {
128     if (!HelpViewer::isLocalUrl(request.url()))
129         return QNetworkAccessManager::createRequest(op, request, outgoingData);
130
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();
144             }
145         }
146     }
147
148     const QString &mimeType = HelpViewer::mimeFromUrl(url);
149     const QByteArray &data = engine.findFile(url).isValid() ? engine.fileData(url)
150         : HelpViewer::PageNotFoundMessage.arg(url).toUtf8();
151
152     return new HelpNetworkReply(request, data, mimeType.isEmpty()
153         ? QLatin1String("application/octet-stream") : mimeType);
154 }
155
156 // -- HelpPage
157
158 class HelpPage : public QWebPage
159 {
160 public:
161     HelpPage(QObject *parent);
162
163 protected:
164     virtual QWebPage *createWindow(QWebPage::WebWindowType);
165     virtual void triggerAction(WebAction action, bool checked = false);
166
167     virtual bool acceptNavigationRequest(QWebFrame *frame,
168         const QNetworkRequest &request, NavigationType type);
169
170 private:
171     bool closeNewTabIfNeeded;
172
173     friend class Help::Internal::HelpViewer;
174     Qt::MouseButtons m_pressedButtons;
175     Qt::KeyboardModifiers m_keyboardModifiers;
176 };
177
178 HelpPage::HelpPage(QObject *parent)
179     : QWebPage(parent)
180     , closeNewTabIfNeeded(false)
181     , m_pressedButtons(Qt::NoButton)
182     , m_keyboardModifiers(Qt::NoModifier)
183 {
184 }
185
186 QWebPage *HelpPage::createWindow(QWebPage::WebWindowType)
187 {
188     HelpPage* newPage = static_cast<HelpPage*>(OpenPagesManager::instance()
189         .createPage()->page());
190     newPage->closeNewTabIfNeeded = closeNewTabIfNeeded;
191     closeNewTabIfNeeded = false;
192     return newPage;
193 }
194
195 void HelpPage::triggerAction(WebAction action, bool checked)
196 {
197     switch (action) {
198         case OpenLinkInNewWindow:
199             closeNewTabIfNeeded = true;
200         default:        // fall through
201             QWebPage::triggerAction(action, checked);
202             break;
203     }
204 }
205
206 bool HelpPage::acceptNavigationRequest(QWebFrame *,
207     const QNetworkRequest &request, QWebPage::NavigationType type)
208 {
209     const bool closeNewTab = closeNewTabIfNeeded;
210     closeNewTabIfNeeded = false;
211
212     const QUrl &url = request.url();
213     if (HelpViewer::launchWithExternalApp(url)) {
214         if (closeNewTab)
215             QMetaObject::invokeMethod(&OpenPagesManager::instance(), "closeCurrentPage");
216         return false;
217     }
218
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);
224             return false;
225     }
226
227     return true;
228 }
229
230 // -- HelpViewer
231
232 HelpViewer::HelpViewer(qreal zoom, QWidget *parent)
233     : QWebView(parent)
234 {
235     setAcceptDrops(false);
236     installEventFilter(this);
237
238     settings()->setAttribute(QWebSettings::JavaEnabled, false);
239     settings()->setAttribute(QWebSettings::PluginsEnabled, false);
240
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*)));
246
247     QAction* action = pageAction(QWebPage::OpenLinkInNewWindow);
248     action->setText(tr("Open Link as New Page"));
249
250     pageAction(QWebPage::DownloadLinkToDisk)->setVisible(false);
251     pageAction(QWebPage::DownloadImageToDisk)->setVisible(false);
252     pageAction(QWebPage::OpenImageInNewWindow)->setVisible(false);
253
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()));
265
266     setFont(viewerFont());
267     setTextSizeMultiplier(zoom == 0.0 ? 1.0 : zoom);
268 }
269
270 HelpViewer::~HelpViewer()
271 {
272 }
273
274 QFont HelpViewer::viewerFont() const
275 {
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"),
281         font));
282 }
283
284 void HelpViewer::setViewerFont(const QFont &font)
285 {
286     QWebSettings *webSettings = settings();
287     webSettings->setFontFamily(QWebSettings::StandardFont, font.family());
288     webSettings->setFontSize(QWebSettings::DefaultFontSize, font.pointSize());
289 }
290
291 void HelpViewer::scaleUp()
292 {
293     setTextSizeMultiplier(textSizeMultiplier() + 0.1);
294 }
295
296 void HelpViewer::scaleDown()
297 {
298     setTextSizeMultiplier(qMax(0.0, textSizeMultiplier() - 0.1));
299 }
300
301 void HelpViewer::resetScale()
302 {
303     setTextSizeMultiplier(1.0);
304 }
305
306 qreal HelpViewer::scale() const
307 {
308     return textSizeMultiplier();
309 }
310
311 QString HelpViewer::title() const
312 {
313     return QWebView::title();
314 }
315
316 void HelpViewer::setTitle(const QString &title)
317 {
318     Q_UNUSED(title)
319 }
320
321 QUrl HelpViewer::source() const
322 {
323     return url();
324 }
325
326 void HelpViewer::setSource(const QUrl &url)
327 {
328     load(url);
329 }
330
331 QString HelpViewer::selectedText() const
332 {
333     return QWebView::selectedText();
334 }
335
336 bool HelpViewer::isForwardAvailable() const
337 {
338     return pageAction(QWebPage::Forward)->isEnabled();
339 }
340
341 bool HelpViewer::isBackwardAvailable() const
342 {
343     return pageAction(QWebPage::Back)->isEnabled();
344 }
345
346 bool HelpViewer::findText(const QString &text, Find::FindFlags flags,
347     bool incremental, bool fromSearch, bool *wrapped)
348 {
349     Q_UNUSED(incremental);
350     Q_UNUSED(fromSearch);
351     if (wrapped)
352         *wrapped = false;
353     QWebPage::FindFlags options;
354     if (flags & Find::FindBackward)
355         options |= QWebPage::FindBackward;
356     if (flags & Find::FindCaseSensitively)
357         options |= QWebPage::FindCaseSensitively;
358
359     bool found = QWebView::findText(text, options);
360     if (!found) {
361         options |= QWebPage::FindWrapsAroundDocument;
362         found = QWebView::findText(text, options);
363         if (found && wrapped)
364             *wrapped = true;
365     }
366     options = QWebPage::HighlightAllOccurrences;
367     QWebView::findText(QLatin1String(""), options); // clear first
368     QWebView::findText(text, options); // force highlighting of all other matches
369     return found;
370 }
371
372 // -- public slots
373
374 void HelpViewer::copy()
375 {
376     triggerPageAction(QWebPage::Copy);
377 }
378
379 void HelpViewer::forward()
380 {
381     QWebView::forward();
382 }
383
384 void HelpViewer::backward()
385 {
386     back();
387 }
388
389 // -- protected
390
391 void HelpViewer::keyPressEvent(QKeyEvent *e)
392 {
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())
396             copy();
397     }
398     QWebView::keyPressEvent(e);
399 }
400
401 void HelpViewer::wheelEvent(QWheelEvent *event)
402 {
403     if (event->modifiers()& Qt::ControlModifier) {
404         event->accept();
405         event->delta() > 0 ? scaleUp() : scaleDown();
406     } else {
407         QWebView::wheelEvent(event);
408     }
409 }
410
411 void HelpViewer::mousePressEvent(QMouseEvent *event)
412 {
413 #ifdef Q_OS_LINUX
414     if (handleForwardBackwardMouseButtons(event))
415         return;
416 #endif
417
418     if (HelpPage *currentPage = static_cast<HelpPage*> (page())) {
419         currentPage->m_pressedButtons = event->buttons();
420         currentPage->m_keyboardModifiers = event->modifiers();
421     }
422
423     QWebView::mousePressEvent(event);
424 }
425
426 void HelpViewer::mouseReleaseEvent(QMouseEvent *event)
427 {
428 #ifndef Q_OS_LINUX
429     if (handleForwardBackwardMouseButtons(event))
430         return;
431 #endif
432
433     QWebView::mouseReleaseEvent(event);
434 }
435
436 // -- private slots
437
438 void HelpViewer::actionChanged()
439 {
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());
445 }
446
447 void HelpViewer::slotNetworkReplyFinished(QNetworkReply *reply)
448 {
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())));
453     }
454 }
455
456 // -- private
457
458 bool HelpViewer::eventFilter(QObject *obj, QEvent *event)
459 {
460     if (event->type() == QEvent::KeyPress) {
461         if (QKeyEvent *keyEvent = static_cast<QKeyEvent*> (event)) {
462             if (keyEvent->key() == Qt::Key_Slash)
463                 emit openFindToolBar();
464         }
465     }
466     return QWebView::eventFilter(obj, event);
467 }
468
469 void HelpViewer::contextMenuEvent(QContextMenuEvent *event)
470 {
471     QWebView::contextMenuEvent(event);
472 }
473
474 #endif  // !QT_NO_WEBKIT