Okular

js_app.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
3 SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "js_app_p.h"
9
10#include <QApplication>
11
12#include <QLocale>
13#include <QTimer>
14
15#include <KLocalizedString>
16#include <QCheckBox>
17#include <QJSEngine>
18#include <QMenu>
19#include <QMessageBox>
20
21#include "../document_p.h"
22#include "../scripter.h"
23#include "config-okular.h"
24#include "js_fullscreen_p.h"
25
26using namespace Okular;
27
28#define OKULAR_TIMERID QStringLiteral("okular_timerID")
29
31Q_GLOBAL_STATIC(TimerCache, g_timerCache)
32
33// the acrobat version we fake
34static const double fake_acroversion = 8.00;
35
36// The property used to hold the value to return from app.popUpMenuEx
37static const char *kResultProperty = "result";
38
39static const struct FakePluginInfo {
40 const char *name;
41 bool certified;
42 bool loaded;
43 const char *path;
44} s_fake_plugins[] = {{"Annots", true, true, ""}, {"EFS", true, true, ""}, {"EScript", true, true, ""}, {"Forms", true, true, ""}, {"ReadOutLoud", true, true, ""}, {"WebLink", true, true, ""}};
45static const int s_num_fake_plugins = sizeof(s_fake_plugins) / sizeof(s_fake_plugins[0]);
46
47int JSApp::formsVersion() const
48{
49 // faking a bit...
50 return fake_acroversion;
51}
52
53QString JSApp::language() const
54{
55 QLocale locale;
58 QString acroLang = QStringLiteral("ENU");
59 if (lang == QLatin1String("da")) {
60 acroLang = QStringLiteral("DAN"); // Danish
61 } else if (lang == QLatin1String("de")) {
62 acroLang = QStringLiteral("DEU"); // German
63 } else if (lang == QLatin1String("en")) {
64 acroLang = QStringLiteral("ENU"); // English
65 } else if (lang == QLatin1String("es")) {
66 acroLang = QStringLiteral("ESP"); // Spanish
67 } else if (lang == QLatin1String("fr")) {
68 acroLang = QStringLiteral("FRA"); // French
69 } else if (lang == QLatin1String("it")) {
70 acroLang = QStringLiteral("ITA"); // Italian
71 } else if (lang == QLatin1String("ko")) {
72 acroLang = QStringLiteral("KOR"); // Korean
73 } else if (lang == QLatin1String("ja")) {
74 acroLang = QStringLiteral("JPN"); // Japanese
75 } else if (lang == QLatin1String("nl")) {
76 acroLang = QStringLiteral("NLD"); // Dutch
77 } else if (lang == QLatin1String("pt") && country == QLatin1String("BR")) {
78 acroLang = QStringLiteral("PTB"); // Brazilian Portuguese
79 } else if (lang == QLatin1String("fi")) {
80 acroLang = QStringLiteral("SUO"); // Finnish
81 } else if (lang == QLatin1String("sv")) {
82 acroLang = QStringLiteral("SVE"); // Swedish
83 } else if (lang == QLatin1String("zh") && country == QLatin1String("CN")) {
84 acroLang = QStringLiteral("CHS"); // Chinese Simplified
85 } else if (lang == QLatin1String("zh") && country == QLatin1String("TW")) {
86 acroLang = QStringLiteral("CHT"); // Chinese Traditional
87 }
88 return acroLang;
89}
90
91int JSApp::numPlugIns() const
92{
93 return s_num_fake_plugins;
94}
95
96QString JSApp::platform() const
97{
98#if defined(Q_OS_WIN)
99 return QString::fromLatin1("WIN");
100#elif defined(Q_OS_MAC)
101 return QString::fromLatin1("MAC");
102#else
103 return QStringLiteral("UNIX");
104#endif
105}
106
107QJSValue JSApp::plugIns() const
108{
109 QJSValue plugins = qjsEngine(this)->newArray(s_num_fake_plugins);
110 for (int i = 0; i < s_num_fake_plugins; ++i) {
111 const FakePluginInfo &info = s_fake_plugins[i];
112 QJSValue plugin = qjsEngine(this)->newObject();
113 plugin.setProperty(QStringLiteral("certified"), info.certified);
114 plugin.setProperty(QStringLiteral("loaded"), info.loaded);
115 plugin.setProperty(QStringLiteral("name"), info.name);
116 plugin.setProperty(QStringLiteral("path"), info.path);
117 plugin.setProperty(QStringLiteral("version"), fake_acroversion);
118 plugins.setProperty(i, plugin);
119 }
120 return plugins;
121}
122
123QStringList JSApp::printColorProfiles() const
124{
125 return QStringList();
126}
127
128QStringList JSApp::printerNames() const
129{
130 return QStringList();
131}
132
133QString JSApp::viewerType() const
134{
135 // faking a bit...
136 return QStringLiteral("Reader");
137}
138
139QString JSApp::viewerVariation() const
140{
141 // faking a bit...
142 return QStringLiteral("Reader");
143}
144
145int JSApp::viewerVersion() const
146{
147 // faking a bit...
148 return fake_acroversion;
149}
150
151/*
152 Alert function defined in the reference, it shows a Dialog Box with options.
153 app.alert()
154*/
155int JSApp::alert(const QJSValue &arguments)
156{
157 const auto cMsg = arguments.property(QStringLiteral("cMsg")).toString();
158 const auto nIcon = arguments.property(QStringLiteral("nIcon")).toInt();
159 const auto nType = arguments.property(QStringLiteral("nType")).toInt();
160 const auto cTitle = arguments.property(QStringLiteral("cTitle")).toString();
161 const auto oCheckbox = arguments.property(QStringLiteral("oCheckbox"));
162 return alert(cMsg, nIcon, nType, cTitle, QJSValue(), oCheckbox);
163}
164
165int JSApp::alert(const QString &cMsg, int nIcon, int nType, const QString &cTitle, [[maybe_unused]] const QJSValue &oDoc, const QJSValue &oCheckbox)
166{
168 switch (nIcon) {
169 case 0:
171 break;
172 case 1:
174 break;
175 case 2:
177 break;
178 case 3:
180 break;
181 }
182
183 const QString title = !cTitle.isEmpty() ? cTitle : QStringLiteral("Okular");
184 QMessageBox box(icon, title, cMsg);
185
187 switch (nType) {
188 case 0:
189 buttons = QMessageBox::Ok;
190 break;
191 case 1:
193 break;
194 case 2:
196 break;
197 case 3:
199 break;
200 }
201 box.setStandardButtons(buttons);
202
203 QCheckBox *checkBox = nullptr;
204 if (oCheckbox.isObject()) {
205 const auto oMsg = oCheckbox.property(QStringLiteral("cMsg"));
206 QString msg = i18n("Do not show this message again");
207
208 if (oMsg.isString()) {
209 msg = oMsg.toString();
210 }
211
212 bool bInitialValue = false;
213 const auto value = oCheckbox.property(QStringLiteral("bInitialValue"));
214 if (value.isBool()) {
215 bInitialValue = value.toBool();
216 }
217 checkBox = new QCheckBox(msg);
218 checkBox->setChecked(bInitialValue);
219 box.setCheckBox(checkBox);
220 }
221
222 // halt timeout until the user has responded
223 QMetaObject::invokeMethod(m_watchdogTimer, qOverload<>(&QTimer::stop));
224
225 int button = box.exec();
226
227 // restart max allowed time
228 QMetaObject::invokeMethod(m_watchdogTimer, qOverload<>(&QTimer::start));
229
230 int ret = 0;
231
232 switch (button) {
233 case QMessageBox::Ok:
234 ret = 1;
235 break;
237 ret = 2;
238 break;
239 case QMessageBox::No:
240 ret = 3;
241 break;
242 case QMessageBox::Yes:
243 ret = 4;
244 break;
245 }
246
247 if (checkBox) {
248 QJSValue(oCheckbox).setProperty(QStringLiteral("bAfterValue"), checkBox->isChecked());
249 }
250
251 delete checkBox;
252
253 return ret;
254}
255
256void JSApp::beep([[maybe_unused]] int nType)
257{
259}
260
261QJSValue JSApp::getNthPlugInName(int nIndex) const
262{
263 if (nIndex < 0 || nIndex >= s_num_fake_plugins) {
264 return qjsEngine(this)->newErrorObject(QJSValue::TypeError, QStringLiteral("PlugIn index out of bounds"));
265 }
266
267 const FakePluginInfo &info = s_fake_plugins[nIndex];
268 return info.name;
269}
270
271void JSApp::goBack()
272{
273 if (!m_doc->m_parent->historyAtBegin()) {
274 m_doc->m_parent->setPrevViewport();
275 }
276}
277
278void JSApp::goForward()
279{
280 if (!m_doc->m_parent->historyAtEnd()) {
281 m_doc->m_parent->setNextViewport();
282 }
283}
284
285// app.setInterval()
286QJSValue JSApp::setInterval(const QString &cExpr, int nMilliseconds)
287{
288 QTimer *timer = new QTimer();
289
290 QObject::connect(timer, &QTimer::timeout, m_doc->m_parent, [=]() { m_doc->executeScript(cExpr); });
291
292 timer->start(nMilliseconds);
293
294 return JSApp::wrapTimer(timer);
295}
296
297// app.clearInterval()
298void JSApp::clearInterval(const QJSValue &oInterval)
299{
300 const int timerId = oInterval.property(OKULAR_TIMERID).toInt();
301 QTimer *timer = g_timerCache->value(timerId);
302 if (timer != nullptr) {
303 timer->stop();
304 g_timerCache->remove(timerId);
305 delete timer;
306 }
307}
308
309// app.setTimeOut()
310QJSValue JSApp::setTimeOut(const QString &cExpr, int nMilliseconds)
311{
312 QTimer *timer = new QTimer();
313 timer->setSingleShot(true);
314
315 QObject::connect(timer, &QTimer::timeout, m_doc->m_parent, [=]() { m_doc->executeScript(cExpr); });
316
317 timer->start(nMilliseconds);
318
319 return JSApp::wrapTimer(timer);
320}
321
322// app.clearTimeOut()
323void JSApp::clearTimeOut(const QJSValue &oTime)
324{
325 const int timerId = oTime.property(OKULAR_TIMERID).toInt();
326 QTimer *timer = g_timerCache->value(timerId);
327
328 if (timer != nullptr) {
329 timer->stop();
330 g_timerCache->remove(timerId);
331 delete timer;
332 }
333}
334
335// app.popUpMenuEx()
336
337bool JSApp::createPopUpMenuTree(int depth, QMenu *rootMenu, const QJSValue &arguments)
338{
339 const int nArgs = arguments.property(QStringLiteral("length")).toInt();
340
341 // If no menu to add or if we got too deep in recursion
342 if (nArgs == 0 || depth > 20) {
343 return false;
344 }
345
346 for (int i = 0; i < nArgs; ++i) {
347 const QJSValue item = arguments.property(i);
348 const QString cName = item.property(QStringLiteral("cName")).toString();
349 const QJSValue cResultProperty = item.property(QStringLiteral("cResult"));
350 const QJSValue oSubMenu = item.property(QStringLiteral("oSubMenu"));
351
352 if (oSubMenu.isArray()) {
353 QMenu *subMenu = rootMenu->addMenu(cName);
354 createPopUpMenuTree(depth + 1, subMenu, oSubMenu);
355 } else {
356 QAction *a = rootMenu->addAction(cName);
357 if (cResultProperty.isUndefined()) {
358 a->setProperty(kResultProperty, cName);
359 } else {
360 a->setProperty(kResultProperty, cResultProperty.toString());
361 }
362 }
363 }
364
365 return true;
366}
367
368QJSValue JSApp::okular_popUpMenuEx(const QJSValue &arguments)
369{
370 QMenu m;
371
372 // Object name is used for tests.
373 m.setObjectName(QStringLiteral("popUpMenuEx"));
374
375 if (!createPopUpMenuTree(0, &m, arguments)) {
376 return {};
377 }
378
379 QAction *result = m.exec(QCursor::pos());
380 return result ? result->property(kResultProperty).toString() : QString();
381}
382
383JSApp::JSApp(DocumentPrivate *doc, QTimer *watchdogTimer, QObject *parent)
384 : QObject(parent)
385 , m_doc(doc)
386 , m_watchdogTimer(watchdogTimer)
387{
388}
389
390JSApp::~JSApp() = default;
391
392QJSValue JSApp::wrapTimer(QTimer *timer) const
393{
394 QJSValue timerObject = qjsEngine(this)->newObject();
395 timerObject.setProperty(OKULAR_TIMERID, timer->timerId());
396
397 g_timerCache->insert(timer->timerId(), timer);
398
399 return timerObject;
400}
401
402void JSApp::clearCachedFields()
403{
404 if (g_timerCache) {
405 qDeleteAll(g_timerCache->begin(), g_timerCache->end());
406 g_timerCache->clear();
407 }
408}
QString i18n(const char *text, const TYPE &arg...)
KI18NLOCALEDATA_EXPORT KCountry country(const char *ianaId)
global.h
Definition action.h:17
void setChecked(bool)
QPoint pos()
bool isArray() const const
bool isObject() const const
bool isUndefined() const const
QJSValue property(const QString &name) const const
void setProperty(const QString &name, const QJSValue &value)
qint32 toInt() const const
QString toString() const const
Language language() const const
QString languageToString(Language language)
Territory territory() const const
QString territoryToString(Territory territory)
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
QAction * addMenu(QMenu *menu)
QAction * exec()
typedef StandardButtons
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QVariant property(const char *name) const const
void setObjectName(QAnyStringView name)
bool setProperty(const char *name, QVariant &&value)
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
void setSingleShot(bool singleShot)
void start()
void stop()
void timeout()
int timerId() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:35 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.