File indexing completed on 2024-05-12 04:33:32

0001 /*
0002     SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
0003     SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "js_field_p.h"
0009 
0010 #include <QDebug>
0011 #include <QHash>
0012 #include <QJSEngine>
0013 #include <QTimer>
0014 
0015 #include "../debug_p.h"
0016 #include "../document_p.h"
0017 #include "../form.h"
0018 #include "../page.h"
0019 #include "../page_p.h"
0020 #include "js_display_p.h"
0021 
0022 using namespace Okular;
0023 
0024 #define OKULAR_NAME QStringLiteral("okular_name")
0025 
0026 typedef QHash<FormField *, Page *> FormCache;
0027 Q_GLOBAL_STATIC(FormCache, g_fieldCache)
0028 typedef QHash<QString, FormField *> ButtonCache;
0029 Q_GLOBAL_STATIC(ButtonCache, g_buttonCache)
0030 
0031 // Helper for modified fields
0032 static void updateField(FormField *field)
0033 {
0034     Page *page = g_fieldCache->value(field);
0035     if (page) {
0036         Document *doc = PagePrivate::get(page)->m_doc->m_parent;
0037         const int pageNumber = page->number();
0038         QTimer::singleShot(0, doc, [doc, pageNumber] { doc->refreshPixmaps(pageNumber); });
0039         Q_EMIT doc->refreshFormWidget(field);
0040     } else {
0041         qWarning() << "Could not get page of field" << field;
0042     }
0043 }
0044 
0045 // Field.doc
0046 QJSValue JSField::doc() const
0047 {
0048     return qjsEngine(this)->globalObject();
0049 }
0050 
0051 // Field.name
0052 QString JSField::name() const
0053 {
0054     return m_field->fullyQualifiedName();
0055 }
0056 
0057 // Field.readonly (getter)
0058 bool JSField::readonly() const
0059 {
0060     return m_field->isReadOnly();
0061 }
0062 
0063 // Field.readonly (setter)
0064 void JSField::setReadonly(bool readonly)
0065 {
0066     m_field->setReadOnly(readonly);
0067 
0068     updateField(m_field);
0069 }
0070 
0071 static QString fieldGetTypeHelper(const FormField *field)
0072 {
0073     switch (field->type()) {
0074     case FormField::FormButton: {
0075         const FormFieldButton *button = static_cast<const FormFieldButton *>(field);
0076         switch (button->buttonType()) {
0077         case FormFieldButton::Push:
0078             return QStringLiteral("button");
0079         case FormFieldButton::CheckBox:
0080             return QStringLiteral("checkbox");
0081         case FormFieldButton::Radio:
0082             return QStringLiteral("radiobutton");
0083         }
0084         break;
0085     }
0086     case FormField::FormText:
0087         return QStringLiteral("text");
0088     case FormField::FormChoice: {
0089         const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(field);
0090         switch (choice->choiceType()) {
0091         case FormFieldChoice::ComboBox:
0092             return QStringLiteral("combobox");
0093         case FormFieldChoice::ListBox:
0094             return QStringLiteral("listbox");
0095         }
0096         break;
0097     }
0098     case FormField::FormSignature:
0099         return QStringLiteral("signature");
0100     }
0101     return QString();
0102 }
0103 
0104 // Field.type
0105 QString JSField::type() const
0106 {
0107     return fieldGetTypeHelper(m_field);
0108 }
0109 
0110 QJSValue JSField::fieldGetValueCore(bool asString) const
0111 {
0112     QJSValue result(QJSValue::UndefinedValue);
0113 
0114     switch (m_field->type()) {
0115     case FormField::FormButton: {
0116         const FormFieldButton *button = static_cast<const FormFieldButton *>(m_field);
0117         if (button->state()) {
0118             result = QStringLiteral("Yes");
0119         } else {
0120             result = QStringLiteral("Off");
0121         }
0122         break;
0123     }
0124     case FormField::FormText: {
0125         const FormFieldText *text = static_cast<const FormFieldText *>(m_field);
0126         const QLocale locale;
0127         bool ok;
0128         const double textAsNumber = locale.toDouble(text->text(), &ok);
0129         if (ok && !asString) {
0130             result = textAsNumber;
0131         } else {
0132             result = text->text();
0133         }
0134         break;
0135     }
0136     case FormField::FormChoice: {
0137         const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(m_field);
0138         const QList<int> currentChoices = choice->currentChoices();
0139         if (currentChoices.count() == 1) {
0140             result = choice->exportValueForChoice(choice->choices().at(currentChoices[0]));
0141         }
0142         break;
0143     }
0144     case FormField::FormSignature: {
0145         break;
0146     }
0147     }
0148 
0149     qCDebug(OkularCoreDebug) << "fieldGetValueCore:"
0150                              << " Field: " << m_field->fullyQualifiedName() << " Type: " << fieldGetTypeHelper(m_field) << " Value: " << result.toString() << (result.isString() ? "(as string)" : "");
0151     return result;
0152 }
0153 // Field.value (getter)
0154 QJSValue JSField::value() const
0155 {
0156     return fieldGetValueCore(/*asString*/ false);
0157 }
0158 
0159 // Field.value (setter)
0160 void JSField::setValue(const QJSValue &value)
0161 {
0162     qCDebug(OkularCoreDebug) << "fieldSetValue: Field: " << m_field->fullyQualifiedName() << " Type: " << fieldGetTypeHelper(m_field) << " Value: " << value.toString();
0163     switch (m_field->type()) {
0164     case FormField::FormButton: {
0165         FormFieldButton *button = static_cast<FormFieldButton *>(m_field);
0166         const QString text = value.toString();
0167         if (text == QStringLiteral("Yes")) {
0168             button->setState(true);
0169             updateField(m_field);
0170         } else if (text == QStringLiteral("Off")) {
0171             button->setState(false);
0172             updateField(m_field);
0173         }
0174         break;
0175     }
0176     case FormField::FormText: {
0177         FormFieldText *textField = static_cast<FormFieldText *>(m_field);
0178         const QString text = value.toString();
0179         if (text != textField->text()) {
0180             textField->setText(text);
0181             updateField(m_field);
0182         }
0183         break;
0184     }
0185     case FormField::FormChoice: {
0186         FormFieldChoice *choice = static_cast<FormFieldChoice *>(m_field);
0187         Q_UNUSED(choice); // ###
0188         break;
0189     }
0190     case FormField::FormSignature: {
0191         break;
0192     }
0193     }
0194 }
0195 
0196 // Field.valueAsString (getter)
0197 QJSValue JSField::valueAsString() const
0198 {
0199     return fieldGetValueCore(/*asString*/ true);
0200 }
0201 
0202 // Field.hidden (getter)
0203 bool JSField::hidden() const
0204 {
0205     return !m_field->isVisible();
0206 }
0207 
0208 // Field.hidden (setter)
0209 void JSField::setHidden(bool hidden)
0210 {
0211     m_field->setVisible(!hidden);
0212 
0213     updateField(m_field);
0214 }
0215 
0216 // Field.display (getter)
0217 int JSField::display() const
0218 {
0219     bool visible = m_field->isVisible();
0220     if (visible) {
0221         return m_field->isPrintable() ? FormDisplay::FormVisible : FormDisplay::FormNoPrint;
0222     }
0223     return m_field->isPrintable() ? FormDisplay::FormNoView : FormDisplay::FormHidden;
0224 }
0225 
0226 // Field.display (setter)
0227 void JSField::setDisplay(int display)
0228 {
0229     switch (display) {
0230     case FormDisplay::FormVisible:
0231         m_field->setVisible(true);
0232         m_field->setPrintable(true);
0233         break;
0234     case FormDisplay::FormHidden:
0235         m_field->setVisible(false);
0236         m_field->setPrintable(false);
0237         break;
0238     case FormDisplay::FormNoPrint:
0239         m_field->setVisible(true);
0240         m_field->setPrintable(false);
0241         break;
0242     case FormDisplay::FormNoView:
0243         m_field->setVisible(false);
0244         m_field->setPrintable(true);
0245         break;
0246     }
0247     updateField(m_field);
0248 }
0249 
0250 //  Instead of getting the Icon, we pick the field.
0251 QJSValue JSField::buttonGetIcon([[maybe_unused]] int nFace) const
0252 {
0253     QJSValue fieldObject = qjsEngine(this)->newObject();
0254     fieldObject.setProperty(OKULAR_NAME, m_field->fullyQualifiedName());
0255     g_buttonCache->insert(m_field->fullyQualifiedName(), m_field);
0256 
0257     return fieldObject;
0258 }
0259 
0260 /*
0261  * Now we send to the button what Icon should be drawn on it
0262  */
0263 void JSField::buttonSetIcon(const QJSValue &oIcon, [[maybe_unused]] int nFace)
0264 {
0265     const QString fieldName = oIcon.property(OKULAR_NAME).toString();
0266 
0267     if (m_field->type() == Okular::FormField::FormButton) {
0268         FormFieldButton *button = static_cast<FormFieldButton *>(m_field);
0269         const auto formField = g_buttonCache->value(fieldName);
0270         if (formField) {
0271             button->setIcon(formField);
0272         }
0273     }
0274 
0275     updateField(m_field);
0276 }
0277 
0278 JSField::JSField(FormField *field, QObject *parent)
0279     : QObject(parent)
0280     , m_field(field)
0281 {
0282 }
0283 
0284 JSField::~JSField() = default;
0285 
0286 QJSValue JSField::wrapField(QJSEngine *engine, FormField *field, Page *page)
0287 {
0288     // ### cache unique wrapper
0289     QJSValue f = engine->newQObject(new JSField(field));
0290     f.setProperty(QStringLiteral("page"), page->number());
0291     g_fieldCache->insert(field, page);
0292     return f;
0293 }
0294 
0295 void JSField::clearCachedFields()
0296 {
0297     if (g_fieldCache.exists()) {
0298         g_fieldCache->clear();
0299     }
0300 
0301     if (g_buttonCache.exists()) {
0302         g_buttonCache->clear();
0303     }
0304 }