Okular

js_field.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_field_p.h"
9
10#include <QDebug>
11#include <QHash>
12#include <QJSEngine>
13#include <QTimer>
14
15#include "../debug_p.h"
16#include "../document_p.h"
17#include "../form.h"
18#include "../page.h"
19#include "../page_p.h"
20#include "js_display_p.h"
21
22using namespace Okular;
23
24#define OKULAR_NAME QStringLiteral("okular_name")
25
27Q_GLOBAL_STATIC(FormCache, g_fieldCache)
29Q_GLOBAL_STATIC(ButtonCache, g_buttonCache)
30
31// Helper for modified fields
32static void updateField(FormField *field)
33{
34 Page *page = g_fieldCache->value(field);
35 if (page) {
36 Document *doc = PagePrivate::get(page)->m_doc->m_parent;
37 const int pageNumber = page->number();
38 QTimer::singleShot(0, doc, [doc, pageNumber] { doc->refreshPixmaps(pageNumber); });
39 Q_EMIT doc->refreshFormWidget(field);
40 } else {
41 qWarning() << "Could not get page of field" << field;
42 }
43}
44
45// Field.doc
46QJSValue JSField::doc() const
47{
48 return qjsEngine(this)->globalObject().property(QStringLiteral("Doc"));
49}
50
51// Field.name
52QString JSField::name() const
53{
54 return m_field->fullyQualifiedName();
55}
56
57// Field.readonly (getter)
58bool JSField::readonly() const
59{
60 return m_field->isReadOnly();
61}
62
63// Field.readonly (setter)
64void JSField::setReadonly(bool readonly)
65{
66 m_field->setReadOnly(readonly);
67
68 updateField(m_field);
69}
70
71static QString fieldGetTypeHelper(const FormField *field)
72{
73 switch (field->type()) {
75 const FormFieldButton *button = static_cast<const FormFieldButton *>(field);
76 switch (button->buttonType()) {
78 return QStringLiteral("button");
80 return QStringLiteral("checkbox");
82 return QStringLiteral("radiobutton");
83 }
84 break;
85 }
87 return QStringLiteral("text");
89 const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(field);
90 switch (choice->choiceType()) {
92 return QStringLiteral("combobox");
94 return QStringLiteral("listbox");
95 }
96 break;
97 }
99 return QStringLiteral("signature");
100 }
101 return QString();
102}
103
104// Field.type
105QString JSField::type() const
106{
107 return fieldGetTypeHelper(m_field);
108}
109
110QJSValue JSField::fieldGetValueCore(bool asString) const
111{
113
114 switch (m_field->type()) {
116 const FormFieldButton *button = static_cast<const FormFieldButton *>(m_field);
117 if (button->state()) {
118 result = QStringLiteral("Yes");
119 } else {
120 result = QStringLiteral("Off");
121 }
122 break;
123 }
124 case FormField::FormText: {
125 const FormFieldText *text = static_cast<const FormFieldText *>(m_field);
126 const QLocale locale(QStringLiteral("en_US"));
127 QString numericalText = text->text();
128 numericalText.replace(QStringLiteral(","), QStringLiteral(".")); // As we do not need to account for thousand separator, commas are definitely decimal separators if they appear
129 bool ok;
130 const double textAsNumber = locale.toDouble(numericalText, &ok);
131 if (ok && !asString) {
132 result = textAsNumber;
133 } else {
134 result = text->text();
135 }
136 break;
137 }
139 const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(m_field);
140 const QList<int> currentChoices = choice->currentChoices();
141 if (choice->choiceType() == FormFieldChoice::ComboBox) {
142 if (currentChoices.count() == 0 && choice->isEditable()) {
143 result = choice->editChoice();
144 }
145 } else if (choice->choiceType() == FormFieldChoice::ListBox) {
146 // value returns array for a listbox with multiple selections
147 if (currentChoices.count() > 1) {
148 result = qjsEngine(this)->newArray(currentChoices.count());
149 int arrayIndex = 0;
150 for (const int &selectionIndex : currentChoices) {
151 result.setProperty(arrayIndex++, choice->exportValueForChoice(choice->choices().at(selectionIndex)));
152 }
153 }
154 }
155 // for both combobox and listbox, return the selection if exactly one item is selected
156 if (currentChoices.count() == 1) {
157 result = choice->exportValueForChoice(choice->choices().at(currentChoices[0]));
158 }
159 break;
160 }
162 break;
163 }
164 }
165
166 qCDebug(OkularCoreDebug) << "fieldGetValueCore:"
167 << " Field: " << m_field->fullyQualifiedName() << " Type: " << fieldGetTypeHelper(m_field) << " Value: " << result.toString() << (result.isString() ? "(as string)" : "");
168 return result;
169}
170// Field.value (getter)
171QJSValue JSField::value() const
172{
173 return fieldGetValueCore(/*asString*/ false);
174}
175
176// Field.value (setter)
177void JSField::setValue(const QJSValue &value)
178{
179 qCDebug(OkularCoreDebug) << "fieldSetValue: Field: " << m_field->fullyQualifiedName() << " Type: " << fieldGetTypeHelper(m_field) << " Value: " << value.toString();
180 switch (m_field->type()) {
182 FormFieldButton *button = static_cast<FormFieldButton *>(m_field);
183 const QString text = value.toString();
184 if (text == QStringLiteral("Yes")) {
185 button->setState(true);
186 updateField(m_field);
187 } else if (text == QStringLiteral("Off")) {
188 button->setState(false);
189 updateField(m_field);
190 }
191 break;
192 }
193 case FormField::FormText: {
194 FormFieldText *textField = static_cast<FormFieldText *>(m_field);
195 const QString text = value.toString();
196 if (text != textField->text()) {
197 textField->setText(text);
198 updateField(m_field);
199 }
200 break;
201 }
203 FormFieldChoice *choice = static_cast<FormFieldChoice *>(m_field);
204 Q_UNUSED(choice); // ###
205 break;
206 }
208 break;
209 }
210 }
211}
212
213// Field.valueAsString (getter)
214QJSValue JSField::valueAsString() const
215{
216 return fieldGetValueCore(/*asString*/ true);
217}
218
219// Field.hidden (getter)
220bool JSField::hidden() const
221{
222 return !m_field->isVisible();
223}
224
225// Field.hidden (setter)
226void JSField::setHidden(bool hidden)
227{
228 m_field->setVisible(!hidden);
229
230 updateField(m_field);
231}
232
233// Field.display (getter)
234int JSField::display() const
235{
236 bool visible = m_field->isVisible();
237 if (visible) {
238 return m_field->isPrintable() ? FormDisplay::FormVisible : FormDisplay::FormNoPrint;
239 }
240 return m_field->isPrintable() ? FormDisplay::FormNoView : FormDisplay::FormHidden;
241}
242
243// Field.display (setter)
244void JSField::setDisplay(int display)
245{
246 switch (display) {
247 case FormDisplay::FormVisible:
248 m_field->setVisible(true);
249 m_field->setPrintable(true);
250 break;
251 case FormDisplay::FormHidden:
252 m_field->setVisible(false);
253 m_field->setPrintable(false);
254 break;
255 case FormDisplay::FormNoPrint:
256 m_field->setVisible(true);
257 m_field->setPrintable(false);
258 break;
259 case FormDisplay::FormNoView:
260 m_field->setVisible(false);
261 m_field->setPrintable(true);
262 break;
263 }
264 updateField(m_field);
265}
266
267QJSValue JSField::numItems() const
268{
270 if (m_field->type() == FormField::FormChoice) {
271 const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(m_field);
272 result = static_cast<uint>(choice->choices().size());
273 }
274 return result;
275}
276
277QJSValue JSField::currentValueIndices() const
278{
280 if (m_field->type() == FormField::FormChoice) {
281 const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(m_field);
282 const QList<int> currentChoices = choice->currentChoices();
283 if (choice->choiceType() == FormFieldChoice::ComboBox) {
284 if (currentChoices.count() == 0 && choice->isEditable()) {
285 result = -1;
286 }
287 } else if (choice->choiceType() == FormFieldChoice::ListBox && currentChoices.count() > 1) {
288 result = qjsEngine(this)->newArray(currentChoices.size());
289 int arrayIndex = 0;
290 for (const int &selectionIndex : currentChoices) {
291 result.setProperty(arrayIndex++, selectionIndex);
292 }
293 }
294 if (currentChoices.count() == 1) {
295 result = currentChoices[0];
296 }
297 }
298 return result;
299}
300
301void JSField::setCurrentValueIndices(const QJSValue &value)
302{
303 if (m_field->type() == FormField::FormChoice) {
304 FormFieldChoice *choice = static_cast<FormFieldChoice *>(m_field);
305 QList<int> tempChoiceList;
306 if (value.isArray()) {
307 for (quint32 i = 0; i < value.property(QStringLiteral("length")).toUInt(); i++) {
308 tempChoiceList << value.property(i).toInt();
309 }
310 } else if (value.isNumber()) {
311 tempChoiceList << value.toInt();
312 }
313 const QList<int> choiceList = tempChoiceList;
314 choice->setCurrentChoices(choiceList);
315 updateField(choice);
316 }
317}
318
319// Instead of getting the Icon, we pick the field.
320QJSValue JSField::buttonGetIcon([[maybe_unused]] int nFace) const
321{
322 QJSValue fieldObject = qjsEngine(this)->newObject();
323 fieldObject.setProperty(OKULAR_NAME, m_field->fullyQualifiedName());
324 g_buttonCache->insert(m_field->fullyQualifiedName(), m_field);
325
326 return fieldObject;
327}
328
329/*
330 * Now we send to the button what Icon should be drawn on it
331 */
332void JSField::buttonSetIcon(const QJSValue &oIcon, [[maybe_unused]] int nFace)
333{
334 const QString fieldName = oIcon.property(OKULAR_NAME).toString();
335
336 if (m_field->type() == Okular::FormField::FormButton) {
337 FormFieldButton *button = static_cast<FormFieldButton *>(m_field);
338 const auto formField = g_buttonCache->value(fieldName);
339 if (formField) {
340 button->setIcon(formField);
341 }
342 }
343
344 updateField(m_field);
345}
346
347JSField::JSField(FormField *field, QObject *parent)
348 : QObject(parent)
349 , m_field(field)
350{
351}
352
353JSField::~JSField() = default;
354
355QJSValue JSField::wrapField(QJSEngine *engine, FormField *field, Page *page)
356{
357 // ### cache unique wrapper
358 QJSValue f = engine->newQObject(new JSField(field));
359 f.setProperty(QStringLiteral("page"), page->number());
360 g_fieldCache->insert(field, page);
361 return f;
362}
363
364void JSField::clearCachedFields()
365{
366 if (g_fieldCache.exists()) {
367 g_fieldCache->clear();
368 }
369
370 if (g_buttonCache.exists()) {
371 g_buttonCache->clear();
372 }
373}
374
375QJSValue JSField::getItemAt(int nIdx, bool bExportValue)
376{
378 if (m_field->type() == FormField::FormChoice) {
379 const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(m_field);
380 if (bExportValue) {
381 if (nIdx < 0 || nIdx >= choice->choices().size()) {
382 result = choice->exportValueForChoice(choice->choices().at(choice->choices().size() - 1));
383 } else {
384 result = choice->exportValueForChoice(choice->choices().at(nIdx));
385 }
386 } else {
387 if (nIdx < 0 || nIdx >= choice->choices().size()) {
388 result = choice->choices().at(choice->choices().size() - 1);
389 } else {
390 result = choice->choices().at(nIdx);
391 }
392 }
393 }
394 return result;
395}
Interface of a button form field.
Definition form.h:247
@ Push
A simple push button.
Definition form.h:253
@ CheckBox
A check box.
Definition form.h:254
@ Radio
A radio button.
Definition form.h:255
virtual void setState(bool state)
Sets the state of the button to the new state .
Definition form.cpp:201
virtual void setIcon(Okular::FormField *field)
Sets the icon of the Button to the Icon of the field parameter.
Definition form.cpp:205
virtual ButtonType buttonType() const =0
The particular type of the button field.
virtual bool state() const =0
The state of the button.
Interface of a choice form field.
Definition form.h:421
virtual void setCurrentChoices(const QList< int > &choices)
Sets the selected choices to choices .
Definition form.cpp:346
QString exportValueForChoice(const QString &choice) const
Returns the export value for a given choice.
Definition form.cpp:375
@ ComboBox
A combo box choice field.
Definition form.h:427
@ ListBox
A list box choice field.
Definition form.h:428
virtual QString editChoice() const
The text entered into an editable combo box choice field.
Definition form.cpp:350
virtual QList< int > currentChoices() const =0
The currently selected choices.
virtual bool isEditable() const
Whether this ComboBox is editable, ie the user can type in a custom value.
Definition form.cpp:336
virtual ChoiceType choiceType() const =0
The particular type of the choice field.
virtual QStringList choices() const =0
The possible choices of the choice field.
Interface of a text form field.
Definition form.h:310
virtual void setText(const QString &text)
Sets the new text in the text field.
Definition form.cpp:241
virtual QString text() const =0
The text of text field.
The base interface of a form field.
Definition form.h:40
@ FormText
A field of variable text. See FormFieldText::TextType.
Definition form.h:52
@ FormChoice
A choice field. See FormFieldChoice::ChoiceType.
Definition form.h:53
@ FormButton
A "button". See FormFieldButton::ButtonType.
Definition form.h:51
@ FormSignature
A signature.
Definition form.h:54
FieldType type() const
The type of the field.
Definition form.cpp:47
virtual QVariant value() const
Returns the value associated wit the form field.
Definition form.cpp:88
global.h
Definition action.h:17
QJSValue newQObject(QObject *object)
bool isArray() const const
bool isNumber() 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
quint32 toUInt() const const
const_reference at(qsizetype i) const const
qsizetype count() const const
qsizetype size() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:51:37 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.