Changeset View
Changeset View
Standalone View
Standalone View
kdevplatform/documentation/standarddocumentationview_qtb.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * This file is part of KDevelop | ||||
3 | * Copyright 2010 Aleix Pol Gonzalez <aleixpol@kde.org> | ||||
4 | * Copyright 2016 Igor Kushnir <igorkuo@gmail.com> | ||||
5 | * Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com> | ||||
6 | * | ||||
7 | * This program is free software; you can redistribute it and/or modify | ||||
8 | * it under the terms of the GNU Library General Public License as | ||||
9 | * published by the Free Software Foundation; either version 2 of the | ||||
10 | * License, or (at your option) any later version. | ||||
11 | * | ||||
12 | * This program is distributed in the hope that it will be useful, | ||||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
15 | * GNU General Public License for more details. | ||||
16 | * | ||||
17 | * You should have received a copy of the GNU General Public | ||||
18 | * License along with this program; if not, write to the | ||||
19 | * Free Software Foundation, Inc., | ||||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
21 | */ | ||||
22 | | ||||
23 | #include "standarddocumentationview.h" | ||||
24 | #include "documentationfindwidget.h" | ||||
25 | #include "debug.h" | ||||
26 | | ||||
27 | #include <util/zoomcontroller.h> | ||||
28 | | ||||
29 | #include <KConfigGroup> | ||||
30 | #include <KSharedConfig> | ||||
31 | #include <KLocalizedString> | ||||
32 | | ||||
33 | #include <QVBoxLayout> | ||||
34 | #include <QContextMenuEvent> | ||||
35 | #include <QMenu> | ||||
36 | #include <QDesktopServices> | ||||
37 | | ||||
38 | #include <QTextBrowser> | ||||
39 | | ||||
40 | #include "standarddocumentationview_p.h" | ||||
41 | | ||||
42 | using namespace KDevelop; | ||||
43 | | ||||
44 | class KDevelop::HelpViewer : public QTextBrowser | ||||
45 | { | ||||
46 | Q_OBJECT | ||||
47 | public: | ||||
48 | | ||||
49 | HelpViewer(StandardDocumentationView* parent) | ||||
50 | : QTextBrowser(parent) | ||||
51 | , m_parent(parent) | ||||
52 | , m_loadFinished(false) | ||||
53 | , m_restoreTimer(0) | ||||
54 | {} | ||||
55 | | ||||
56 | void setSource(const QUrl& url) override | ||||
57 | { | ||||
58 | if (StandardDocumentationView::isUrlSchemeSupported(url)) { | ||||
59 | m_loadFinished = false; | ||||
60 | QTextBrowser::setSource(url); | ||||
61 | } else { | ||||
62 | bool ok = false; | ||||
63 | const QString& scheme = url.scheme(); | ||||
64 | if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) { | ||||
65 | ok = QDesktopServices::openUrl(url); | ||||
66 | } | ||||
67 | if (!ok) { | ||||
68 | qCDebug(DOCUMENTATION) << "ignoring unsupported url" << url; | ||||
69 | } | ||||
70 | } | ||||
71 | } | ||||
72 | | ||||
73 | void setUrlWithContent(const QUrl& url, const QByteArray& content) | ||||
74 | { | ||||
75 | if (StandardDocumentationView::isUrlSchemeSupported(url)) { | ||||
76 | m_requested = url; | ||||
77 | m_content = qCompress(content, 8); | ||||
78 | if (m_restoreTimer) { | ||||
79 | killTimer(m_restoreTimer); | ||||
80 | m_restoreTimer = 0; | ||||
81 | } | ||||
82 | } | ||||
83 | } | ||||
84 | | ||||
85 | void reload() override | ||||
86 | { | ||||
87 | if (m_restoreTimer) { | ||||
88 | killTimer(m_restoreTimer); | ||||
89 | m_restoreTimer = 0; | ||||
90 | qCDebug(DOCUMENTATION) << "queued restore of url" << m_requested; | ||||
91 | setSource(m_requested); | ||||
92 | } | ||||
93 | QTextBrowser::reload(); | ||||
94 | } | ||||
95 | | ||||
96 | void queueRestore(int delay) | ||||
97 | { | ||||
98 | if (m_restoreTimer) { | ||||
99 | // kill pending restore timer | ||||
100 | killTimer(m_restoreTimer); | ||||
101 | } | ||||
102 | m_restoreTimer = startTimer(delay); | ||||
103 | } | ||||
104 | | ||||
105 | // adapted from Qt's assistant | ||||
106 | QVariant loadResource(int type, const QUrl &name) override | ||||
107 | { | ||||
108 | // check if we have a callback and we're not loading a requested html url | ||||
109 | if (!(type == QTextDocument::HtmlResource && name == m_requested)) { | ||||
110 | // the callback is invoked with a QVariant that's explicitly invalid | ||||
111 | QVariant newContent(QVariant::Invalid); | ||||
112 | auto resolvedUrl = name; | ||||
113 | if (m_parent->loadResource(type, resolvedUrl, newContent)) { | ||||
114 | return newContent; | ||||
115 | } | ||||
116 | } | ||||
117 | if (type == QTextDocument::HtmlResource) { | ||||
118 | if (name == m_requested) { | ||||
119 | qCDebug(DOCUMENTATION) << "loadResource type" << type << "url" << name << "cached=" << m_requested; | ||||
120 | } else { | ||||
121 | // the current load is now finished, a new one | ||||
122 | // may be triggered by the slot connected to the | ||||
123 | // linkClicked() signal. | ||||
124 | // TODO: should we handle "file:///" URLs directly here? | ||||
125 | m_loadFinished = true; | ||||
126 | emit m_parent->linkClicked(name); | ||||
127 | } | ||||
128 | } else if (type != QTextDocument::StyleSheetResource) { | ||||
129 | m_loadFinished = true; | ||||
130 | qCDebug(DOCUMENTATION) << "HelpViewer::loadResource called with unsupported type" << type << "name=" << name; | ||||
131 | } | ||||
132 | // always just return the cached content | ||||
133 | return m_content.isEmpty() ? m_content : qUncompress(m_content); | ||||
134 | } | ||||
135 | | ||||
136 | void timerEvent(QTimerEvent *e) override | ||||
137 | { | ||||
138 | if (e->timerId() == m_restoreTimer) { | ||||
139 | reload(); | ||||
140 | } | ||||
141 | } | ||||
142 | | ||||
143 | StandardDocumentationView* m_parent; | ||||
144 | QUrl m_requested; | ||||
145 | QByteArray m_content; | ||||
146 | bool m_loadFinished; | ||||
147 | int m_restoreTimer; | ||||
148 | | ||||
149 | Q_SIGNALS: | ||||
150 | void loadFinished(const QUrl& url); | ||||
151 | | ||||
152 | public Q_SLOTS: | ||||
153 | void setLoadFinished(bool) | ||||
154 | { | ||||
155 | m_loadFinished = true; | ||||
156 | emit loadFinished(source()); | ||||
157 | if (m_restoreTimer) { | ||||
158 | reload(); | ||||
159 | } | ||||
160 | } | ||||
161 | }; | ||||
162 | | ||||
163 | void StandardDocumentationViewPrivate::init(StandardDocumentationView* parent) | ||||
164 | { | ||||
165 | m_parent = parent; | ||||
166 | m_view = new HelpViewer(parent); | ||||
167 | m_view->setContextMenuPolicy(Qt::NoContextMenu); | ||||
168 | parent->connect(m_view, &HelpViewer::loadFinished, parent, &StandardDocumentationView::linkClicked); | ||||
169 | parent->layout()->addWidget(m_view); | ||||
170 | } | ||||
171 | | ||||
172 | void StandardDocumentationViewPrivate::setup() | ||||
173 | { | ||||
174 | } | ||||
175 | | ||||
176 | void StandardDocumentationView::search ( const QString& text, DocumentationFindWidget::FindOptions options ) | ||||
177 | { | ||||
178 | typedef QTextDocument WebkitThing; | ||||
179 | WebkitThing::FindFlags ff = 0; | ||||
180 | if(options & DocumentationFindWidget::Previous) | ||||
181 | ff |= WebkitThing::FindBackward; | ||||
182 | | ||||
183 | if(options & DocumentationFindWidget::MatchCase) | ||||
184 | ff |= WebkitThing::FindCaseSensitively; | ||||
185 | | ||||
186 | d->m_view->find(text, ff); | ||||
187 | } | ||||
188 | | ||||
189 | void StandardDocumentationView::searchIncremental(const QString& text, DocumentationFindWidget::FindOptions options) | ||||
190 | { | ||||
191 | typedef QTextDocument WebkitThing; | ||||
192 | WebkitThing::FindFlags findFlags; | ||||
193 | | ||||
194 | if (options & DocumentationFindWidget::MatchCase) | ||||
195 | findFlags |= WebkitThing::FindCaseSensitively; | ||||
196 | | ||||
197 | d->m_view->find(text, findFlags); | ||||
198 | } | ||||
199 | | ||||
200 | void StandardDocumentationView::finishSearch() | ||||
201 | { | ||||
202 | // passing emptry string to reset search, as told in API docs | ||||
203 | d->m_view->find(QString()); | ||||
204 | } | ||||
205 | | ||||
206 | void KDevelop::StandardDocumentationView::setOverrideCss(const QUrl& url) | ||||
207 | { | ||||
208 | Q_UNUSED(url); | ||||
209 | return; | ||||
210 | } | ||||
211 | | ||||
212 | void KDevelop::StandardDocumentationView::load(const QUrl& url) | ||||
213 | { | ||||
214 | d->m_view->setSource(url); | ||||
215 | } | ||||
216 | | ||||
217 | void KDevelop::StandardDocumentationView::load(const QUrl& url, const QByteArray& content) | ||||
218 | { | ||||
219 | d->m_view->setUrlWithContent(url, content); | ||||
220 | d->m_view->setSource(url); | ||||
221 | } | ||||
222 | | ||||
223 | void KDevelop::StandardDocumentationView::restore() | ||||
224 | { | ||||
225 | // force a restore of the cached url/content | ||||
226 | // this has to be queued as we cannot be certain if | ||||
227 | // calling QTextBrowser::setSource() will have any | ||||
228 | // effect at all. | ||||
229 | d->m_view->queueRestore(250); | ||||
230 | } | ||||
231 | | ||||
232 | void KDevelop::StandardDocumentationView::setHtml(const QString& html) | ||||
233 | { | ||||
234 | d->m_view->setHtml(html); | ||||
235 | } | ||||
236 | | ||||
237 | void KDevelop::StandardDocumentationView::setNetworkAccessManager(QNetworkAccessManager* manager) | ||||
238 | { | ||||
239 | Q_UNUSED(manager); | ||||
240 | return; | ||||
241 | } | ||||
242 | | ||||
243 | void KDevelop::StandardDocumentationView::setDelegateLinks(bool delegate) | ||||
244 | { | ||||
245 | Q_UNUSED(delegate); | ||||
246 | return; | ||||
247 | } | ||||
248 | | ||||
249 | QMenu* StandardDocumentationView::createStandardContextMenu(const QPoint& pos) | ||||
250 | { | ||||
251 | auto menu = d->m_view->createStandardContextMenu(pos); | ||||
252 | QAction *reloadAction = new QAction(i18n("Reload"), menu); | ||||
253 | reloadAction->connect(reloadAction, &QAction::triggered, d->m_view, &HelpViewer::reload); | ||||
254 | menu->addAction(reloadAction); | ||||
255 | return menu; | ||||
256 | } | ||||
257 | | ||||
258 | bool StandardDocumentationView::eventFilter(QObject* object, QEvent* event) | ||||
259 | { | ||||
260 | return QWidget::eventFilter(object, event); | ||||
261 | } | ||||
262 | | ||||
263 | void StandardDocumentationView::updateZoomFactor(double zoomFactor) | ||||
264 | { | ||||
265 | double fontSize = d->m_view->font().pointSizeF(); | ||||
266 | if (fontSize <= 0) { | ||||
267 | return; | ||||
268 | } | ||||
269 | double newSize = fontSize * zoomFactor; | ||||
270 | if (newSize > fontSize) { | ||||
271 | d->m_view->zoomIn(int(newSize - fontSize + 0.5)); | ||||
272 | } else if (newSize != fontSize) { | ||||
273 | d->m_view->zoomOut(int(fontSize - newSize + 0.5)); | ||||
274 | } | ||||
275 | } | ||||
276 | | ||||
277 | QWidget* StandardDocumentationView::view() const | ||||
278 | { | ||||
279 | return d->m_view; | ||||
280 | } | ||||
281 | | ||||
282 | #include "standarddocumentationview_qtb.moc" |