OSDN Git Service

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