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