Okular

kjs_app.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Pino Toscano <[email protected]>
3  SPDX-FileCopyrightText: 2008 Harri Porten <[email protected]>
4 
5  SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "kjs_app_p.h"
9 
10 #include <kjs/kjsarguments.h>
11 #include <kjs/kjsinterpreter.h>
12 #include <kjs/kjsobject.h>
13 #include <kjs/kjsprototype.h>
14 #include <kjs_version.h>
15 
16 #include <QApplication>
17 
18 #include <QLocale>
19 #include <QTimer>
20 
21 #include <KLocalizedString>
22 #include <QCheckBox>
23 #include <QMessageBox>
24 
25 #include "../document_p.h"
26 #include "../scripter.h"
27 #include "config-okular.h"
28 #include "kjs_fullscreen_p.h"
29 
30 using namespace Okular;
31 
32 #define OKULAR_TIMERID QStringLiteral("okular_timerID")
33 
34 static KJSPrototype *g_appProto;
36 Q_GLOBAL_STATIC(TimerCache, g_timerCache)
37 
38 // the acrobat version we fake
39 static const double fake_acroversion = 8.00;
40 
41 static const struct FakePluginInfo {
42  const char *name;
43  bool certified;
44  bool loaded;
45  const char *path;
46 } s_fake_plugins[] = {{"Annots", true, true, ""}, {"EFS", true, true, ""}, {"EScript", true, true, ""}, {"Forms", true, true, ""}, {"ReadOutLoud", true, true, ""}, {"WebLink", true, true, ""}};
47 static const int s_num_fake_plugins = sizeof(s_fake_plugins) / sizeof(s_fake_plugins[0]);
48 
49 static KJSObject appGetFormsVersion(KJSContext *, void *)
50 {
51  // faking a bit...
52  return KJSNumber(fake_acroversion);
53 }
54 
55 static KJSObject appGetLanguage(KJSContext *, void *)
56 {
58  QString lang = QLocale::languageToString(locale.language());
60  QString acroLang = QStringLiteral("ENU");
61  if (lang == QLatin1String("da")) {
62  acroLang = QStringLiteral("DAN"); // Danish
63  } else if (lang == QLatin1String("de")) {
64  acroLang = QStringLiteral("DEU"); // German
65  } else if (lang == QLatin1String("en")) {
66  acroLang = QStringLiteral("ENU"); // English
67  } else if (lang == QLatin1String("es")) {
68  acroLang = QStringLiteral("ESP"); // Spanish
69  } else if (lang == QLatin1String("fr")) {
70  acroLang = QStringLiteral("FRA"); // French
71  } else if (lang == QLatin1String("it")) {
72  acroLang = QStringLiteral("ITA"); // Italian
73  } else if (lang == QLatin1String("ko")) {
74  acroLang = QStringLiteral("KOR"); // Korean
75  } else if (lang == QLatin1String("ja")) {
76  acroLang = QStringLiteral("JPN"); // Japanese
77  } else if (lang == QLatin1String("nl")) {
78  acroLang = QStringLiteral("NLD"); // Dutch
79  } else if (lang == QLatin1String("pt") && country == QLatin1String("BR")) {
80  acroLang = QStringLiteral("PTB"); // Brazilian Portuguese
81  } else if (lang == QLatin1String("fi")) {
82  acroLang = QStringLiteral("SUO"); // Finnish
83  } else if (lang == QLatin1String("sv")) {
84  acroLang = QStringLiteral("SVE"); // Swedish
85  } else if (lang == QLatin1String("zh") && country == QLatin1String("CN")) {
86  acroLang = QStringLiteral("CHS"); // Chinese Simplified
87  } else if (lang == QLatin1String("zh") && country == QLatin1String("TW")) {
88  acroLang = QStringLiteral("CHT"); // Chinese Traditional
89  }
90  return KJSString(acroLang);
91 }
92 
93 static KJSObject appGetNumPlugins(KJSContext *, void *)
94 {
95  return KJSNumber(s_num_fake_plugins);
96 }
97 
98 static KJSObject appGetPlatform(KJSContext *, void *)
99 {
100 #if defined(Q_OS_WIN)
101  return KJSString(QString::fromLatin1("WIN"));
102 #elif defined(Q_OS_MAC)
103  return KJSString(QString::fromLatin1("MAC"));
104 #else
105  return KJSString(QStringLiteral("UNIX"));
106 #endif
107 }
108 
109 static KJSObject appGetPlugIns(KJSContext *context, void *)
110 {
111  KJSArray plugins(context, s_num_fake_plugins);
112  for (int i = 0; i < s_num_fake_plugins; ++i) {
113  const FakePluginInfo &info = s_fake_plugins[i];
114  KJSObject plugin;
115  plugin.setProperty(context, QStringLiteral("certified"), info.certified);
116  plugin.setProperty(context, QStringLiteral("loaded"), info.loaded);
117  plugin.setProperty(context, QStringLiteral("name"), info.name);
118  plugin.setProperty(context, QStringLiteral("path"), info.path);
119  plugin.setProperty(context, QStringLiteral("version"), fake_acroversion);
120  plugins.setProperty(context, QString::number(i), plugin);
121  }
122  return plugins;
123 }
124 
125 static KJSObject appGetPrintColorProfiles(KJSContext *context, void *)
126 {
127  return KJSArray(context, 0);
128 }
129 
130 static KJSObject appGetPrinterNames(KJSContext *context, void *)
131 {
132  return KJSArray(context, 0);
133 }
134 
135 static KJSObject appGetViewerType(KJSContext *, void *)
136 {
137  // faking a bit...
138  return KJSString(QStringLiteral("Reader"));
139 }
140 
141 static KJSObject appGetViewerVariation(KJSContext *, void *)
142 {
143  // faking a bit...
144  return KJSString(QStringLiteral("Reader"));
145 }
146 
147 static KJSObject appGetViewerVersion(KJSContext *, void *)
148 {
149  // faking a bit...
150  return KJSNumber(fake_acroversion);
151 }
152 
153 /*
154  Alert function defined in the reference, it shows a Dialog Box with options.
155  app.alert()
156 */
157 static KJSObject appAlert(KJSContext *context, void *, const KJSArguments &arguments)
158 {
159  if (arguments.count() < 1) {
160  return context->throwException(i18n("Missing alert type"));
161  }
162  KJSObject cMsg, nIcon, nType, cTitle, oCheckbox;
163 
164  if (arguments.at(0).isObject()) {
165  KJSObject obj = arguments.at(0);
166  cMsg = obj.property(context, QStringLiteral("cMsg"));
167  nIcon = obj.property(context, QStringLiteral("nIcon"));
168  nType = obj.property(context, QStringLiteral("nType"));
169  cTitle = obj.property(context, QStringLiteral("cTitle"));
170  oCheckbox = obj.property(context, QStringLiteral("oCheckbox"));
171  } else {
172  cMsg = arguments.at(0);
173  nIcon = arguments.at(1);
174  nType = arguments.at(2);
175  cTitle = arguments.at(3);
176  oCheckbox = arguments.at(5);
177  }
178 
180  if (nIcon.isNumber()) {
181  switch (nIcon.toInt32(context)) {
182  case 0:
183  icon = QMessageBox::Critical;
184  break;
185  case 1:
186  icon = QMessageBox::Warning;
187  break;
188  case 2:
189  icon = QMessageBox::Question;
190  break;
191  case 3:
193  break;
194  }
195  }
196 
197  const QString title = cTitle.isString() ? cTitle.toString(context) : QStringLiteral("Okular");
198  QMessageBox box(icon, title, cMsg.toString(context));
199 
201  if (nType.isNumber()) {
202  switch (nType.toInt32(context)) {
203  case 0:
204  buttons = QMessageBox::Ok;
205  break;
206  case 1:
208  break;
209  case 2:
210  buttons = QMessageBox::Yes | QMessageBox::No;
211  break;
212  case 3:
214  break;
215  }
216  }
217  box.setStandardButtons(buttons);
218 
219  QCheckBox *checkBox = nullptr;
220  if (oCheckbox.isObject()) {
221  KJSObject oMsg = oCheckbox.property(context, QStringLiteral("cMsg"));
222  QString msg = i18n("Do not show this message again");
223 
224  if (oMsg.isString()) {
225  msg = oMsg.toString(context);
226  }
227 
228  bool bInitialValue = false;
229  KJSObject value = oCheckbox.property(context, QStringLiteral("bInitialValue"));
230  if (value.isBoolean()) {
231  bInitialValue = value.toBoolean(context);
232  }
233  checkBox = new QCheckBox(msg);
234  checkBox->setChecked(bInitialValue);
235  box.setCheckBox(checkBox);
236  }
237 
238  // halt timeout until the user has responded
239  context->interpreter().stopTimeoutCheck();
240 
241  int button = box.exec();
242 
243  // restart max allowed time
244  context->interpreter().startTimeoutCheck();
245 
246  int ret;
247 
248  switch (button) {
249  case QMessageBox::Ok:
250  ret = 1;
251  break;
252  case QMessageBox::Cancel:
253  ret = 2;
254  break;
255  case QMessageBox::No:
256  ret = 3;
257  break;
258  case QMessageBox::Yes:
259  ret = 4;
260  break;
261  }
262 
263  if (checkBox) {
264  oCheckbox.setProperty(context, QStringLiteral("bAfterValue"), checkBox->isChecked());
265  }
266 
267  delete checkBox;
268 
269  return KJSNumber(ret);
270 }
271 
272 static KJSObject appBeep(KJSContext *context, void *, const KJSArguments &arguments)
273 {
274  if (arguments.count() < 1) {
275  return context->throwException(QStringLiteral("Missing beep type"));
276  }
278  return KJSUndefined();
279 }
280 
281 static KJSObject appGetNthPlugInName(KJSContext *context, void *, const KJSArguments &arguments)
282 {
283  if (arguments.count() < 1) {
284  return context->throwException(QStringLiteral("Missing plugin index"));
285  }
286  const int nIndex = arguments.at(0).toInt32(context);
287 
288  if (nIndex < 0 || nIndex >= s_num_fake_plugins) {
289  return context->throwException(QStringLiteral("PlugIn index out of bounds"));
290  }
291 
292  const FakePluginInfo &info = s_fake_plugins[nIndex];
293  return KJSString(info.name);
294 }
295 
296 static KJSObject appGoBack(KJSContext *, void *object, const KJSArguments &)
297 {
298  const DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
299  if (doc->m_parent->historyAtBegin()) {
300  return KJSUndefined();
301  }
302 
303  doc->m_parent->setPrevViewport();
304  return KJSUndefined();
305 }
306 
307 static KJSObject appGoForward(KJSContext *, void *object, const KJSArguments &)
308 {
309  const DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
310  if (doc->m_parent->historyAtEnd()) {
311  return KJSUndefined();
312  }
313 
314  doc->m_parent->setNextViewport();
315  return KJSUndefined();
316 }
317 
318 // app.setInterval()
319 static KJSObject appSetInterval(KJSContext *ctx, void *object, const KJSArguments &arguments)
320 {
321  DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
322  const QString function = arguments.at(0).toString(ctx) + QLatin1Char(';');
323  const int interval = arguments.at(1).toInt32(ctx);
324 
325  QTimer *timer = new QTimer();
326 
327  QObject::connect(timer, &QTimer::timeout, doc->m_parent, [=]() { doc->executeScript(function); });
328 
329  timer->start(interval);
330 
331  return JSApp::wrapTimer(ctx, timer);
332 }
333 
334 // app.clearInterval()
335 static KJSObject appClearInterval(KJSContext *ctx, void *, const KJSArguments &arguments)
336 {
337  KJSObject timerObject = arguments.at(0);
338  const int timerId = timerObject.property(ctx, OKULAR_TIMERID).toInt32(ctx);
339  QTimer *timer = g_timerCache->value(timerId);
340  if (timer != nullptr) {
341  timer->stop();
342  g_timerCache->remove(timerId);
343  delete timer;
344  }
345 
346  return KJSUndefined();
347 }
348 
349 // app.setTimeOut()
350 static KJSObject appSetTimeOut(KJSContext *ctx, void *object, const KJSArguments &arguments)
351 {
352  DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
353  const QString function = arguments.at(0).toString(ctx) + QLatin1Char(';');
354  const int interval = arguments.at(1).toInt32(ctx);
355 
356  QTimer *timer = new QTimer();
357  timer->setSingleShot(true);
358 
359  QObject::connect(timer, &QTimer::timeout, doc->m_parent, [=]() { doc->executeScript(function); });
360 
361  timer->start(interval);
362 
363  return JSApp::wrapTimer(ctx, timer);
364 }
365 
366 // app.clearTimeOut()
367 static KJSObject appClearTimeOut(KJSContext *ctx, void *, const KJSArguments &arguments)
368 {
369  KJSObject timerObject = arguments.at(0);
370  const int timerId = timerObject.property(ctx, OKULAR_TIMERID).toInt32(ctx);
371  QTimer *timer = g_timerCache->value(timerId);
372 
373  if (timer != nullptr) {
374  timer->stop();
375  g_timerCache->remove(timerId);
376  delete timer;
377  }
378 
379  return KJSUndefined();
380 }
381 
382 void JSApp::initType(KJSContext *ctx)
383 {
384  static bool initialized = false;
385  if (initialized) {
386  return;
387  }
388  initialized = true;
389 
390  g_appProto = new KJSPrototype();
391 
392  g_appProto->defineProperty(ctx, QStringLiteral("formsVersion"), appGetFormsVersion);
393  g_appProto->defineProperty(ctx, QStringLiteral("language"), appGetLanguage);
394  g_appProto->defineProperty(ctx, QStringLiteral("numPlugIns"), appGetNumPlugins);
395  g_appProto->defineProperty(ctx, QStringLiteral("platform"), appGetPlatform);
396  g_appProto->defineProperty(ctx, QStringLiteral("plugIns"), appGetPlugIns);
397  g_appProto->defineProperty(ctx, QStringLiteral("printColorProfiles"), appGetPrintColorProfiles);
398  g_appProto->defineProperty(ctx, QStringLiteral("printerNames"), appGetPrinterNames);
399  g_appProto->defineProperty(ctx, QStringLiteral("viewerType"), appGetViewerType);
400  g_appProto->defineProperty(ctx, QStringLiteral("viewerVariation"), appGetViewerVariation);
401  g_appProto->defineProperty(ctx, QStringLiteral("viewerVersion"), appGetViewerVersion);
402 
403  g_appProto->defineFunction(ctx, QStringLiteral("alert"), appAlert);
404  g_appProto->defineFunction(ctx, QStringLiteral("beep"), appBeep);
405  g_appProto->defineFunction(ctx, QStringLiteral("getNthPlugInName"), appGetNthPlugInName);
406  g_appProto->defineFunction(ctx, QStringLiteral("goBack"), appGoBack);
407  g_appProto->defineFunction(ctx, QStringLiteral("goForward"), appGoForward);
408  g_appProto->defineFunction(ctx, QStringLiteral("setInterval"), appSetInterval);
409  g_appProto->defineFunction(ctx, QStringLiteral("clearInterval"), appClearInterval);
410  g_appProto->defineFunction(ctx, QStringLiteral("setTimeOut"), appSetTimeOut);
411  g_appProto->defineFunction(ctx, QStringLiteral("clearTimeOut"), appClearTimeOut);
412 }
413 
414 KJSObject JSApp::object(KJSContext *ctx, DocumentPrivate *doc)
415 {
416  return g_appProto->constructObject(ctx, doc);
417 }
418 
419 KJSObject JSApp::wrapTimer(KJSContext *ctx, QTimer *timer)
420 {
421  KJSObject timerObject = g_appProto->constructObject(ctx, timer);
422  timerObject.setProperty(ctx, OKULAR_TIMERID, timer->timerId());
423 
424  g_timerCache->insert(timer->timerId(), timer);
425 
426  return timerObject;
427 }
428 
429 void JSApp::clearCachedFields()
430 {
431  if (g_timerCache) {
432  qDeleteAll(g_timerCache->begin(), g_timerCache->end());
433  g_timerCache->clear();
434  }
435 }
void stopTimeoutCheck()
bool toBoolean(KJSContext *ctx)
QString number(int n, int base)
int count() const
The documentation to the global Okular namespace.
Definition: action.h:16
bool isObject() const
void setSingleShot(bool singleShot)
bool isNumber() const
KI18NLOCALEDATA_EXPORT KCountry country(const char *ianaId)
void setChecked(bool)
KJSInterpreter interpreter()
typedef StandardButtons
int timerId() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
QString languageToString(QLocale::Language language)
void start(int msec)
QString i18n(const char *text, const TYPE &arg...)
KJSObject at(int idx) const
KJSObject property(KJSContext *ctx, const QString &name)
void timeout()
QString countryToString(QLocale::Country country)
KJSObject throwException(const QString &message) const
int toInt32(KJSContext *ctx)
void defineProperty(KJSContext *ctx, const QString &name, PropertyGetter getter, PropertySetter setter=nullptr)
LocaleWrapper locale()
void defineFunction(KJSContext *ctx, const QString &name, FunctionCall callback)
KJSObject constructObject(KJSContext *ctx, void *internalValue=nullptr)
bool isString() const
QString path(const QString &relativePath)
void stop()
QString fromLatin1(const char *str, int size)
const char * name(StandardAction id)
QString toString(KJSContext *ctx)
void startTimeoutCheck()
void setProperty(KJSContext *ctx, const QString &name, bool value)
bool isBoolean() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Mar 23 2023 04:04:24 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.