File indexing completed on 2024-04-28 15:51:44

0001 /*
0002     SPDX-FileCopyrightText: 2007 Pino Toscano <pino@kde.org>
0003     SPDX-FileCopyrightText: 2018 Intevation GmbH <intevation@intevation.de>
0004 
0005     Work sponsored by the LiMux project of the city of Munich:
0006     SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "formwidgets.h"
0012 #include "core/page.h"
0013 #include "pageview.h"
0014 #include "pageviewutils.h"
0015 #include "revisionviewer.h"
0016 #include "signaturepartutils.h"
0017 #include "signaturepropertiesdialog.h"
0018 
0019 #include <KLineEdit>
0020 #include <KLocalizedString>
0021 #include <KStandardAction>
0022 #include <QAction>
0023 #include <QButtonGroup>
0024 #include <QEvent>
0025 #include <QKeyEvent>
0026 #include <QMenu>
0027 #include <QPainter>
0028 #include <QUrl>
0029 
0030 // local includes
0031 #include "core/action.h"
0032 #include "core/document.h"
0033 #include "gui/debug_ui.h"
0034 
0035 FormWidgetsController::FormWidgetsController(Okular::Document *doc)
0036     : QObject(doc)
0037     , m_doc(doc)
0038 {
0039     // Q_EMIT changed signal when a form has changed
0040     connect(this, &FormWidgetsController::formTextChangedByUndoRedo, this, &FormWidgetsController::changed);
0041     connect(this, &FormWidgetsController::formListChangedByUndoRedo, this, &FormWidgetsController::changed);
0042     connect(this, &FormWidgetsController::formComboChangedByUndoRedo, this, &FormWidgetsController::changed);
0043 
0044     // connect form modification signals to and from document
0045     connect(this, &FormWidgetsController::formTextChangedByWidget, doc, &Okular::Document::editFormText);
0046     connect(doc, &Okular::Document::formTextChangedByUndoRedo, this, &FormWidgetsController::formTextChangedByUndoRedo);
0047 
0048     connect(this, &FormWidgetsController::formListChangedByWidget, doc, &Okular::Document::editFormList);
0049     connect(doc, &Okular::Document::formListChangedByUndoRedo, this, &FormWidgetsController::formListChangedByUndoRedo);
0050 
0051     connect(this, &FormWidgetsController::formComboChangedByWidget, doc, &Okular::Document::editFormCombo);
0052     connect(doc, &Okular::Document::formComboChangedByUndoRedo, this, &FormWidgetsController::formComboChangedByUndoRedo);
0053 
0054     connect(this, &FormWidgetsController::formButtonsChangedByWidget, doc, &Okular::Document::editFormButtons);
0055     connect(doc, &Okular::Document::formButtonsChangedByUndoRedo, this, &FormWidgetsController::slotFormButtonsChangedByUndoRedo);
0056 
0057     // Connect undo/redo signals
0058     connect(this, &FormWidgetsController::requestUndo, doc, &Okular::Document::undo);
0059     connect(this, &FormWidgetsController::requestRedo, doc, &Okular::Document::redo);
0060 
0061     connect(doc, &Okular::Document::canUndoChanged, this, &FormWidgetsController::canUndoChanged);
0062     connect(doc, &Okular::Document::canRedoChanged, this, &FormWidgetsController::canRedoChanged);
0063 
0064     // Connect the generic formWidget refresh signal
0065     connect(doc, &Okular::Document::refreshFormWidget, this, &FormWidgetsController::refreshFormWidget);
0066 }
0067 
0068 FormWidgetsController::~FormWidgetsController()
0069 {
0070 }
0071 
0072 void FormWidgetsController::signalAction(Okular::Action *a)
0073 {
0074     Q_EMIT action(a);
0075 }
0076 
0077 void FormWidgetsController::signalMouseUpAction(Okular::Action *action, Okular::FormField *form)
0078 {
0079     Q_EMIT mouseUpAction(action, form);
0080 }
0081 
0082 void FormWidgetsController::processScriptAction(Okular::Action *a, Okular::FormField *field, Okular::Annotation::AdditionalActionType type)
0083 {
0084     // If it's not a Action Script or if the field is not a FormText, handle it normally
0085     if (a->actionType() != Okular::Action::Script || field->type() != Okular::FormField::FormText) {
0086         Q_EMIT action(a);
0087         return;
0088     }
0089     switch (type) {
0090     // These cases are to be handled by the FormField text, so we let it happen.
0091     case Okular::Annotation::FocusIn:
0092     case Okular::Annotation::FocusOut:
0093         return;
0094     case Okular::Annotation::PageOpening:
0095     case Okular::Annotation::PageClosing:
0096     case Okular::Annotation::CursorEntering:
0097     case Okular::Annotation::CursorLeaving:
0098     case Okular::Annotation::MousePressed:
0099     case Okular::Annotation::MouseReleased:
0100         Q_EMIT action(a);
0101     }
0102 }
0103 
0104 void FormWidgetsController::registerRadioButton(FormWidgetIface *fwButton, Okular::FormFieldButton *formButton)
0105 {
0106     if (!fwButton) {
0107         return;
0108     }
0109 
0110     QAbstractButton *button = dynamic_cast<QAbstractButton *>(fwButton);
0111     if (!button) {
0112         qWarning() << "fwButton is not a QAbstractButton" << fwButton;
0113         return;
0114     }
0115 
0116     QList<RadioData>::iterator it = m_radios.begin(), itEnd = m_radios.end();
0117     const int id = formButton->id();
0118     m_buttons.insert(id, button);
0119     for (; it != itEnd; ++it) {
0120         const RadioData &rd = *it;
0121         const QList<int>::const_iterator idsIt = std::find(rd.ids.begin(), rd.ids.end(), id);
0122         if (idsIt != rd.ids.constEnd()) {
0123             qCDebug(OkularUiDebug) << "Adding id" << id << "To group including" << rd.ids;
0124             rd.group->addButton(button);
0125             rd.group->setId(button, id);
0126             return;
0127         }
0128     }
0129 
0130     const QList<int> siblings = formButton->siblings();
0131 
0132     RadioData newdata;
0133     newdata.ids = siblings;
0134     newdata.ids.append(id);
0135     newdata.group = new QButtonGroup();
0136     newdata.group->addButton(button);
0137     newdata.group->setId(button, id);
0138 
0139     // Groups of 1 (like checkboxes) can't be exclusive
0140     if (siblings.isEmpty()) {
0141         newdata.group->setExclusive(false);
0142     }
0143 
0144     connect(newdata.group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this, &FormWidgetsController::slotButtonClicked);
0145     m_radios.append(newdata);
0146 }
0147 
0148 void FormWidgetsController::dropRadioButtons()
0149 {
0150     QList<RadioData>::iterator it = m_radios.begin(), itEnd = m_radios.end();
0151     for (; it != itEnd; ++it) {
0152         delete (*it).group;
0153     }
0154     m_radios.clear();
0155     m_buttons.clear();
0156 }
0157 
0158 bool FormWidgetsController::canUndo()
0159 {
0160     return m_doc->canUndo();
0161 }
0162 
0163 bool FormWidgetsController::canRedo()
0164 {
0165     return m_doc->canRedo();
0166 }
0167 
0168 bool FormWidgetsController::shouldFormWidgetBeShown(Okular::FormField *form)
0169 {
0170     return !form->isReadOnly() || form->type() == Okular::FormField::FormSignature;
0171 }
0172 
0173 void FormWidgetsController::slotButtonClicked(QAbstractButton *button)
0174 {
0175     int pageNumber = -1;
0176     CheckBoxEdit *check = qobject_cast<CheckBoxEdit *>(button);
0177     if (check) {
0178         // Checkboxes need to be uncheckable so if clicking a checked one
0179         // disable the exclusive status temporarily and uncheck it
0180         Okular::FormFieldButton *formButton = static_cast<Okular::FormFieldButton *>(check->formField());
0181         if (formButton->state()) {
0182             const bool wasExclusive = button->group()->exclusive();
0183             button->group()->setExclusive(false);
0184             check->setChecked(false);
0185             button->group()->setExclusive(wasExclusive);
0186         }
0187         pageNumber = check->pageItem()->pageNumber();
0188     } else if (RadioButtonEdit *radio = qobject_cast<RadioButtonEdit *>(button)) {
0189         pageNumber = radio->pageItem()->pageNumber();
0190     }
0191 
0192     const QList<QAbstractButton *> buttons = button->group()->buttons();
0193     QList<bool> checked;
0194     QList<bool> prevChecked;
0195     QList<Okular::FormFieldButton *> formButtons;
0196 
0197     for (QAbstractButton *button : buttons) {
0198         checked.append(button->isChecked());
0199         Okular::FormFieldButton *formButton = static_cast<Okular::FormFieldButton *>(dynamic_cast<FormWidgetIface *>(button)->formField());
0200         formButtons.append(formButton);
0201         prevChecked.append(formButton->state());
0202     }
0203     if (checked != prevChecked) {
0204         Q_EMIT formButtonsChangedByWidget(pageNumber, formButtons, checked);
0205     }
0206     if (check) {
0207         // The formButtonsChangedByWidget signal changes the value of the underlying
0208         // Okular::FormField of the checkbox. We need to execute the activation
0209         // action after this.
0210         check->doActivateAction();
0211     }
0212 }
0213 
0214 void FormWidgetsController::slotFormButtonsChangedByUndoRedo(int pageNumber, const QList<Okular::FormFieldButton *> &formButtons)
0215 {
0216     QList<int> extraPages;
0217     for (const Okular::FormFieldButton *formButton : formButtons) {
0218         int id = formButton->id();
0219         QAbstractButton *button = m_buttons[id];
0220         int itemPageNumber = -1;
0221         if (CheckBoxEdit *check = qobject_cast<CheckBoxEdit *>(button)) {
0222             itemPageNumber = check->pageItem()->pageNumber();
0223             Q_EMIT refreshFormWidget(check->formField());
0224         } else if (RadioButtonEdit *radio = qobject_cast<RadioButtonEdit *>(button)) {
0225             itemPageNumber = radio->pageItem()->pageNumber();
0226         }
0227         // temporarily disable exclusiveness of the button group
0228         // since it breaks doing/redoing steps into which all the checkboxes
0229         // are unchecked
0230         const bool wasExclusive = button->group()->exclusive();
0231         button->group()->setExclusive(false);
0232         bool checked = formButton->state();
0233         button->setChecked(checked);
0234         button->group()->setExclusive(wasExclusive);
0235         button->setFocus();
0236         if (itemPageNumber != -1 && itemPageNumber != pageNumber) {
0237             extraPages << itemPageNumber;
0238         }
0239     }
0240     Q_EMIT changed(pageNumber);
0241     for (auto page : extraPages) {
0242         Q_EMIT changed(page);
0243     }
0244 }
0245 
0246 Okular::Document *FormWidgetsController::document() const
0247 {
0248     return m_doc;
0249 }
0250 
0251 FormWidgetIface *FormWidgetFactory::createWidget(Okular::FormField *ff, PageView *pageView)
0252 {
0253     FormWidgetIface *widget = nullptr;
0254 
0255     switch (ff->type()) {
0256     case Okular::FormField::FormButton: {
0257         Okular::FormFieldButton *ffb = static_cast<Okular::FormFieldButton *>(ff);
0258         switch (ffb->buttonType()) {
0259         case Okular::FormFieldButton::Push:
0260             widget = new PushButtonEdit(ffb, pageView);
0261             break;
0262         case Okular::FormFieldButton::CheckBox:
0263             widget = new CheckBoxEdit(ffb, pageView);
0264             break;
0265         case Okular::FormFieldButton::Radio:
0266             widget = new RadioButtonEdit(ffb, pageView);
0267             break;
0268         default:;
0269         }
0270         break;
0271     }
0272     case Okular::FormField::FormText: {
0273         Okular::FormFieldText *fft = static_cast<Okular::FormFieldText *>(ff);
0274         switch (fft->textType()) {
0275         case Okular::FormFieldText::Multiline:
0276             widget = new TextAreaEdit(fft, pageView);
0277             break;
0278         case Okular::FormFieldText::Normal:
0279             widget = new FormLineEdit(fft, pageView);
0280             break;
0281         case Okular::FormFieldText::FileSelect:
0282             widget = new FileEdit(fft, pageView);
0283             break;
0284         }
0285         break;
0286     }
0287     case Okular::FormField::FormChoice: {
0288         Okular::FormFieldChoice *ffc = static_cast<Okular::FormFieldChoice *>(ff);
0289         switch (ffc->choiceType()) {
0290         case Okular::FormFieldChoice::ListBox:
0291             widget = new ListEdit(ffc, pageView);
0292             break;
0293         case Okular::FormFieldChoice::ComboBox:
0294             widget = new ComboEdit(ffc, pageView);
0295             break;
0296         }
0297         break;
0298     }
0299     case Okular::FormField::FormSignature: {
0300         Okular::FormFieldSignature *ffs = static_cast<Okular::FormFieldSignature *>(ff);
0301         if (ffs->isVisible() && ffs->signatureType() != Okular::FormFieldSignature::UnknownType) {
0302             widget = new SignatureEdit(ffs, pageView);
0303         }
0304         break;
0305     }
0306     default:;
0307     }
0308 
0309     if (!FormWidgetsController::shouldFormWidgetBeShown(ff)) {
0310         widget->setVisibility(false);
0311     }
0312 
0313     return widget;
0314 }
0315 
0316 FormWidgetIface::FormWidgetIface(QWidget *w, Okular::FormField *ff)
0317     : m_controller(nullptr)
0318     , m_ff(ff)
0319     , m_widget(w)
0320     , m_pageItem(nullptr)
0321 {
0322 }
0323 
0324 FormWidgetIface::~FormWidgetIface()
0325 {
0326 }
0327 
0328 Okular::NormalizedRect FormWidgetIface::rect() const
0329 {
0330     return m_ff->rect();
0331 }
0332 
0333 void FormWidgetIface::setWidthHeight(int w, int h)
0334 {
0335     m_widget->resize(w, h);
0336 }
0337 
0338 void FormWidgetIface::moveTo(int x, int y)
0339 {
0340     m_widget->move(x, y);
0341 }
0342 
0343 bool FormWidgetIface::setVisibility(bool visible)
0344 {
0345     bool hadfocus = m_widget->hasFocus();
0346     if (hadfocus && !visible) {
0347         m_widget->clearFocus();
0348     }
0349     m_widget->setVisible(visible);
0350     return hadfocus;
0351 }
0352 
0353 void FormWidgetIface::setCanBeFilled(bool fill)
0354 {
0355     m_widget->setEnabled(fill);
0356 }
0357 
0358 void FormWidgetIface::setPageItem(PageViewItem *pageItem)
0359 {
0360     m_pageItem = pageItem;
0361 }
0362 
0363 void FormWidgetIface::setFormField(Okular::FormField *field)
0364 {
0365     m_ff = field;
0366 }
0367 
0368 Okular::FormField *FormWidgetIface::formField() const
0369 {
0370     return m_ff;
0371 }
0372 
0373 PageViewItem *FormWidgetIface::pageItem() const
0374 {
0375     return m_pageItem;
0376 }
0377 
0378 void FormWidgetIface::setFormWidgetsController(FormWidgetsController *controller)
0379 {
0380     m_controller = controller;
0381     QObject *obj = dynamic_cast<QObject *>(this);
0382     QObject::connect(m_controller, &FormWidgetsController::refreshFormWidget, obj, [this](Okular::FormField *form) { slotRefresh(form); });
0383 }
0384 
0385 void FormWidgetIface::slotRefresh(Okular::FormField *form)
0386 {
0387     if (m_ff != form) {
0388         return;
0389     }
0390     setVisibility(form->isVisible() && m_controller->shouldFormWidgetBeShown(form));
0391 
0392     m_widget->setEnabled(!form->isReadOnly());
0393 }
0394 
0395 PushButtonEdit::PushButtonEdit(Okular::FormFieldButton *button, PageView *pageView)
0396     : QPushButton(pageView->viewport())
0397     , FormWidgetIface(this, button)
0398 {
0399     setText(button->caption());
0400 
0401     if (button->caption().isEmpty()) {
0402         setFlat(true);
0403     }
0404 
0405     setVisible(button->isVisible());
0406     setCursor(Qt::ArrowCursor);
0407 }
0408 
0409 CheckBoxEdit::CheckBoxEdit(Okular::FormFieldButton *button, PageView *pageView)
0410     : QCheckBox(pageView->viewport())
0411     , FormWidgetIface(this, button)
0412 {
0413     setText(button->caption());
0414 
0415     setVisible(button->isVisible());
0416     setCursor(Qt::ArrowCursor);
0417 }
0418 
0419 void CheckBoxEdit::setFormWidgetsController(FormWidgetsController *controller)
0420 {
0421     Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
0422     FormWidgetIface::setFormWidgetsController(controller);
0423     m_controller->registerRadioButton(this, form);
0424     setChecked(form->state());
0425 }
0426 
0427 void CheckBoxEdit::doActivateAction()
0428 {
0429     Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
0430     if (form->activationAction()) {
0431         m_controller->signalAction(form->activationAction());
0432     }
0433 }
0434 
0435 void CheckBoxEdit::slotRefresh(Okular::FormField *form)
0436 {
0437     if (form != m_ff) {
0438         return;
0439     }
0440     FormWidgetIface::slotRefresh(form);
0441 
0442     Okular::FormFieldButton *button = static_cast<Okular::FormFieldButton *>(m_ff);
0443     bool oldState = isChecked();
0444     bool newState = button->state();
0445     if (oldState != newState) {
0446         setChecked(button->state());
0447         doActivateAction();
0448     }
0449 }
0450 
0451 RadioButtonEdit::RadioButtonEdit(Okular::FormFieldButton *button, PageView *pageView)
0452     : QRadioButton(pageView->viewport())
0453     , FormWidgetIface(this, button)
0454 {
0455     setText(button->caption());
0456 
0457     setVisible(button->isVisible());
0458     setCursor(Qt::ArrowCursor);
0459 }
0460 
0461 void RadioButtonEdit::setFormWidgetsController(FormWidgetsController *controller)
0462 {
0463     Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
0464     FormWidgetIface::setFormWidgetsController(controller);
0465     m_controller->registerRadioButton(this, form);
0466     setChecked(form->state());
0467 }
0468 
0469 FormLineEdit::FormLineEdit(Okular::FormFieldText *text, PageView *pageView)
0470     : QLineEdit(pageView->viewport())
0471     , FormWidgetIface(this, text)
0472 {
0473     int maxlen = text->maximumLength();
0474     if (maxlen >= 0) {
0475         setMaxLength(maxlen);
0476     }
0477     setAlignment(text->textAlignment());
0478     setText(text->text());
0479     if (text->isPassword()) {
0480         setEchoMode(QLineEdit::Password);
0481     }
0482 
0483     m_prevCursorPos = cursorPosition();
0484     m_prevAnchorPos = cursorPosition();
0485     m_editing = false;
0486 
0487     connect(this, &QLineEdit::textEdited, this, &FormLineEdit::slotChanged);
0488     connect(this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged);
0489 
0490     setVisible(text->isVisible());
0491 }
0492 
0493 void FormLineEdit::setFormWidgetsController(FormWidgetsController *controller)
0494 {
0495     FormWidgetIface::setFormWidgetsController(controller);
0496     connect(m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &FormLineEdit::slotHandleTextChangedByUndoRedo);
0497 }
0498 
0499 bool FormLineEdit::event(QEvent *e)
0500 {
0501     if (e->type() == QEvent::KeyPress) {
0502         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
0503         if (keyEvent == QKeySequence::Undo) {
0504             Q_EMIT m_controller->requestUndo();
0505             return true;
0506         } else if (keyEvent == QKeySequence::Redo) {
0507             Q_EMIT m_controller->requestRedo();
0508             return true;
0509         }
0510     } else if (e->type() == QEvent::FocusIn) {
0511         const auto fft = static_cast<Okular::FormFieldText *>(m_ff);
0512         if (text() != fft->text()) {
0513             setText(fft->text());
0514         }
0515         m_editing = true;
0516 
0517         QFocusEvent *focusEvent = static_cast<QFocusEvent *>(e);
0518         if (focusEvent->reason() != Qt::ActiveWindowFocusReason) {
0519             if (const Okular::Action *action = m_ff->additionalAction(Okular::Annotation::FocusIn)) {
0520                 m_controller->document()->processFocusAction(action, fft);
0521             }
0522         }
0523         setFocus();
0524     } else if (e->type() == QEvent::FocusOut) {
0525         m_editing = false;
0526 
0527         // Don't worry about focus events from other sources than the user FocusEvent to edit the field
0528         QFocusEvent *focusEvent = static_cast<QFocusEvent *>(e);
0529         if (focusEvent->reason() == Qt::OtherFocusReason || focusEvent->reason() == Qt::ActiveWindowFocusReason) {
0530             return true;
0531         }
0532 
0533         if (m_ff->additionalAction(Okular::FormField::FieldModified) && !m_ff->isReadOnly()) {
0534             Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
0535             m_controller->document()->processKeystrokeCommitAction(m_ff->additionalAction(Okular::FormField::FieldModified), form);
0536         }
0537 
0538         if (const Okular::Action *action = m_ff->additionalAction(Okular::Annotation::FocusOut)) {
0539             bool ok = false;
0540             m_controller->document()->processValidateAction(action, static_cast<Okular::FormFieldText *>(m_ff), ok);
0541         }
0542         if (const Okular::Action *action = m_ff->additionalAction(Okular::FormField::FormatField)) {
0543             m_controller->document()->processFormatAction(action, static_cast<Okular::FormFieldText *>(m_ff));
0544         }
0545     }
0546     return QLineEdit::event(e);
0547 }
0548 
0549 void FormLineEdit::contextMenuEvent(QContextMenuEvent *event)
0550 {
0551     QMenu *menu = createStandardContextMenu();
0552 
0553     QList<QAction *> actionList = menu->actions();
0554     enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct };
0555 
0556     QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
0557     QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
0558     connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
0559     connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
0560     kundo->setEnabled(m_controller->canUndo());
0561     kredo->setEnabled(m_controller->canRedo());
0562 
0563     QAction *oldUndo, *oldRedo;
0564     oldUndo = actionList[UndoAct];
0565     oldRedo = actionList[RedoAct];
0566 
0567     menu->insertAction(oldUndo, kundo);
0568     menu->insertAction(oldRedo, kredo);
0569 
0570     menu->removeAction(oldUndo);
0571     menu->removeAction(oldRedo);
0572 
0573     menu->exec(event->globalPos());
0574     delete menu;
0575 }
0576 
0577 void FormLineEdit::slotChanged()
0578 {
0579     Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
0580     int cursorPos = cursorPosition();
0581 
0582     if (text() != form->text()) {
0583         if (form->additionalAction(Okular::FormField::FieldModified) && m_editing && !form->isReadOnly()) {
0584             m_controller->document()->processKeystrokeAction(form->additionalAction(Okular::FormField::FieldModified), form, text());
0585         }
0586 
0587         Q_EMIT m_controller->formTextChangedByWidget(pageItem()->pageNumber(), form, text(), cursorPos, m_prevCursorPos, m_prevAnchorPos);
0588     }
0589 
0590     m_prevCursorPos = cursorPos;
0591     m_prevAnchorPos = cursorPos;
0592     if (hasSelectedText()) {
0593         if (cursorPos == selectionStart()) {
0594             m_prevAnchorPos = selectionStart() + selectedText().size();
0595         } else {
0596             m_prevAnchorPos = selectionStart();
0597         }
0598     }
0599 }
0600 
0601 void FormLineEdit::slotHandleTextChangedByUndoRedo(int pageNumber, Okular::FormFieldText *textForm, const QString &contents, int cursorPos, int anchorPos)
0602 {
0603     Q_UNUSED(pageNumber);
0604     if (textForm != m_ff || contents == text()) {
0605         return;
0606     }
0607     disconnect(this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged);
0608     setText(contents);
0609     setCursorPosition(anchorPos);
0610     cursorForward(true, cursorPos - anchorPos);
0611     connect(this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged);
0612     m_prevCursorPos = cursorPos;
0613     m_prevAnchorPos = anchorPos;
0614     setFocus();
0615 }
0616 
0617 void FormLineEdit::slotRefresh(Okular::FormField *form)
0618 {
0619     if (form != m_ff) {
0620         return;
0621     }
0622     FormWidgetIface::slotRefresh(form);
0623 
0624     Okular::FormFieldText *text = static_cast<Okular::FormFieldText *>(form);
0625     setText(text->text());
0626 }
0627 
0628 TextAreaEdit::TextAreaEdit(Okular::FormFieldText *text, PageView *pageView)
0629     : KTextEdit(pageView->viewport())
0630     , FormWidgetIface(this, text)
0631 {
0632     setAcceptRichText(text->isRichText());
0633     setCheckSpellingEnabled(text->canBeSpellChecked());
0634     setAlignment(text->textAlignment());
0635     setPlainText(text->text());
0636     setUndoRedoEnabled(false);
0637 
0638     connect(this, &QTextEdit::textChanged, this, &TextAreaEdit::slotChanged);
0639     connect(this, &QTextEdit::cursorPositionChanged, this, &TextAreaEdit::slotChanged);
0640     connect(this, &KTextEdit::aboutToShowContextMenu, this, &TextAreaEdit::slotUpdateUndoAndRedoInContextMenu);
0641     m_prevCursorPos = textCursor().position();
0642     m_prevAnchorPos = textCursor().anchor();
0643     m_editing = false;
0644     setVisible(text->isVisible());
0645 }
0646 
0647 TextAreaEdit::~TextAreaEdit()
0648 {
0649     // We need this because otherwise on destruction we destruct the syntax highlighter
0650     // that ends up calling text changed but then we go to slotChanged and we're already
0651     // half destructed and all is bad
0652     disconnect(this, &QTextEdit::textChanged, this, &TextAreaEdit::slotChanged);
0653 }
0654 
0655 bool TextAreaEdit::event(QEvent *e)
0656 {
0657     if (e->type() == QEvent::KeyPress) {
0658         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
0659         if (keyEvent == QKeySequence::Undo) {
0660             Q_EMIT m_controller->requestUndo();
0661             return true;
0662         } else if (keyEvent == QKeySequence::Redo) {
0663             Q_EMIT m_controller->requestRedo();
0664             return true;
0665         }
0666     } else if (e->type() == QEvent::FocusIn) {
0667         const auto fft = static_cast<Okular::FormFieldText *>(m_ff);
0668         if (toPlainText() != fft->text()) {
0669             setText(fft->text());
0670         }
0671         m_editing = true;
0672     } else if (e->type() == QEvent::FocusOut) {
0673         m_editing = false;
0674 
0675         if (m_ff->additionalAction(Okular::FormField::FieldModified) && !m_ff->isReadOnly()) {
0676             m_controller->document()->processKeystrokeCommitAction(m_ff->additionalAction(Okular::FormField::FieldModified), static_cast<Okular::FormFieldText *>(m_ff));
0677         }
0678 
0679         if (const Okular::Action *action = m_ff->additionalAction(Okular::FormField::FormatField)) {
0680             m_controller->document()->processFormatAction(action, static_cast<Okular::FormFieldText *>(m_ff));
0681         }
0682     }
0683     return KTextEdit::event(e);
0684 }
0685 
0686 void TextAreaEdit::slotUpdateUndoAndRedoInContextMenu(QMenu *menu)
0687 {
0688     if (!menu) {
0689         return;
0690     }
0691 
0692     QList<QAction *> actionList = menu->actions();
0693     enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
0694 
0695     QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
0696     QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
0697     connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
0698     connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
0699     kundo->setEnabled(m_controller->canUndo());
0700     kredo->setEnabled(m_controller->canRedo());
0701 
0702     QAction *oldUndo, *oldRedo;
0703     oldUndo = actionList[UndoAct];
0704     oldRedo = actionList[RedoAct];
0705 
0706     menu->insertAction(oldUndo, kundo);
0707     menu->insertAction(oldRedo, kredo);
0708 
0709     menu->removeAction(oldUndo);
0710     menu->removeAction(oldRedo);
0711 }
0712 
0713 void TextAreaEdit::setFormWidgetsController(FormWidgetsController *controller)
0714 {
0715     FormWidgetIface::setFormWidgetsController(controller);
0716     connect(m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &TextAreaEdit::slotHandleTextChangedByUndoRedo);
0717 }
0718 
0719 void TextAreaEdit::slotHandleTextChangedByUndoRedo(int pageNumber, Okular::FormFieldText *textForm, const QString &contents, int cursorPos, int anchorPos)
0720 {
0721     Q_UNUSED(pageNumber);
0722     if (textForm != m_ff) {
0723         return;
0724     }
0725     setPlainText(contents);
0726     QTextCursor c = textCursor();
0727     c.setPosition(anchorPos);
0728     c.setPosition(cursorPos, QTextCursor::KeepAnchor);
0729     m_prevCursorPos = cursorPos;
0730     m_prevAnchorPos = anchorPos;
0731     setTextCursor(c);
0732     setFocus();
0733 }
0734 
0735 void TextAreaEdit::slotChanged()
0736 {
0737     Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
0738     int cursorPos = textCursor().position();
0739 
0740     if (toPlainText() != form->text()) {
0741         if (form->additionalAction(Okular::FormField::FieldModified) && m_editing && !form->isReadOnly()) {
0742             m_controller->document()->processKeystrokeAction(form->additionalAction(Okular::FormField::FieldModified), form, toPlainText());
0743         }
0744 
0745         Q_EMIT m_controller->formTextChangedByWidget(pageItem()->pageNumber(), form, toPlainText(), cursorPos, m_prevCursorPos, m_prevAnchorPos);
0746     }
0747     m_prevCursorPos = cursorPos;
0748     m_prevAnchorPos = textCursor().anchor();
0749 }
0750 
0751 void TextAreaEdit::slotRefresh(Okular::FormField *form)
0752 {
0753     if (form != m_ff) {
0754         return;
0755     }
0756     FormWidgetIface::slotRefresh(form);
0757 
0758     Okular::FormFieldText *text = static_cast<Okular::FormFieldText *>(form);
0759     setPlainText(text->text());
0760 }
0761 
0762 FileEdit::FileEdit(Okular::FormFieldText *text, PageView *pageView)
0763     : KUrlRequester(pageView->viewport())
0764     , FormWidgetIface(this, text)
0765 {
0766     setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly);
0767     setNameFilter(i18n("All Files (*)"));
0768     setUrl(QUrl::fromUserInput(text->text()));
0769     lineEdit()->setAlignment(text->textAlignment());
0770 
0771     m_prevCursorPos = lineEdit()->cursorPosition();
0772     m_prevAnchorPos = lineEdit()->cursorPosition();
0773 
0774     connect(this, &KUrlRequester::textChanged, this, &FileEdit::slotChanged);
0775     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged);
0776     setVisible(text->isVisible());
0777 }
0778 
0779 void FileEdit::setFormWidgetsController(FormWidgetsController *controller)
0780 {
0781     FormWidgetIface::setFormWidgetsController(controller);
0782     connect(m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &FileEdit::slotHandleFileChangedByUndoRedo);
0783 }
0784 
0785 bool FileEdit::eventFilter(QObject *obj, QEvent *event)
0786 {
0787     if (obj == lineEdit()) {
0788         if (event->type() == QEvent::KeyPress) {
0789             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
0790             if (keyEvent == QKeySequence::Undo) {
0791                 Q_EMIT m_controller->requestUndo();
0792                 return true;
0793             } else if (keyEvent == QKeySequence::Redo) {
0794                 Q_EMIT m_controller->requestRedo();
0795                 return true;
0796             }
0797         } else if (event->type() == QEvent::ContextMenu) {
0798             QContextMenuEvent *contextMenuEvent = static_cast<QContextMenuEvent *>(event);
0799 
0800             QMenu *menu = ((QLineEdit *)lineEdit())->createStandardContextMenu();
0801 
0802             QList<QAction *> actionList = menu->actions();
0803             enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct };
0804 
0805             QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
0806             QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
0807             connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
0808             connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
0809             kundo->setEnabled(m_controller->canUndo());
0810             kredo->setEnabled(m_controller->canRedo());
0811 
0812             QAction *oldUndo, *oldRedo;
0813             oldUndo = actionList[UndoAct];
0814             oldRedo = actionList[RedoAct];
0815 
0816             menu->insertAction(oldUndo, kundo);
0817             menu->insertAction(oldRedo, kredo);
0818 
0819             menu->removeAction(oldUndo);
0820             menu->removeAction(oldRedo);
0821 
0822             menu->exec(contextMenuEvent->globalPos());
0823             delete menu;
0824             return true;
0825         }
0826     }
0827     return KUrlRequester::eventFilter(obj, event);
0828 }
0829 
0830 void FileEdit::slotChanged()
0831 {
0832     // Make sure line edit's text matches url expansion
0833     if (text() != url().toLocalFile()) {
0834         this->setText(url().toLocalFile());
0835     }
0836 
0837     Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
0838 
0839     QString contents = text();
0840     int cursorPos = lineEdit()->cursorPosition();
0841     if (contents != form->text()) {
0842         Q_EMIT m_controller->formTextChangedByWidget(pageItem()->pageNumber(), form, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos);
0843     }
0844 
0845     m_prevCursorPos = cursorPos;
0846     m_prevAnchorPos = cursorPos;
0847     if (lineEdit()->hasSelectedText()) {
0848         if (cursorPos == lineEdit()->selectionStart()) {
0849             m_prevAnchorPos = lineEdit()->selectionStart() + lineEdit()->selectedText().size();
0850         } else {
0851             m_prevAnchorPos = lineEdit()->selectionStart();
0852         }
0853     }
0854 }
0855 
0856 void FileEdit::slotHandleFileChangedByUndoRedo(int pageNumber, Okular::FormFieldText *form, const QString &contents, int cursorPos, int anchorPos)
0857 {
0858     Q_UNUSED(pageNumber);
0859     if (form != m_ff || contents == text()) {
0860         return;
0861     }
0862     disconnect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged);
0863     setText(contents);
0864     lineEdit()->setCursorPosition(anchorPos);
0865     lineEdit()->cursorForward(true, cursorPos - anchorPos);
0866     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged);
0867     m_prevCursorPos = cursorPos;
0868     m_prevAnchorPos = anchorPos;
0869     setFocus();
0870 }
0871 
0872 ListEdit::ListEdit(Okular::FormFieldChoice *choice, PageView *pageView)
0873     : QListWidget(pageView->viewport())
0874     , FormWidgetIface(this, choice)
0875 {
0876     addItems(choice->choices());
0877     setSelectionMode(choice->multiSelect() ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection);
0878     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
0879     const QList<int> selectedItems = choice->currentChoices();
0880     if (choice->multiSelect()) {
0881         for (const int index : selectedItems) {
0882             if (index >= 0 && index < count()) {
0883                 item(index)->setSelected(true);
0884             }
0885         }
0886     } else {
0887         if (selectedItems.count() == 1 && selectedItems.at(0) >= 0 && selectedItems.at(0) < count()) {
0888             setCurrentRow(selectedItems.at(0));
0889             scrollToItem(item(selectedItems.at(0)));
0890         }
0891     }
0892 
0893     connect(this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged);
0894 
0895     setVisible(choice->isVisible());
0896     setCursor(Qt::ArrowCursor);
0897 }
0898 
0899 void ListEdit::setFormWidgetsController(FormWidgetsController *controller)
0900 {
0901     FormWidgetIface::setFormWidgetsController(controller);
0902     connect(m_controller, &FormWidgetsController::formListChangedByUndoRedo, this, &ListEdit::slotHandleFormListChangedByUndoRedo);
0903 }
0904 
0905 void ListEdit::slotSelectionChanged()
0906 {
0907     const QList<QListWidgetItem *> selection = selectedItems();
0908     QList<int> rows;
0909     for (const QListWidgetItem *item : selection) {
0910         rows.append(row(item));
0911     }
0912     Okular::FormFieldChoice *form = static_cast<Okular::FormFieldChoice *>(m_ff);
0913     if (rows != form->currentChoices()) {
0914         Q_EMIT m_controller->formListChangedByWidget(pageItem()->pageNumber(), form, rows);
0915     }
0916 }
0917 
0918 void ListEdit::slotHandleFormListChangedByUndoRedo(int pageNumber, Okular::FormFieldChoice *listForm, const QList<int> &choices)
0919 {
0920     Q_UNUSED(pageNumber);
0921 
0922     if (m_ff != listForm) {
0923         return;
0924     }
0925 
0926     disconnect(this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged);
0927     for (int i = 0; i < count(); i++) {
0928         item(i)->setSelected(choices.contains(i));
0929     }
0930     connect(this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged);
0931 
0932     setFocus();
0933 }
0934 
0935 ComboEdit::ComboEdit(Okular::FormFieldChoice *choice, PageView *pageView)
0936     : QComboBox(pageView->viewport())
0937     , FormWidgetIface(this, choice)
0938 {
0939     addItems(choice->choices());
0940     setEditable(true);
0941     setInsertPolicy(NoInsert);
0942     lineEdit()->setReadOnly(!choice->isEditable());
0943     QList<int> selectedItems = choice->currentChoices();
0944     if (selectedItems.count() == 1 && selectedItems.at(0) >= 0 && selectedItems.at(0) < count()) {
0945         setCurrentIndex(selectedItems.at(0));
0946     }
0947 
0948     if (choice->isEditable() && !choice->editChoice().isEmpty()) {
0949         lineEdit()->setText(choice->editChoice());
0950     }
0951 
0952     connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ComboEdit::slotValueChanged);
0953     connect(this, &QComboBox::editTextChanged, this, &ComboEdit::slotValueChanged);
0954     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged);
0955 
0956     setVisible(choice->isVisible());
0957     setCursor(Qt::ArrowCursor);
0958     m_prevCursorPos = lineEdit()->cursorPosition();
0959     m_prevAnchorPos = lineEdit()->cursorPosition();
0960 }
0961 
0962 void ComboEdit::setFormWidgetsController(FormWidgetsController *controller)
0963 {
0964     FormWidgetIface::setFormWidgetsController(controller);
0965     connect(m_controller, &FormWidgetsController::formComboChangedByUndoRedo, this, &ComboEdit::slotHandleFormComboChangedByUndoRedo);
0966 }
0967 
0968 void ComboEdit::slotValueChanged()
0969 {
0970     const QString text = lineEdit()->text();
0971 
0972     Okular::FormFieldChoice *form = static_cast<Okular::FormFieldChoice *>(m_ff);
0973 
0974     QString prevText;
0975     if (form->currentChoices().isEmpty()) {
0976         prevText = form->editChoice();
0977     } else {
0978         prevText = form->choices().at(form->currentChoices().constFirst());
0979     }
0980 
0981     int cursorPos = lineEdit()->cursorPosition();
0982     if (text != prevText) {
0983         Q_EMIT m_controller->formComboChangedByWidget(pageItem()->pageNumber(), form, currentText(), cursorPos, m_prevCursorPos, m_prevAnchorPos);
0984     }
0985     prevText = text;
0986     m_prevCursorPos = cursorPos;
0987     m_prevAnchorPos = cursorPos;
0988     if (lineEdit()->hasSelectedText()) {
0989         if (cursorPos == lineEdit()->selectionStart()) {
0990             m_prevAnchorPos = lineEdit()->selectionStart() + lineEdit()->selectedText().size();
0991         } else {
0992             m_prevAnchorPos = lineEdit()->selectionStart();
0993         }
0994     }
0995 }
0996 
0997 void ComboEdit::slotHandleFormComboChangedByUndoRedo(int pageNumber, Okular::FormFieldChoice *form, const QString &text, int cursorPos, int anchorPos)
0998 {
0999     Q_UNUSED(pageNumber);
1000 
1001     if (m_ff != form) {
1002         return;
1003     }
1004 
1005     // Determine if text corrisponds to an index choices
1006     int index = -1;
1007     for (int i = 0; i < count(); i++) {
1008         if (itemText(i) == text) {
1009             index = i;
1010         }
1011     }
1012 
1013     m_prevCursorPos = cursorPos;
1014     m_prevAnchorPos = anchorPos;
1015 
1016     disconnect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged);
1017     const bool isCustomValue = index == -1;
1018     if (isCustomValue) {
1019         setEditText(text);
1020     } else {
1021         setCurrentIndex(index);
1022     }
1023     lineEdit()->setCursorPosition(anchorPos);
1024     lineEdit()->cursorForward(true, cursorPos - anchorPos);
1025     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged);
1026     setFocus();
1027 }
1028 
1029 void ComboEdit::contextMenuEvent(QContextMenuEvent *event)
1030 {
1031     QMenu *menu = lineEdit()->createStandardContextMenu();
1032 
1033     QList<QAction *> actionList = menu->actions();
1034     enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct };
1035 
1036     QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
1037     QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
1038     connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
1039     connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
1040     kundo->setEnabled(m_controller->canUndo());
1041     kredo->setEnabled(m_controller->canRedo());
1042 
1043     QAction *oldUndo, *oldRedo;
1044     oldUndo = actionList[UndoAct];
1045     oldRedo = actionList[RedoAct];
1046 
1047     menu->insertAction(oldUndo, kundo);
1048     menu->insertAction(oldRedo, kredo);
1049 
1050     menu->removeAction(oldUndo);
1051     menu->removeAction(oldRedo);
1052 
1053     menu->exec(event->globalPos());
1054     delete menu;
1055 }
1056 
1057 bool ComboEdit::event(QEvent *e)
1058 {
1059     if (e->type() == QEvent::KeyPress) {
1060         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
1061         if (keyEvent == QKeySequence::Undo) {
1062             Q_EMIT m_controller->requestUndo();
1063             return true;
1064         } else if (keyEvent == QKeySequence::Redo) {
1065             Q_EMIT m_controller->requestRedo();
1066             return true;
1067         }
1068     }
1069     return QComboBox::event(e);
1070 }
1071 
1072 SignatureEdit::SignatureEdit(Okular::FormFieldSignature *signature, PageView *pageView)
1073     : QAbstractButton(pageView->viewport())
1074     , FormWidgetIface(this, signature)
1075     , m_widgetPressed(false)
1076     , m_dummyMode(false)
1077     , m_wasVisible(false)
1078 {
1079     setCursor(Qt::PointingHandCursor);
1080     if (signature->signatureType() == Okular::FormFieldSignature::UnsignedSignature) {
1081         setToolTip(i18n("Unsigned Signature Field (Click to Sign)"));
1082         connect(this, &SignatureEdit::clicked, this, &SignatureEdit::signUnsignedSignature);
1083     } else {
1084         connect(this, &SignatureEdit::clicked, this, &SignatureEdit::slotViewProperties);
1085     }
1086 }
1087 
1088 void SignatureEdit::setDummyMode(bool set)
1089 {
1090     m_dummyMode = set;
1091     if (m_dummyMode) {
1092         m_wasVisible = isVisible();
1093         // if widget was hidden then show it.
1094         // even if it wasn't hidden calling this will still update the background.
1095         setVisibility(true);
1096     } else {
1097         // forms were not visible before this call so hide this widget.
1098         if (!m_wasVisible) {
1099             setVisibility(false);
1100         } else {
1101             // forms were visible even before this call so only update the background color.
1102             update();
1103         }
1104     }
1105 }
1106 
1107 bool SignatureEdit::event(QEvent *e)
1108 {
1109     if (m_dummyMode && e->type() != QEvent::Paint) {
1110         e->accept();
1111         return true;
1112     }
1113 
1114     switch (e->type()) {
1115     case QEvent::MouseButtonPress: {
1116         QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1117         if (ev->button() == Qt::LeftButton) {
1118             m_widgetPressed = true;
1119             update();
1120         }
1121         break;
1122     }
1123     case QEvent::MouseButtonRelease: {
1124         QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1125         if (ev->button() == Qt::LeftButton) {
1126             m_widgetPressed = false;
1127             update();
1128         }
1129         break;
1130     }
1131     case QEvent::Leave: {
1132         m_widgetPressed = false;
1133         update();
1134     }
1135     default:
1136         break;
1137     }
1138 
1139     return QAbstractButton::event(e);
1140 }
1141 
1142 void SignatureEdit::contextMenuEvent(QContextMenuEvent *event)
1143 {
1144     QMenu *menu = new QMenu(this);
1145     Okular::FormFieldSignature *formSignature = static_cast<Okular::FormFieldSignature *>(formField());
1146     if (formSignature->signatureType() == Okular::FormFieldSignature::UnsignedSignature) {
1147         QAction *signAction = new QAction(i18n("&Sign..."), menu);
1148         connect(signAction, &QAction::triggered, this, &SignatureEdit::signUnsignedSignature);
1149         menu->addAction(signAction);
1150     } else {
1151         QAction *signatureProperties = new QAction(i18n("Signature Properties"), menu);
1152         connect(signatureProperties, &QAction::triggered, this, &SignatureEdit::slotViewProperties);
1153         menu->addAction(signatureProperties);
1154     }
1155     menu->exec(event->globalPos());
1156     delete menu;
1157 }
1158 
1159 void SignatureEdit::paintEvent(QPaintEvent *)
1160 {
1161     QPainter painter(this);
1162     // no borders when user hasn't allowed the forms to be shown
1163     if (m_dummyMode && !m_wasVisible) {
1164         painter.setPen(Qt::transparent);
1165     } else {
1166         painter.setPen(Qt::black);
1167     }
1168 
1169     if (m_widgetPressed || m_dummyMode) {
1170         QColor col = palette().color(QPalette::Active, QPalette::Highlight);
1171         col.setAlpha(50);
1172         painter.setBrush(col);
1173     } else {
1174         painter.setBrush(Qt::transparent);
1175     }
1176     painter.drawRect(0, 0, width() - 2, height() - 2);
1177 }
1178 
1179 void SignatureEdit::slotViewProperties()
1180 {
1181     if (m_dummyMode) {
1182         return;
1183     }
1184 
1185     Okular::FormFieldSignature *formSignature = static_cast<Okular::FormFieldSignature *>(formField());
1186     SignaturePropertiesDialog propDlg(m_controller->m_doc, formSignature, this);
1187     propDlg.exec();
1188 }
1189 
1190 void SignatureEdit::signUnsignedSignature()
1191 {
1192     if (m_dummyMode) {
1193         return;
1194     }
1195 
1196     Okular::FormFieldSignature *formSignature = static_cast<Okular::FormFieldSignature *>(formField());
1197     PageView *pageView = static_cast<PageView *>(parent()->parent());
1198     SignaturePartUtils::signUnsignedSignature(formSignature, pageView, pageView->document());
1199 }
1200 
1201 // Code for additional action handling.
1202 // Challenge: Change preprocessor magic to C++ magic!
1203 //
1204 // The mouseRelease event is special because the PDF spec
1205 // says that the activation action takes precedence over this.
1206 // So the mouse release action is only signaled if no activation
1207 // action exists.
1208 //
1209 // For checkboxes the activation action is not triggered as
1210 // they are still triggered from the clicked signal and additionally
1211 // when the checked state changes.
1212 
1213 #define DEFINE_ADDITIONAL_ACTIONS(FormClass, BaseClass)                                                                                                                                                                                        \
1214     void FormClass::mousePressEvent(QMouseEvent *event)                                                                                                                                                                                        \
1215     {                                                                                                                                                                                                                                          \
1216         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::MousePressed);                                                                                                                                                        \
1217         if (act) {                                                                                                                                                                                                                             \
1218             m_controller->signalAction(act);                                                                                                                                                                                                   \
1219         }                                                                                                                                                                                                                                      \
1220         BaseClass::mousePressEvent(event);                                                                                                                                                                                                     \
1221     }                                                                                                                                                                                                                                          \
1222     void FormClass::mouseReleaseEvent(QMouseEvent *event)                                                                                                                                                                                      \
1223     {                                                                                                                                                                                                                                          \
1224         if (!QWidget::rect().contains(event->position().toPoint())) {                                                                                                                                                                          \
1225             BaseClass::mouseReleaseEvent(event);                                                                                                                                                                                               \
1226             return;                                                                                                                                                                                                                            \
1227         }                                                                                                                                                                                                                                      \
1228         Okular::Action *act = m_ff->activationAction();                                                                                                                                                                                        \
1229         if (act && !qobject_cast<CheckBoxEdit *>(this)) {                                                                                                                                                                                      \
1230             m_controller->signalMouseUpAction(act, m_ff);                                                                                                                                                                                      \
1231         } else if ((act = m_ff->additionalAction(Okular::Annotation::MouseReleased))) {                                                                                                                                                        \
1232             m_controller->signalMouseUpAction(act, m_ff);                                                                                                                                                                                      \
1233         }                                                                                                                                                                                                                                      \
1234         BaseClass::mouseReleaseEvent(event);                                                                                                                                                                                                   \
1235     }                                                                                                                                                                                                                                          \
1236     void FormClass::focusInEvent(QFocusEvent *event)                                                                                                                                                                                           \
1237     {                                                                                                                                                                                                                                          \
1238         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::FocusIn);                                                                                                                                                             \
1239         if (act && event->reason() != Qt::ActiveWindowFocusReason) {                                                                                                                                                                           \
1240             m_controller->processScriptAction(act, m_ff, Okular::Annotation::FocusIn);                                                                                                                                                         \
1241         }                                                                                                                                                                                                                                      \
1242         BaseClass::focusInEvent(event);                                                                                                                                                                                                        \
1243     }                                                                                                                                                                                                                                          \
1244     void FormClass::focusOutEvent(QFocusEvent *event)                                                                                                                                                                                          \
1245     {                                                                                                                                                                                                                                          \
1246         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::FocusOut);                                                                                                                                                            \
1247         if (act) {                                                                                                                                                                                                                             \
1248             m_controller->processScriptAction(act, m_ff, Okular::Annotation::FocusOut);                                                                                                                                                        \
1249         }                                                                                                                                                                                                                                      \
1250         BaseClass::focusOutEvent(event);                                                                                                                                                                                                       \
1251     }                                                                                                                                                                                                                                          \
1252     void FormClass::leaveEvent(QEvent *event)                                                                                                                                                                                                  \
1253     {                                                                                                                                                                                                                                          \
1254         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::CursorLeaving);                                                                                                                                                       \
1255         if (act) {                                                                                                                                                                                                                             \
1256             m_controller->signalAction(act);                                                                                                                                                                                                   \
1257         }                                                                                                                                                                                                                                      \
1258         BaseClass::leaveEvent(event);                                                                                                                                                                                                          \
1259     }                                                                                                                                                                                                                                          \
1260     void FormClass::enterEvent(QEnterEvent *event)                                                                                                                                                                                             \
1261     {                                                                                                                                                                                                                                          \
1262         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::CursorEntering);                                                                                                                                                      \
1263         if (act) {                                                                                                                                                                                                                             \
1264             m_controller->signalAction(act);                                                                                                                                                                                                   \
1265         }                                                                                                                                                                                                                                      \
1266         BaseClass::enterEvent(event);                                                                                                                                                                                                          \
1267     }
1268 
1269 DEFINE_ADDITIONAL_ACTIONS(PushButtonEdit, QPushButton)
1270 DEFINE_ADDITIONAL_ACTIONS(CheckBoxEdit, QCheckBox)
1271 DEFINE_ADDITIONAL_ACTIONS(RadioButtonEdit, QRadioButton)
1272 DEFINE_ADDITIONAL_ACTIONS(FormLineEdit, QLineEdit)
1273 DEFINE_ADDITIONAL_ACTIONS(TextAreaEdit, KTextEdit)
1274 DEFINE_ADDITIONAL_ACTIONS(FileEdit, KUrlRequester)
1275 DEFINE_ADDITIONAL_ACTIONS(ListEdit, QListWidget)
1276 DEFINE_ADDITIONAL_ACTIONS(ComboEdit, QComboBox)
1277 DEFINE_ADDITIONAL_ACTIONS(SignatureEdit, QAbstractButton)
1278 
1279 #undef DEFINE_ADDITIONAL_ACTIONS