1 /*
2     SPDX-FileCopyrightText: 2007 Pino Toscano <pino@kde.org>
3     SPDX-FileCopyrightText: 2018 Intevation GmbH <intevation@intevation.de>
4 
5     Work sponsored by the LiMux project of the city of Munich:
6     SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
7 
8     SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 
11 #include "formwidgets.h"
12 #include "pageviewutils.h"
13 #include "revisionviewer.h"
14 #include "signaturepropertiesdialog.h"
15 
16 #include <KLineEdit>
17 #include <KLocalizedString>
18 #include <KStandardAction>
19 #include <QAction>
20 #include <QButtonGroup>
21 #include <QEvent>
22 #include <QKeyEvent>
23 #include <QMenu>
24 #include <QPainter>
25 #include <QUrl>
26 
27 // local includes
28 #include "core/action.h"
29 #include "core/document.h"
30 #include "debug_ui.h"
31 
FormWidgetsController(Okular::Document * doc)32 FormWidgetsController::FormWidgetsController(Okular::Document *doc)
33     : QObject(doc)
34     , m_doc(doc)
35 {
36     // emit changed signal when a form has changed
37     connect(this, &FormWidgetsController::formTextChangedByUndoRedo, this, &FormWidgetsController::changed);
38     connect(this, &FormWidgetsController::formListChangedByUndoRedo, this, &FormWidgetsController::changed);
39     connect(this, &FormWidgetsController::formComboChangedByUndoRedo, this, &FormWidgetsController::changed);
40 
41     // connect form modification signals to and from document
42     connect(this, &FormWidgetsController::formTextChangedByWidget, doc, &Okular::Document::editFormText);
43     connect(doc, &Okular::Document::formTextChangedByUndoRedo, this, &FormWidgetsController::formTextChangedByUndoRedo);
44 
45     connect(this, &FormWidgetsController::formListChangedByWidget, doc, &Okular::Document::editFormList);
46     connect(doc, &Okular::Document::formListChangedByUndoRedo, this, &FormWidgetsController::formListChangedByUndoRedo);
47 
48     connect(this, &FormWidgetsController::formComboChangedByWidget, doc, &Okular::Document::editFormCombo);
49     connect(doc, &Okular::Document::formComboChangedByUndoRedo, this, &FormWidgetsController::formComboChangedByUndoRedo);
50 
51     connect(this, &FormWidgetsController::formButtonsChangedByWidget, doc, &Okular::Document::editFormButtons);
52     connect(doc, &Okular::Document::formButtonsChangedByUndoRedo, this, &FormWidgetsController::slotFormButtonsChangedByUndoRedo);
53 
54     // Connect undo/redo signals
55     connect(this, &FormWidgetsController::requestUndo, doc, &Okular::Document::undo);
56     connect(this, &FormWidgetsController::requestRedo, doc, &Okular::Document::redo);
57 
58     connect(doc, &Okular::Document::canUndoChanged, this, &FormWidgetsController::canUndoChanged);
59     connect(doc, &Okular::Document::canRedoChanged, this, &FormWidgetsController::canRedoChanged);
60 
61     // Connect the generic formWidget refresh signal
62     connect(doc, &Okular::Document::refreshFormWidget, this, &FormWidgetsController::refreshFormWidget);
63 }
64 
~FormWidgetsController()65 FormWidgetsController::~FormWidgetsController()
66 {
67 }
68 
signalAction(Okular::Action * a)69 void FormWidgetsController::signalAction(Okular::Action *a)
70 {
71     emit action(a);
72 }
73 
processScriptAction(Okular::Action * a,Okular::FormField * field,Okular::Annotation::AdditionalActionType type)74 void FormWidgetsController::processScriptAction(Okular::Action *a, Okular::FormField *field, Okular::Annotation::AdditionalActionType type)
75 {
76     // If it's not a Action Script or if the field is not a FormText, handle it normally
77     if (a->actionType() != Okular::Action::Script || field->type() != Okular::FormField::FormText) {
78         emit action(a);
79         return;
80     }
81     switch (type) {
82     // These cases are to be handled by the FormField text, so we let it happen.
83     case Okular::Annotation::FocusIn:
84     case Okular::Annotation::FocusOut:
85         return;
86     case Okular::Annotation::PageOpening:
87     case Okular::Annotation::PageClosing:
88     case Okular::Annotation::CursorEntering:
89     case Okular::Annotation::CursorLeaving:
90     case Okular::Annotation::MousePressed:
91     case Okular::Annotation::MouseReleased:
92         emit action(a);
93     }
94 }
95 
registerRadioButton(FormWidgetIface * fwButton,Okular::FormFieldButton * formButton)96 void FormWidgetsController::registerRadioButton(FormWidgetIface *fwButton, Okular::FormFieldButton *formButton)
97 {
98     if (!fwButton)
99         return;
100 
101     QAbstractButton *button = dynamic_cast<QAbstractButton *>(fwButton);
102     if (!button) {
103         qWarning() << "fwButton is not a QAbstractButton" << fwButton;
104         return;
105     }
106 
107     QList<RadioData>::iterator it = m_radios.begin(), itEnd = m_radios.end();
108     const int id = formButton->id();
109     m_buttons.insert(id, button);
110     for (; it != itEnd; ++it) {
111         const RadioData &rd = *it;
112         const QList<int>::const_iterator idsIt = std::find(rd.ids.begin(), rd.ids.end(), id);
113         if (idsIt != rd.ids.constEnd()) {
114             qCDebug(OkularUiDebug) << "Adding id" << id << "To group including" << rd.ids;
115             rd.group->addButton(button);
116             rd.group->setId(button, id);
117             return;
118         }
119     }
120 
121     const QList<int> siblings = formButton->siblings();
122 
123     RadioData newdata;
124     newdata.ids = siblings;
125     newdata.ids.append(id);
126     newdata.group = new QButtonGroup();
127     newdata.group->addButton(button);
128     newdata.group->setId(button, id);
129 
130     // Groups of 1 (like checkboxes) can't be exclusive
131     if (siblings.isEmpty())
132         newdata.group->setExclusive(false);
133 
134     connect(newdata.group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this, &FormWidgetsController::slotButtonClicked);
135     m_radios.append(newdata);
136 }
137 
dropRadioButtons()138 void FormWidgetsController::dropRadioButtons()
139 {
140     QList<RadioData>::iterator it = m_radios.begin(), itEnd = m_radios.end();
141     for (; it != itEnd; ++it) {
142         delete (*it).group;
143     }
144     m_radios.clear();
145     m_buttons.clear();
146 }
147 
canUndo()148 bool FormWidgetsController::canUndo()
149 {
150     return m_doc->canUndo();
151 }
152 
canRedo()153 bool FormWidgetsController::canRedo()
154 {
155     return m_doc->canRedo();
156 }
157 
shouldFormWidgetBeShown(Okular::FormField * form)158 bool FormWidgetsController::shouldFormWidgetBeShown(Okular::FormField *form)
159 {
160     return !form->isReadOnly() || form->type() == Okular::FormField::FormSignature;
161 }
162 
slotButtonClicked(QAbstractButton * button)163 void FormWidgetsController::slotButtonClicked(QAbstractButton *button)
164 {
165     int pageNumber = -1;
166     CheckBoxEdit *check = qobject_cast<CheckBoxEdit *>(button);
167     if (check) {
168         // Checkboxes need to be uncheckable so if clicking a checked one
169         // disable the exclusive status temporarily and uncheck it
170         Okular::FormFieldButton *formButton = static_cast<Okular::FormFieldButton *>(check->formField());
171         if (formButton->state()) {
172             const bool wasExclusive = button->group()->exclusive();
173             button->group()->setExclusive(false);
174             check->setChecked(false);
175             button->group()->setExclusive(wasExclusive);
176         }
177         pageNumber = check->pageItem()->pageNumber();
178     } else if (RadioButtonEdit *radio = qobject_cast<RadioButtonEdit *>(button)) {
179         pageNumber = radio->pageItem()->pageNumber();
180     }
181 
182     const QList<QAbstractButton *> buttons = button->group()->buttons();
183     QList<bool> checked;
184     QList<bool> prevChecked;
185     QList<Okular::FormFieldButton *> formButtons;
186 
187     for (QAbstractButton *button : buttons) {
188         checked.append(button->isChecked());
189         Okular::FormFieldButton *formButton = static_cast<Okular::FormFieldButton *>(dynamic_cast<FormWidgetIface *>(button)->formField());
190         formButtons.append(formButton);
191         prevChecked.append(formButton->state());
192     }
193     if (checked != prevChecked)
194         emit formButtonsChangedByWidget(pageNumber, formButtons, checked);
195     if (check) {
196         // The formButtonsChangedByWidget signal changes the value of the underlying
197         // Okular::FormField of the checkbox. We need to execute the activation
198         // action after this.
199         check->doActivateAction();
200     }
201 }
202 
slotFormButtonsChangedByUndoRedo(int pageNumber,const QList<Okular::FormFieldButton * > & formButtons)203 void FormWidgetsController::slotFormButtonsChangedByUndoRedo(int pageNumber, const QList<Okular::FormFieldButton *> &formButtons)
204 {
205     for (const Okular::FormFieldButton *formButton : formButtons) {
206         int id = formButton->id();
207         QAbstractButton *button = m_buttons[id];
208         CheckBoxEdit *check = qobject_cast<CheckBoxEdit *>(button);
209         if (check) {
210             emit refreshFormWidget(check->formField());
211         }
212         // temporarily disable exclusiveness of the button group
213         // since it breaks doing/redoing steps into which all the checkboxes
214         // are unchecked
215         const bool wasExclusive = button->group()->exclusive();
216         button->group()->setExclusive(false);
217         bool checked = formButton->state();
218         button->setChecked(checked);
219         button->group()->setExclusive(wasExclusive);
220         button->setFocus();
221     }
222     emit changed(pageNumber);
223 }
224 
createWidget(Okular::FormField * ff,QWidget * parent)225 FormWidgetIface *FormWidgetFactory::createWidget(Okular::FormField *ff, QWidget *parent)
226 {
227     FormWidgetIface *widget = nullptr;
228 
229     switch (ff->type()) {
230     case Okular::FormField::FormButton: {
231         Okular::FormFieldButton *ffb = static_cast<Okular::FormFieldButton *>(ff);
232         switch (ffb->buttonType()) {
233         case Okular::FormFieldButton::Push:
234             widget = new PushButtonEdit(ffb, parent);
235             break;
236         case Okular::FormFieldButton::CheckBox:
237             widget = new CheckBoxEdit(ffb, parent);
238             break;
239         case Okular::FormFieldButton::Radio:
240             widget = new RadioButtonEdit(ffb, parent);
241             break;
242         default:;
243         }
244         break;
245     }
246     case Okular::FormField::FormText: {
247         Okular::FormFieldText *fft = static_cast<Okular::FormFieldText *>(ff);
248         switch (fft->textType()) {
249         case Okular::FormFieldText::Multiline:
250             widget = new TextAreaEdit(fft, parent);
251             break;
252         case Okular::FormFieldText::Normal:
253             widget = new FormLineEdit(fft, parent);
254             break;
255         case Okular::FormFieldText::FileSelect:
256             widget = new FileEdit(fft, parent);
257             break;
258         }
259         break;
260     }
261     case Okular::FormField::FormChoice: {
262         Okular::FormFieldChoice *ffc = static_cast<Okular::FormFieldChoice *>(ff);
263         switch (ffc->choiceType()) {
264         case Okular::FormFieldChoice::ListBox:
265             widget = new ListEdit(ffc, parent);
266             break;
267         case Okular::FormFieldChoice::ComboBox:
268             widget = new ComboEdit(ffc, parent);
269             break;
270         }
271         break;
272     }
273     case Okular::FormField::FormSignature: {
274         Okular::FormFieldSignature *ffs = static_cast<Okular::FormFieldSignature *>(ff);
275         if (ffs->isVisible() && ffs->signatureType() != Okular::FormFieldSignature::UnknownType)
276             widget = new SignatureEdit(ffs, parent);
277         break;
278     }
279     default:;
280     }
281 
282     if (!FormWidgetsController::shouldFormWidgetBeShown(ff))
283         widget->setVisibility(false);
284 
285     return widget;
286 }
287 
FormWidgetIface(QWidget * w,Okular::FormField * ff)288 FormWidgetIface::FormWidgetIface(QWidget *w, Okular::FormField *ff)
289     : m_controller(nullptr)
290     , m_ff(ff)
291     , m_widget(w)
292     , m_pageItem(nullptr)
293 {
294 }
295 
~FormWidgetIface()296 FormWidgetIface::~FormWidgetIface()
297 {
298 }
299 
rect() const300 Okular::NormalizedRect FormWidgetIface::rect() const
301 {
302     return m_ff->rect();
303 }
304 
setWidthHeight(int w,int h)305 void FormWidgetIface::setWidthHeight(int w, int h)
306 {
307     m_widget->resize(w, h);
308 }
309 
moveTo(int x,int y)310 void FormWidgetIface::moveTo(int x, int y)
311 {
312     m_widget->move(x, y);
313 }
314 
setVisibility(bool visible)315 bool FormWidgetIface::setVisibility(bool visible)
316 {
317     bool hadfocus = m_widget->hasFocus();
318     if (hadfocus)
319         m_widget->clearFocus();
320     m_widget->setVisible(visible);
321     return hadfocus;
322 }
323 
setCanBeFilled(bool fill)324 void FormWidgetIface::setCanBeFilled(bool fill)
325 {
326     m_widget->setEnabled(fill);
327 }
328 
setPageItem(PageViewItem * pageItem)329 void FormWidgetIface::setPageItem(PageViewItem *pageItem)
330 {
331     m_pageItem = pageItem;
332 }
333 
setFormField(Okular::FormField * field)334 void FormWidgetIface::setFormField(Okular::FormField *field)
335 {
336     m_ff = field;
337 }
338 
formField() const339 Okular::FormField *FormWidgetIface::formField() const
340 {
341     return m_ff;
342 }
343 
pageItem() const344 PageViewItem *FormWidgetIface::pageItem() const
345 {
346     return m_pageItem;
347 }
348 
setFormWidgetsController(FormWidgetsController * controller)349 void FormWidgetIface::setFormWidgetsController(FormWidgetsController *controller)
350 {
351     m_controller = controller;
352     QObject *obj = dynamic_cast<QObject *>(this);
353     QObject::connect(m_controller, &FormWidgetsController::refreshFormWidget, obj, [this](Okular::FormField *form) { slotRefresh(form); });
354 }
355 
slotRefresh(Okular::FormField * form)356 void FormWidgetIface::slotRefresh(Okular::FormField *form)
357 {
358     if (m_ff != form) {
359         return;
360     }
361     setVisibility(form->isVisible() && m_controller->shouldFormWidgetBeShown(form));
362 
363     m_widget->setEnabled(!form->isReadOnly());
364 }
365 
PushButtonEdit(Okular::FormFieldButton * button,QWidget * parent)366 PushButtonEdit::PushButtonEdit(Okular::FormFieldButton *button, QWidget *parent)
367     : QPushButton(parent)
368     , FormWidgetIface(this, button)
369 {
370     setText(button->caption());
371 
372     if (button->caption().isEmpty()) {
373         setFlat(true);
374     }
375 
376     setVisible(button->isVisible());
377     setCursor(Qt::ArrowCursor);
378 }
379 
CheckBoxEdit(Okular::FormFieldButton * button,QWidget * parent)380 CheckBoxEdit::CheckBoxEdit(Okular::FormFieldButton *button, QWidget *parent)
381     : QCheckBox(parent)
382     , FormWidgetIface(this, button)
383 {
384     setText(button->caption());
385 
386     setVisible(button->isVisible());
387     setCursor(Qt::ArrowCursor);
388 }
389 
setFormWidgetsController(FormWidgetsController * controller)390 void CheckBoxEdit::setFormWidgetsController(FormWidgetsController *controller)
391 {
392     Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
393     FormWidgetIface::setFormWidgetsController(controller);
394     m_controller->registerRadioButton(this, form);
395     setChecked(form->state());
396 }
397 
doActivateAction()398 void CheckBoxEdit::doActivateAction()
399 {
400     Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
401     if (form->activationAction())
402         m_controller->signalAction(form->activationAction());
403 }
404 
slotRefresh(Okular::FormField * form)405 void CheckBoxEdit::slotRefresh(Okular::FormField *form)
406 {
407     if (form != m_ff) {
408         return;
409     }
410     FormWidgetIface::slotRefresh(form);
411 
412     Okular::FormFieldButton *button = static_cast<Okular::FormFieldButton *>(m_ff);
413     bool oldState = isChecked();
414     bool newState = button->state();
415     if (oldState != newState) {
416         setChecked(button->state());
417         doActivateAction();
418     }
419 }
420 
RadioButtonEdit(Okular::FormFieldButton * button,QWidget * parent)421 RadioButtonEdit::RadioButtonEdit(Okular::FormFieldButton *button, QWidget *parent)
422     : QRadioButton(parent)
423     , FormWidgetIface(this, button)
424 {
425     setText(button->caption());
426 
427     setVisible(button->isVisible());
428     setCursor(Qt::ArrowCursor);
429 }
430 
setFormWidgetsController(FormWidgetsController * controller)431 void RadioButtonEdit::setFormWidgetsController(FormWidgetsController *controller)
432 {
433     Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
434     FormWidgetIface::setFormWidgetsController(controller);
435     m_controller->registerRadioButton(this, form);
436     setChecked(form->state());
437 }
438 
FormLineEdit(Okular::FormFieldText * text,QWidget * parent)439 FormLineEdit::FormLineEdit(Okular::FormFieldText *text, QWidget *parent)
440     : QLineEdit(parent)
441     , FormWidgetIface(this, text)
442 {
443     int maxlen = text->maximumLength();
444     if (maxlen >= 0)
445         setMaxLength(maxlen);
446     setAlignment(text->textAlignment());
447     setText(text->text());
448     if (text->isPassword())
449         setEchoMode(QLineEdit::Password);
450 
451     m_prevCursorPos = cursorPosition();
452     m_prevAnchorPos = cursorPosition();
453     m_editing = false;
454 
455     connect(this, &QLineEdit::textEdited, this, &FormLineEdit::slotChanged);
456     connect(this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged);
457 
458     setVisible(text->isVisible());
459 }
460 
setFormWidgetsController(FormWidgetsController * controller)461 void FormLineEdit::setFormWidgetsController(FormWidgetsController *controller)
462 {
463     FormWidgetIface::setFormWidgetsController(controller);
464     connect(m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &FormLineEdit::slotHandleTextChangedByUndoRedo);
465 }
466 
event(QEvent * e)467 bool FormLineEdit::event(QEvent *e)
468 {
469     if (e->type() == QEvent::KeyPress) {
470         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
471         if (keyEvent == QKeySequence::Undo) {
472             emit m_controller->requestUndo();
473             return true;
474         } else if (keyEvent == QKeySequence::Redo) {
475             emit m_controller->requestRedo();
476             return true;
477         }
478     } else if (e->type() == QEvent::FocusIn) {
479         const auto fft = static_cast<Okular::FormFieldText *>(m_ff);
480         if (text() != fft->text())
481             setText(fft->text());
482         m_editing = true;
483 
484         QFocusEvent *focusEvent = static_cast<QFocusEvent *>(e);
485         if (focusEvent->reason() != Qt::ActiveWindowFocusReason) {
486             if (const Okular::Action *action = m_ff->additionalAction(Okular::Annotation::FocusIn))
487                 emit m_controller->focusAction(action, fft);
488         }
489         setFocus();
490     } else if (e->type() == QEvent::FocusOut) {
491         m_editing = false;
492 
493         // Don't worry about focus events from other sources than the user FocusEvent to edit the field
494         QFocusEvent *focusEvent = static_cast<QFocusEvent *>(e);
495         if (focusEvent->reason() == Qt::OtherFocusReason || focusEvent->reason() == Qt::ActiveWindowFocusReason)
496             return true;
497 
498         if (const Okular::Action *action = m_ff->additionalAction(Okular::Annotation::FocusOut)) {
499             bool ok = false;
500             emit m_controller->validateAction(action, static_cast<Okular::FormFieldText *>(m_ff), ok);
501         }
502         if (const Okular::Action *action = m_ff->additionalAction(Okular::FormField::FormatField)) {
503             emit m_controller->formatAction(action, static_cast<Okular::FormFieldText *>(m_ff));
504         }
505     }
506     return QLineEdit::event(e);
507 }
508 
contextMenuEvent(QContextMenuEvent * event)509 void FormLineEdit::contextMenuEvent(QContextMenuEvent *event)
510 {
511     QMenu *menu = createStandardContextMenu();
512 
513     QList<QAction *> actionList = menu->actions();
514     enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct };
515 
516     QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
517     QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
518     connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
519     connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
520     kundo->setEnabled(m_controller->canUndo());
521     kredo->setEnabled(m_controller->canRedo());
522 
523     QAction *oldUndo, *oldRedo;
524     oldUndo = actionList[UndoAct];
525     oldRedo = actionList[RedoAct];
526 
527     menu->insertAction(oldUndo, kundo);
528     menu->insertAction(oldRedo, kredo);
529 
530     menu->removeAction(oldUndo);
531     menu->removeAction(oldRedo);
532 
533     menu->exec(event->globalPos());
534     delete menu;
535 }
536 
slotChanged()537 void FormLineEdit::slotChanged()
538 {
539     Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
540     QString contents = text();
541     int cursorPos = cursorPosition();
542 
543     if (form->additionalAction(Okular::FormField::FieldModified) && m_editing && !form->isReadOnly()) {
544         bool ok = false;
545         QString oldInputText = form->text();
546         form->setText(text());
547         emit m_controller->keystrokeAction(form->additionalAction(Okular::FormField::FieldModified), form, ok);
548         form->setText(oldInputText);
549         if (!ok) {
550             setText(oldInputText);
551             return;
552         }
553     }
554 
555     if (contents != form->text()) {
556         emit m_controller->formTextChangedByWidget(pageItem()->pageNumber(), form, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos);
557     }
558 
559     m_prevCursorPos = cursorPos;
560     m_prevAnchorPos = cursorPos;
561     if (hasSelectedText()) {
562         if (cursorPos == selectionStart()) {
563             m_prevAnchorPos = selectionStart() + selectedText().size();
564         } else {
565             m_prevAnchorPos = selectionStart();
566         }
567     }
568 }
569 
slotHandleTextChangedByUndoRedo(int pageNumber,Okular::FormFieldText * textForm,const QString & contents,int cursorPos,int anchorPos)570 void FormLineEdit::slotHandleTextChangedByUndoRedo(int pageNumber, Okular::FormFieldText *textForm, const QString &contents, int cursorPos, int anchorPos)
571 {
572     Q_UNUSED(pageNumber);
573     if (textForm != m_ff || contents == text()) {
574         return;
575     }
576     disconnect(this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged);
577     setText(contents);
578     setCursorPosition(anchorPos);
579     cursorForward(true, cursorPos - anchorPos);
580     connect(this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged);
581     m_prevCursorPos = cursorPos;
582     m_prevAnchorPos = anchorPos;
583     setFocus();
584 }
585 
slotRefresh(Okular::FormField * form)586 void FormLineEdit::slotRefresh(Okular::FormField *form)
587 {
588     if (form != m_ff) {
589         return;
590     }
591     FormWidgetIface::slotRefresh(form);
592 
593     Okular::FormFieldText *text = static_cast<Okular::FormFieldText *>(form);
594     setText(text->text());
595 }
596 
TextAreaEdit(Okular::FormFieldText * text,QWidget * parent)597 TextAreaEdit::TextAreaEdit(Okular::FormFieldText *text, QWidget *parent)
598     : KTextEdit(parent)
599     , FormWidgetIface(this, text)
600 {
601     setAcceptRichText(text->isRichText());
602     setCheckSpellingEnabled(text->canBeSpellChecked());
603     setAlignment(text->textAlignment());
604     setPlainText(text->text());
605     setUndoRedoEnabled(false);
606 
607     connect(this, &QTextEdit::textChanged, this, &TextAreaEdit::slotChanged);
608     connect(this, &QTextEdit::cursorPositionChanged, this, &TextAreaEdit::slotChanged);
609     connect(this, &KTextEdit::aboutToShowContextMenu, this, &TextAreaEdit::slotUpdateUndoAndRedoInContextMenu);
610     m_prevCursorPos = textCursor().position();
611     m_prevAnchorPos = textCursor().anchor();
612     m_editing = false;
613     setVisible(text->isVisible());
614 }
615 
~TextAreaEdit()616 TextAreaEdit::~TextAreaEdit()
617 {
618     // We need this because otherwise on destruction we destruct the syntax highlighter
619     // that ends up calling text changed but then we go to slotChanged and we're already
620     // half destructed and all is bad
621     disconnect(this, &QTextEdit::textChanged, this, &TextAreaEdit::slotChanged);
622 }
623 
event(QEvent * e)624 bool TextAreaEdit::event(QEvent *e)
625 {
626     if (e->type() == QEvent::KeyPress) {
627         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
628         if (keyEvent == QKeySequence::Undo) {
629             emit m_controller->requestUndo();
630             return true;
631         } else if (keyEvent == QKeySequence::Redo) {
632             emit m_controller->requestRedo();
633             return true;
634         }
635     } else if (e->type() == QEvent::FocusIn) {
636         const auto fft = static_cast<Okular::FormFieldText *>(m_ff);
637         if (toPlainText() != fft->text())
638             setText(fft->text());
639         m_editing = true;
640     } else if (e->type() == QEvent::FocusOut) {
641         m_editing = false;
642         if (const Okular::Action *action = m_ff->additionalAction(Okular::FormField::FormatField)) {
643             emit m_controller->formatAction(action, static_cast<Okular::FormFieldText *>(m_ff));
644         }
645     }
646     return KTextEdit::event(e);
647 }
648 
slotUpdateUndoAndRedoInContextMenu(QMenu * menu)649 void TextAreaEdit::slotUpdateUndoAndRedoInContextMenu(QMenu *menu)
650 {
651     if (!menu)
652         return;
653 
654     QList<QAction *> actionList = menu->actions();
655     enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
656 
657     QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
658     QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
659     connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
660     connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
661     kundo->setEnabled(m_controller->canUndo());
662     kredo->setEnabled(m_controller->canRedo());
663 
664     QAction *oldUndo, *oldRedo;
665     oldUndo = actionList[UndoAct];
666     oldRedo = actionList[RedoAct];
667 
668     menu->insertAction(oldUndo, kundo);
669     menu->insertAction(oldRedo, kredo);
670 
671     menu->removeAction(oldUndo);
672     menu->removeAction(oldRedo);
673 }
674 
setFormWidgetsController(FormWidgetsController * controller)675 void TextAreaEdit::setFormWidgetsController(FormWidgetsController *controller)
676 {
677     FormWidgetIface::setFormWidgetsController(controller);
678     connect(m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &TextAreaEdit::slotHandleTextChangedByUndoRedo);
679 }
680 
slotHandleTextChangedByUndoRedo(int pageNumber,Okular::FormFieldText * textForm,const QString & contents,int cursorPos,int anchorPos)681 void TextAreaEdit::slotHandleTextChangedByUndoRedo(int pageNumber, Okular::FormFieldText *textForm, const QString &contents, int cursorPos, int anchorPos)
682 {
683     Q_UNUSED(pageNumber);
684     if (textForm != m_ff) {
685         return;
686     }
687     setPlainText(contents);
688     QTextCursor c = textCursor();
689     c.setPosition(anchorPos);
690     c.setPosition(cursorPos, QTextCursor::KeepAnchor);
691     m_prevCursorPos = cursorPos;
692     m_prevAnchorPos = anchorPos;
693     setTextCursor(c);
694     setFocus();
695 }
696 
slotChanged()697 void TextAreaEdit::slotChanged()
698 {
699     Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
700     QString contents = toPlainText();
701     int cursorPos = textCursor().position();
702 
703     if (form->additionalAction(Okular::FormField::FieldModified) && m_editing && !form->isReadOnly()) {
704         bool ok = false;
705         QString oldInputText = form->text();
706         form->setText(toPlainText());
707         emit m_controller->keystrokeAction(form->additionalAction(Okular::FormField::FieldModified), form, ok);
708         form->setText(oldInputText);
709         if (!ok) {
710             setText(oldInputText);
711             return;
712         }
713     }
714 
715     if (contents != form->text()) {
716         emit m_controller->formTextChangedByWidget(pageItem()->pageNumber(), form, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos);
717     }
718     m_prevCursorPos = cursorPos;
719     m_prevAnchorPos = textCursor().anchor();
720 }
721 
slotRefresh(Okular::FormField * form)722 void TextAreaEdit::slotRefresh(Okular::FormField *form)
723 {
724     if (form != m_ff) {
725         return;
726     }
727     FormWidgetIface::slotRefresh(form);
728 
729     Okular::FormFieldText *text = static_cast<Okular::FormFieldText *>(form);
730     setPlainText(text->text());
731 }
732 
FileEdit(Okular::FormFieldText * text,QWidget * parent)733 FileEdit::FileEdit(Okular::FormFieldText *text, QWidget *parent)
734     : KUrlRequester(parent)
735     , FormWidgetIface(this, text)
736 {
737     setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly);
738     setFilter(i18n("*|All Files"));
739     setUrl(QUrl::fromUserInput(text->text()));
740     lineEdit()->setAlignment(text->textAlignment());
741 
742     m_prevCursorPos = lineEdit()->cursorPosition();
743     m_prevAnchorPos = lineEdit()->cursorPosition();
744 
745     connect(this, &KUrlRequester::textChanged, this, &FileEdit::slotChanged);
746     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged);
747     setVisible(text->isVisible());
748 }
749 
setFormWidgetsController(FormWidgetsController * controller)750 void FileEdit::setFormWidgetsController(FormWidgetsController *controller)
751 {
752     FormWidgetIface::setFormWidgetsController(controller);
753     connect(m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &FileEdit::slotHandleFileChangedByUndoRedo);
754 }
755 
eventFilter(QObject * obj,QEvent * event)756 bool FileEdit::eventFilter(QObject *obj, QEvent *event)
757 {
758     if (obj == lineEdit()) {
759         if (event->type() == QEvent::KeyPress) {
760             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
761             if (keyEvent == QKeySequence::Undo) {
762                 emit m_controller->requestUndo();
763                 return true;
764             } else if (keyEvent == QKeySequence::Redo) {
765                 emit m_controller->requestRedo();
766                 return true;
767             }
768         } else if (event->type() == QEvent::ContextMenu) {
769             QContextMenuEvent *contextMenuEvent = static_cast<QContextMenuEvent *>(event);
770 
771             QMenu *menu = ((QLineEdit *)lineEdit())->createStandardContextMenu();
772 
773             QList<QAction *> actionList = menu->actions();
774             enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct };
775 
776             QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
777             QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
778             connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
779             connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
780             kundo->setEnabled(m_controller->canUndo());
781             kredo->setEnabled(m_controller->canRedo());
782 
783             QAction *oldUndo, *oldRedo;
784             oldUndo = actionList[UndoAct];
785             oldRedo = actionList[RedoAct];
786 
787             menu->insertAction(oldUndo, kundo);
788             menu->insertAction(oldRedo, kredo);
789 
790             menu->removeAction(oldUndo);
791             menu->removeAction(oldRedo);
792 
793             menu->exec(contextMenuEvent->globalPos());
794             delete menu;
795             return true;
796         }
797     }
798     return KUrlRequester::eventFilter(obj, event);
799 }
800 
slotChanged()801 void FileEdit::slotChanged()
802 {
803     // Make sure line edit's text matches url expansion
804     if (text() != url().toLocalFile())
805         this->setText(url().toLocalFile());
806 
807     Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(m_ff);
808 
809     QString contents = text();
810     int cursorPos = lineEdit()->cursorPosition();
811     if (contents != form->text()) {
812         emit m_controller->formTextChangedByWidget(pageItem()->pageNumber(), form, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos);
813     }
814 
815     m_prevCursorPos = cursorPos;
816     m_prevAnchorPos = cursorPos;
817     if (lineEdit()->hasSelectedText()) {
818         if (cursorPos == lineEdit()->selectionStart()) {
819             m_prevAnchorPos = lineEdit()->selectionStart() + lineEdit()->selectedText().size();
820         } else {
821             m_prevAnchorPos = lineEdit()->selectionStart();
822         }
823     }
824 }
825 
slotHandleFileChangedByUndoRedo(int pageNumber,Okular::FormFieldText * form,const QString & contents,int cursorPos,int anchorPos)826 void FileEdit::slotHandleFileChangedByUndoRedo(int pageNumber, Okular::FormFieldText *form, const QString &contents, int cursorPos, int anchorPos)
827 {
828     Q_UNUSED(pageNumber);
829     if (form != m_ff || contents == text()) {
830         return;
831     }
832     disconnect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged);
833     setText(contents);
834     lineEdit()->setCursorPosition(anchorPos);
835     lineEdit()->cursorForward(true, cursorPos - anchorPos);
836     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged);
837     m_prevCursorPos = cursorPos;
838     m_prevAnchorPos = anchorPos;
839     setFocus();
840 }
841 
ListEdit(Okular::FormFieldChoice * choice,QWidget * parent)842 ListEdit::ListEdit(Okular::FormFieldChoice *choice, QWidget *parent)
843     : QListWidget(parent)
844     , FormWidgetIface(this, choice)
845 {
846     addItems(choice->choices());
847     setSelectionMode(choice->multiSelect() ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection);
848     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
849     const QList<int> selectedItems = choice->currentChoices();
850     if (choice->multiSelect()) {
851         for (const int index : selectedItems)
852             if (index >= 0 && index < count())
853                 item(index)->setSelected(true);
854     } else {
855         if (selectedItems.count() == 1 && selectedItems.at(0) >= 0 && selectedItems.at(0) < count()) {
856             setCurrentRow(selectedItems.at(0));
857             scrollToItem(item(selectedItems.at(0)));
858         }
859     }
860 
861     connect(this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged);
862 
863     setVisible(choice->isVisible());
864     setCursor(Qt::ArrowCursor);
865 }
866 
setFormWidgetsController(FormWidgetsController * controller)867 void ListEdit::setFormWidgetsController(FormWidgetsController *controller)
868 {
869     FormWidgetIface::setFormWidgetsController(controller);
870     connect(m_controller, &FormWidgetsController::formListChangedByUndoRedo, this, &ListEdit::slotHandleFormListChangedByUndoRedo);
871 }
872 
slotSelectionChanged()873 void ListEdit::slotSelectionChanged()
874 {
875     const QList<QListWidgetItem *> selection = selectedItems();
876     QList<int> rows;
877     for (const QListWidgetItem *item : selection) {
878         rows.append(row(item));
879     }
880     Okular::FormFieldChoice *form = static_cast<Okular::FormFieldChoice *>(m_ff);
881     if (rows != form->currentChoices()) {
882         emit m_controller->formListChangedByWidget(pageItem()->pageNumber(), form, rows);
883     }
884 }
885 
slotHandleFormListChangedByUndoRedo(int pageNumber,Okular::FormFieldChoice * listForm,const QList<int> & choices)886 void ListEdit::slotHandleFormListChangedByUndoRedo(int pageNumber, Okular::FormFieldChoice *listForm, const QList<int> &choices)
887 {
888     Q_UNUSED(pageNumber);
889 
890     if (m_ff != listForm) {
891         return;
892     }
893 
894     disconnect(this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged);
895     for (int i = 0; i < count(); i++) {
896         item(i)->setSelected(choices.contains(i));
897     }
898     connect(this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged);
899 
900     setFocus();
901 }
902 
ComboEdit(Okular::FormFieldChoice * choice,QWidget * parent)903 ComboEdit::ComboEdit(Okular::FormFieldChoice *choice, QWidget *parent)
904     : QComboBox(parent)
905     , FormWidgetIface(this, choice)
906 {
907     addItems(choice->choices());
908     setEditable(true);
909     setInsertPolicy(NoInsert);
910     lineEdit()->setReadOnly(!choice->isEditable());
911     QList<int> selectedItems = choice->currentChoices();
912     if (selectedItems.count() == 1 && selectedItems.at(0) >= 0 && selectedItems.at(0) < count())
913         setCurrentIndex(selectedItems.at(0));
914 
915     if (choice->isEditable() && !choice->editChoice().isEmpty())
916         lineEdit()->setText(choice->editChoice());
917 
918     connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ComboEdit::slotValueChanged);
919     connect(this, &QComboBox::editTextChanged, this, &ComboEdit::slotValueChanged);
920     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged);
921 
922     setVisible(choice->isVisible());
923     setCursor(Qt::ArrowCursor);
924     m_prevCursorPos = lineEdit()->cursorPosition();
925     m_prevAnchorPos = lineEdit()->cursorPosition();
926 }
927 
setFormWidgetsController(FormWidgetsController * controller)928 void ComboEdit::setFormWidgetsController(FormWidgetsController *controller)
929 {
930     FormWidgetIface::setFormWidgetsController(controller);
931     connect(m_controller, &FormWidgetsController::formComboChangedByUndoRedo, this, &ComboEdit::slotHandleFormComboChangedByUndoRedo);
932 }
933 
slotValueChanged()934 void ComboEdit::slotValueChanged()
935 {
936     const QString text = lineEdit()->text();
937 
938     Okular::FormFieldChoice *form = static_cast<Okular::FormFieldChoice *>(m_ff);
939 
940     QString prevText;
941     if (form->currentChoices().isEmpty()) {
942         prevText = form->editChoice();
943     } else {
944         prevText = form->choices().at(form->currentChoices().constFirst());
945     }
946 
947     int cursorPos = lineEdit()->cursorPosition();
948     if (text != prevText) {
949         emit m_controller->formComboChangedByWidget(pageItem()->pageNumber(), form, currentText(), cursorPos, m_prevCursorPos, m_prevAnchorPos);
950     }
951     prevText = text;
952     m_prevCursorPos = cursorPos;
953     m_prevAnchorPos = cursorPos;
954     if (lineEdit()->hasSelectedText()) {
955         if (cursorPos == lineEdit()->selectionStart()) {
956             m_prevAnchorPos = lineEdit()->selectionStart() + lineEdit()->selectedText().size();
957         } else {
958             m_prevAnchorPos = lineEdit()->selectionStart();
959         }
960     }
961 }
962 
slotHandleFormComboChangedByUndoRedo(int pageNumber,Okular::FormFieldChoice * form,const QString & text,int cursorPos,int anchorPos)963 void ComboEdit::slotHandleFormComboChangedByUndoRedo(int pageNumber, Okular::FormFieldChoice *form, const QString &text, int cursorPos, int anchorPos)
964 {
965     Q_UNUSED(pageNumber);
966 
967     if (m_ff != form) {
968         return;
969     }
970 
971     // Determine if text corrisponds to an index choices
972     int index = -1;
973     for (int i = 0; i < count(); i++) {
974         if (itemText(i) == text) {
975             index = i;
976         }
977     }
978 
979     m_prevCursorPos = cursorPos;
980     m_prevAnchorPos = anchorPos;
981 
982     disconnect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged);
983     const bool isCustomValue = index == -1;
984     if (isCustomValue) {
985         setEditText(text);
986     } else {
987         setCurrentIndex(index);
988     }
989     lineEdit()->setCursorPosition(anchorPos);
990     lineEdit()->cursorForward(true, cursorPos - anchorPos);
991     connect(lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged);
992     setFocus();
993 }
994 
contextMenuEvent(QContextMenuEvent * event)995 void ComboEdit::contextMenuEvent(QContextMenuEvent *event)
996 {
997     QMenu *menu = lineEdit()->createStandardContextMenu();
998 
999     QList<QAction *> actionList = menu->actions();
1000     enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct };
1001 
1002     QAction *kundo = KStandardAction::create(KStandardAction::Undo, m_controller, SIGNAL(requestUndo()), menu);
1003     QAction *kredo = KStandardAction::create(KStandardAction::Redo, m_controller, SIGNAL(requestRedo()), menu);
1004     connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled);
1005     connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled);
1006     kundo->setEnabled(m_controller->canUndo());
1007     kredo->setEnabled(m_controller->canRedo());
1008 
1009     QAction *oldUndo, *oldRedo;
1010     oldUndo = actionList[UndoAct];
1011     oldRedo = actionList[RedoAct];
1012 
1013     menu->insertAction(oldUndo, kundo);
1014     menu->insertAction(oldRedo, kredo);
1015 
1016     menu->removeAction(oldUndo);
1017     menu->removeAction(oldRedo);
1018 
1019     menu->exec(event->globalPos());
1020     delete menu;
1021 }
1022 
event(QEvent * e)1023 bool ComboEdit::event(QEvent *e)
1024 {
1025     if (e->type() == QEvent::KeyPress) {
1026         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
1027         if (keyEvent == QKeySequence::Undo) {
1028             emit m_controller->requestUndo();
1029             return true;
1030         } else if (keyEvent == QKeySequence::Redo) {
1031             emit m_controller->requestRedo();
1032             return true;
1033         }
1034     }
1035     return QComboBox::event(e);
1036 }
1037 
SignatureEdit(Okular::FormFieldSignature * signature,QWidget * parent)1038 SignatureEdit::SignatureEdit(Okular::FormFieldSignature *signature, QWidget *parent)
1039     : QAbstractButton(parent)
1040     , FormWidgetIface(this, signature)
1041     , m_widgetPressed(false)
1042     , m_dummyMode(false)
1043     , m_wasVisible(false)
1044 {
1045     setCursor(Qt::PointingHandCursor);
1046     connect(this, &SignatureEdit::clicked, this, &SignatureEdit::slotViewProperties);
1047 }
1048 
setDummyMode(bool set)1049 void SignatureEdit::setDummyMode(bool set)
1050 {
1051     m_dummyMode = set;
1052     if (m_dummyMode) {
1053         m_wasVisible = isVisible();
1054         // if widget was hidden then show it.
1055         // even if it wasn't hidden calling this will still update the background.
1056         setVisibility(true);
1057     } else {
1058         // forms were not visible before this call so hide this widget.
1059         if (!m_wasVisible)
1060             setVisibility(false);
1061         // forms were visible even before this call so only update the background color.
1062         else
1063             update();
1064     }
1065 }
1066 
event(QEvent * e)1067 bool SignatureEdit::event(QEvent *e)
1068 {
1069     if (m_dummyMode && e->type() != QEvent::Paint) {
1070         e->accept();
1071         return true;
1072     }
1073 
1074     switch (e->type()) {
1075     case QEvent::MouseButtonPress: {
1076         QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1077         if (ev->button() == Qt::LeftButton) {
1078             m_widgetPressed = true;
1079             update();
1080         }
1081         break;
1082     }
1083     case QEvent::MouseButtonRelease: {
1084         QMouseEvent *ev = static_cast<QMouseEvent *>(e);
1085         if (ev->button() == Qt::LeftButton) {
1086             m_widgetPressed = false;
1087             update();
1088         }
1089         break;
1090     }
1091     case QEvent::Leave: {
1092         m_widgetPressed = false;
1093         update();
1094     }
1095     default:
1096         break;
1097     }
1098 
1099     return QAbstractButton::event(e);
1100 }
1101 
contextMenuEvent(QContextMenuEvent * event)1102 void SignatureEdit::contextMenuEvent(QContextMenuEvent *event)
1103 {
1104     QMenu *menu = new QMenu(this);
1105     QAction *signatureProperties = new QAction(i18n("Signature Properties"), menu);
1106     connect(signatureProperties, &QAction::triggered, this, &SignatureEdit::slotViewProperties);
1107     menu->addAction(signatureProperties);
1108     menu->exec(event->globalPos());
1109     delete menu;
1110 }
1111 
paintEvent(QPaintEvent *)1112 void SignatureEdit::paintEvent(QPaintEvent *)
1113 {
1114     QPainter painter(this);
1115     // no borders when user hasn't allowed the forms to be shown
1116     if (m_dummyMode && !m_wasVisible) {
1117         painter.setPen(Qt::transparent);
1118     } else {
1119         painter.setPen(Qt::black);
1120     }
1121 
1122     if (m_widgetPressed || m_dummyMode) {
1123         QColor col = palette().color(QPalette::Active, QPalette::Highlight);
1124         col.setAlpha(50);
1125         painter.setBrush(col);
1126     } else {
1127         painter.setBrush(Qt::transparent);
1128     }
1129     painter.drawRect(0, 0, width() - 2, height() - 2);
1130 }
1131 
slotViewProperties()1132 void SignatureEdit::slotViewProperties()
1133 {
1134     if (m_dummyMode)
1135         return;
1136 
1137     Okular::FormFieldSignature *formSignature = static_cast<Okular::FormFieldSignature *>(formField());
1138     SignaturePropertiesDialog propDlg(m_controller->m_doc, formSignature, this);
1139     propDlg.exec();
1140 }
1141 
1142 // Code for additional action handling.
1143 // Challenge: Change preprocessor magic to C++ magic!
1144 //
1145 // The mouseRelease event is special because the PDF spec
1146 // says that the activation action takes precedence over this.
1147 // So the mouse release action is only signaled if no activation
1148 // action exists.
1149 //
1150 // For checkboxes the activation action is not triggered as
1151 // they are still triggered from the clicked signal and additionally
1152 // when the checked state changes.
1153 
1154 #define DEFINE_ADDITIONAL_ACTIONS(FormClass, BaseClass)                                                                                                                                                                                        \
1155     void FormClass::mousePressEvent(QMouseEvent *event)                                                                                                                                                                                        \
1156     {                                                                                                                                                                                                                                          \
1157         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::MousePressed);                                                                                                                                                        \
1158         if (act) {                                                                                                                                                                                                                             \
1159             m_controller->signalAction(act);                                                                                                                                                                                                   \
1160         }                                                                                                                                                                                                                                      \
1161         BaseClass::mousePressEvent(event);                                                                                                                                                                                                     \
1162     }                                                                                                                                                                                                                                          \
1163     void FormClass::mouseReleaseEvent(QMouseEvent *event)                                                                                                                                                                                      \
1164     {                                                                                                                                                                                                                                          \
1165         if (!QWidget::rect().contains(event->localPos().toPoint())) {                                                                                                                                                                          \
1166             BaseClass::mouseReleaseEvent(event);                                                                                                                                                                                               \
1167             return;                                                                                                                                                                                                                            \
1168         }                                                                                                                                                                                                                                      \
1169         Okular::Action *act = m_ff->activationAction();                                                                                                                                                                                        \
1170         if (act && !qobject_cast<CheckBoxEdit *>(this)) {                                                                                                                                                                                      \
1171             m_controller->signalAction(act);                                                                                                                                                                                                   \
1172         } else if ((act = m_ff->additionalAction(Okular::Annotation::MouseReleased))) {                                                                                                                                                        \
1173             m_controller->signalAction(act);                                                                                                                                                                                                   \
1174         }                                                                                                                                                                                                                                      \
1175         BaseClass::mouseReleaseEvent(event);                                                                                                                                                                                                   \
1176     }                                                                                                                                                                                                                                          \
1177     void FormClass::focusInEvent(QFocusEvent *event)                                                                                                                                                                                           \
1178     {                                                                                                                                                                                                                                          \
1179         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::FocusIn);                                                                                                                                                             \
1180         if (act) {                                                                                                                                                                                                                             \
1181             m_controller->processScriptAction(act, m_ff, Okular::Annotation::FocusIn);                                                                                                                                                         \
1182         }                                                                                                                                                                                                                                      \
1183         BaseClass::focusInEvent(event);                                                                                                                                                                                                        \
1184     }                                                                                                                                                                                                                                          \
1185     void FormClass::focusOutEvent(QFocusEvent *event)                                                                                                                                                                                          \
1186     {                                                                                                                                                                                                                                          \
1187         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::FocusOut);                                                                                                                                                            \
1188         if (act) {                                                                                                                                                                                                                             \
1189             m_controller->processScriptAction(act, m_ff, Okular::Annotation::FocusOut);                                                                                                                                                        \
1190         }                                                                                                                                                                                                                                      \
1191         BaseClass::focusOutEvent(event);                                                                                                                                                                                                       \
1192     }                                                                                                                                                                                                                                          \
1193     void FormClass::leaveEvent(QEvent *event)                                                                                                                                                                                                  \
1194     {                                                                                                                                                                                                                                          \
1195         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::CursorLeaving);                                                                                                                                                       \
1196         if (act) {                                                                                                                                                                                                                             \
1197             m_controller->signalAction(act);                                                                                                                                                                                                   \
1198         }                                                                                                                                                                                                                                      \
1199         BaseClass::leaveEvent(event);                                                                                                                                                                                                          \
1200     }                                                                                                                                                                                                                                          \
1201     void FormClass::enterEvent(QEvent *event)                                                                                                                                                                                                  \
1202     {                                                                                                                                                                                                                                          \
1203         Okular::Action *act = m_ff->additionalAction(Okular::Annotation::CursorEntering);                                                                                                                                                      \
1204         if (act) {                                                                                                                                                                                                                             \
1205             m_controller->signalAction(act);                                                                                                                                                                                                   \
1206         }                                                                                                                                                                                                                                      \
1207         BaseClass::enterEvent(event);                                                                                                                                                                                                          \
1208     }
1209 
1210 DEFINE_ADDITIONAL_ACTIONS(PushButtonEdit, QPushButton)
1211 DEFINE_ADDITIONAL_ACTIONS(CheckBoxEdit, QCheckBox)
1212 DEFINE_ADDITIONAL_ACTIONS(RadioButtonEdit, QRadioButton)
1213 DEFINE_ADDITIONAL_ACTIONS(FormLineEdit, QLineEdit)
1214 DEFINE_ADDITIONAL_ACTIONS(TextAreaEdit, KTextEdit)
1215 DEFINE_ADDITIONAL_ACTIONS(FileEdit, KUrlRequester)
1216 DEFINE_ADDITIONAL_ACTIONS(ListEdit, QListWidget)
1217 DEFINE_ADDITIONAL_ACTIONS(ComboEdit, QComboBox)
1218 DEFINE_ADDITIONAL_ACTIONS(SignatureEdit, QAbstractButton)
1219 
1220 #undef DEFINE_ADDITIONAL_ACTIONS
1221