File indexing completed on 2024-04-28 04:32:42

0001 /*
0002     SPDX-FileCopyrightText: 2013 Jon Mease <jon.mease@gmail.com>
0003 
0004     Work sponsored by the LiMux project of the city of Munich:
0005     SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "documentcommands_p.h"
0011 
0012 #include "annotations.h"
0013 #include "debug_p.h"
0014 #include "document_p.h"
0015 #include "form.h"
0016 #include "page.h"
0017 #include "page_p.h"
0018 #include "utils_p.h"
0019 
0020 #include <KLocalizedString>
0021 
0022 namespace Okular
0023 {
0024 void moveViewportIfBoundingRectNotFullyVisible(Okular::NormalizedRect boundingRect, DocumentPrivate *docPriv, int pageNumber)
0025 {
0026     const Rotation pageRotation = docPriv->m_parent->page(pageNumber)->rotation();
0027     const QTransform rotationMatrix = Okular::buildRotationMatrix(pageRotation);
0028     boundingRect.transform(rotationMatrix);
0029     if (!docPriv->isNormalizedRectangleFullyVisible(boundingRect, pageNumber)) {
0030         DocumentViewport searchViewport(pageNumber);
0031         searchViewport.rePos.enabled = true;
0032         searchViewport.rePos.normalizedX = (boundingRect.left + boundingRect.right) / 2.0;
0033         searchViewport.rePos.normalizedY = (boundingRect.top + boundingRect.bottom) / 2.0;
0034         docPriv->m_parent->setViewport(searchViewport, nullptr, true);
0035     }
0036 }
0037 
0038 Okular::NormalizedRect buildBoundingRectangleForButtons(const QList<Okular::FormFieldButton *> &formButtons)
0039 {
0040     // Initialize coordinates of the bounding rect
0041     double left = 1.0;
0042     double top = 1.0;
0043     double right = 0.0;
0044     double bottom = 0.0;
0045 
0046     for (const FormFieldButton *formButton : formButtons) {
0047         left = qMin<double>(left, formButton->rect().left);
0048         top = qMin<double>(top, formButton->rect().top);
0049         right = qMax<double>(right, formButton->rect().right);
0050         bottom = qMax<double>(bottom, formButton->rect().bottom);
0051     }
0052     Okular::NormalizedRect boundingRect(left, top, right, bottom);
0053     return boundingRect;
0054 }
0055 
0056 AddAnnotationCommand::AddAnnotationCommand(Okular::DocumentPrivate *docPriv, Okular::Annotation *annotation, int pageNumber)
0057     : m_docPriv(docPriv)
0058     , m_annotation(annotation)
0059     , m_pageNumber(pageNumber)
0060     , m_done(false)
0061 {
0062     setText(i18nc("Add an annotation to the page", "add annotation"));
0063 }
0064 
0065 AddAnnotationCommand::~AddAnnotationCommand()
0066 {
0067     if (!m_done) {
0068         delete m_annotation;
0069     }
0070 }
0071 
0072 void AddAnnotationCommand::undo()
0073 {
0074     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0075     m_docPriv->performRemovePageAnnotation(m_pageNumber, m_annotation);
0076     m_done = false;
0077 }
0078 
0079 void AddAnnotationCommand::redo()
0080 {
0081     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0082     m_docPriv->performAddPageAnnotation(m_pageNumber, m_annotation);
0083     m_done = true;
0084 }
0085 
0086 bool AddAnnotationCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
0087 {
0088     if (m_done) {
0089         // We don't always update m_annotation because even if the annotation has been added to the document
0090         // it can have been removed later so the annotation pointer is stored inside a following RemoveAnnotationCommand
0091         // and thus doesn't need updating because it didn't change
0092         // because of the document reload
0093         auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
0094         if (a) {
0095             m_annotation = a;
0096         }
0097     }
0098 
0099     return true;
0100 }
0101 
0102 RemoveAnnotationCommand::RemoveAnnotationCommand(Okular::DocumentPrivate *doc, Okular::Annotation *annotation, int pageNumber)
0103     : m_docPriv(doc)
0104     , m_annotation(annotation)
0105     , m_pageNumber(pageNumber)
0106     , m_done(false)
0107 {
0108     setText(i18nc("Remove an annotation from the page", "remove annotation"));
0109 }
0110 
0111 RemoveAnnotationCommand::~RemoveAnnotationCommand()
0112 {
0113     if (m_done) {
0114         delete m_annotation;
0115     }
0116 }
0117 
0118 void RemoveAnnotationCommand::undo()
0119 {
0120     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0121     m_docPriv->performAddPageAnnotation(m_pageNumber, m_annotation);
0122     m_done = false;
0123 }
0124 
0125 void RemoveAnnotationCommand::redo()
0126 {
0127     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0128     m_docPriv->performRemovePageAnnotation(m_pageNumber, m_annotation);
0129     m_done = true;
0130 }
0131 
0132 bool RemoveAnnotationCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
0133 {
0134     if (!m_done) {
0135         // We don't always update m_annotation because it can happen that the annotation remove has been undo
0136         // and that annotation addition has also been undone so the annotation pointer is stored inside
0137         // a previous AddAnnotationCommand and thus doesn't need updating because it didn't change
0138         // because of the document reload
0139         auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
0140         if (a) {
0141             m_annotation = a;
0142         }
0143     }
0144 
0145     return true;
0146 }
0147 
0148 ModifyAnnotationPropertiesCommand::ModifyAnnotationPropertiesCommand(DocumentPrivate *docPriv, Annotation *annotation, int pageNumber, const QDomNode &oldProperties, const QDomNode &newProperties)
0149     : m_docPriv(docPriv)
0150     , m_annotation(annotation)
0151     , m_pageNumber(pageNumber)
0152     , m_prevProperties(oldProperties)
0153     , m_newProperties(newProperties)
0154 {
0155     setText(i18nc("Modify an annotation's internal properties (Color, line-width, etc.)", "modify annotation properties"));
0156 }
0157 
0158 void ModifyAnnotationPropertiesCommand::undo()
0159 {
0160     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0161     m_annotation->setAnnotationProperties(m_prevProperties);
0162     m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
0163 }
0164 
0165 void ModifyAnnotationPropertiesCommand::redo()
0166 {
0167     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0168     m_annotation->setAnnotationProperties(m_newProperties);
0169     m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
0170 }
0171 
0172 bool ModifyAnnotationPropertiesCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
0173 {
0174     // Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
0175     auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
0176     if (a) {
0177         m_annotation = a;
0178     }
0179 
0180     return true;
0181 }
0182 
0183 TranslateAnnotationCommand::TranslateAnnotationCommand(DocumentPrivate *docPriv, Annotation *annotation, int pageNumber, const Okular::NormalizedPoint &delta, bool completeDrag)
0184     : m_docPriv(docPriv)
0185     , m_annotation(annotation)
0186     , m_pageNumber(pageNumber)
0187     , m_delta(delta)
0188     , m_completeDrag(completeDrag)
0189 {
0190     setText(i18nc("Translate an annotation's position on the page", "translate annotation"));
0191 }
0192 
0193 void TranslateAnnotationCommand::undo()
0194 {
0195     moveViewportIfBoundingRectNotFullyVisible(translateBoundingRectangle(minusDelta()), m_docPriv, m_pageNumber);
0196     m_annotation->translate(minusDelta());
0197     m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
0198 }
0199 
0200 void TranslateAnnotationCommand::redo()
0201 {
0202     moveViewportIfBoundingRectNotFullyVisible(translateBoundingRectangle(m_delta), m_docPriv, m_pageNumber);
0203     m_annotation->translate(m_delta);
0204     m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
0205 }
0206 
0207 int TranslateAnnotationCommand::id() const
0208 {
0209     return 1;
0210 }
0211 
0212 bool TranslateAnnotationCommand::mergeWith(const QUndoCommand *uc)
0213 {
0214     TranslateAnnotationCommand *tuc = (TranslateAnnotationCommand *)uc;
0215 
0216     if (tuc->m_annotation != m_annotation) {
0217         return false;
0218     }
0219 
0220     if (m_completeDrag) {
0221         return false;
0222     }
0223     m_delta = Okular::NormalizedPoint(tuc->m_delta.x + m_delta.x, tuc->m_delta.y + m_delta.y);
0224     m_completeDrag = tuc->m_completeDrag;
0225     return true;
0226 }
0227 
0228 Okular::NormalizedPoint TranslateAnnotationCommand::minusDelta()
0229 {
0230     return Okular::NormalizedPoint(-m_delta.x, -m_delta.y);
0231 }
0232 
0233 Okular::NormalizedRect TranslateAnnotationCommand::translateBoundingRectangle(const Okular::NormalizedPoint &delta)
0234 {
0235     Okular::NormalizedRect annotBoundingRect = m_annotation->boundingRectangle();
0236     annotBoundingRect.left = annotBoundingRect.left + delta.x;
0237     annotBoundingRect.right = annotBoundingRect.right + delta.x;
0238     annotBoundingRect.top = annotBoundingRect.top + delta.y;
0239     annotBoundingRect.bottom = annotBoundingRect.bottom + delta.y;
0240 
0241     return annotBoundingRect;
0242 }
0243 
0244 bool TranslateAnnotationCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
0245 {
0246     // Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
0247     auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
0248     if (a) {
0249         m_annotation = a;
0250     }
0251 
0252     return true;
0253 }
0254 
0255 AdjustAnnotationCommand::AdjustAnnotationCommand(Okular::DocumentPrivate *docPriv, Okular::Annotation *annotation, int pageNumber, const Okular::NormalizedPoint &delta1, const Okular::NormalizedPoint &delta2, bool completeDrag)
0256     : m_docPriv(docPriv)
0257     , m_annotation(annotation)
0258     , m_pageNumber(pageNumber)
0259     , m_delta1(delta1)
0260     , m_delta2(delta2)
0261     , m_completeDrag(completeDrag)
0262 {
0263     setText(i18nc("Change an annotation's size", "adjust annotation"));
0264 }
0265 
0266 void AdjustAnnotationCommand::undo()
0267 {
0268     const NormalizedPoint minusDelta1 = Okular::NormalizedPoint(-m_delta1.x, -m_delta1.y);
0269     const NormalizedPoint minusDelta2 = Okular::NormalizedPoint(-m_delta2.x, -m_delta2.y);
0270     moveViewportIfBoundingRectNotFullyVisible(adjustBoundingRectangle(minusDelta1, minusDelta2), m_docPriv, m_pageNumber);
0271     m_annotation->adjust(minusDelta1, minusDelta2);
0272     m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
0273 }
0274 
0275 void AdjustAnnotationCommand::redo()
0276 {
0277     moveViewportIfBoundingRectNotFullyVisible(adjustBoundingRectangle(m_delta1, m_delta2), m_docPriv, m_pageNumber);
0278     m_annotation->adjust(m_delta1, m_delta2);
0279     m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
0280 }
0281 
0282 int AdjustAnnotationCommand::id() const
0283 {
0284     return 5;
0285 }
0286 
0287 bool AdjustAnnotationCommand::mergeWith(const QUndoCommand *uc)
0288 {
0289     AdjustAnnotationCommand *tuc = (AdjustAnnotationCommand *)uc;
0290 
0291     if (tuc->m_annotation != m_annotation) {
0292         return false;
0293     }
0294 
0295     if (m_completeDrag) {
0296         return false;
0297     }
0298     m_delta1 = Okular::NormalizedPoint(tuc->m_delta1.x + m_delta1.x, tuc->m_delta1.y + m_delta1.y);
0299     m_delta2 = Okular::NormalizedPoint(tuc->m_delta2.x + m_delta2.x, tuc->m_delta2.y + m_delta2.y);
0300     m_completeDrag = tuc->m_completeDrag;
0301     return true;
0302 }
0303 
0304 Okular::NormalizedRect AdjustAnnotationCommand::adjustBoundingRectangle(const Okular::NormalizedPoint &delta1, const Okular::NormalizedPoint &delta2)
0305 {
0306     Okular::NormalizedRect annotBoundingRect = m_annotation->boundingRectangle();
0307     annotBoundingRect.left = annotBoundingRect.left + delta1.x;
0308     annotBoundingRect.right = annotBoundingRect.right + delta2.x;
0309     annotBoundingRect.top = annotBoundingRect.top + delta1.y;
0310     annotBoundingRect.bottom = annotBoundingRect.bottom + delta2.y;
0311 
0312     return annotBoundingRect;
0313 }
0314 
0315 bool AdjustAnnotationCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
0316 {
0317     // Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
0318     auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
0319     if (a) {
0320         m_annotation = a;
0321     }
0322 
0323     return true;
0324 }
0325 
0326 EditTextCommand::EditTextCommand(const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
0327     : m_newContents(newContents)
0328     , m_newCursorPos(newCursorPos)
0329     , m_prevContents(prevContents)
0330     , m_prevCursorPos(prevCursorPos)
0331     , m_prevAnchorPos(prevAnchorPos)
0332 {
0333     setText(i18nc("Generic text edit command", "edit text"));
0334 
0335     //// Determine edit type
0336     // If There was a selection then edit was not a simple single character backspace, delete, or insert
0337     if (m_prevCursorPos != m_prevAnchorPos) {
0338         qCDebug(OkularCoreDebug) << "OtherEdit, selection";
0339         m_editType = OtherEdit;
0340     } else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor() == oldContentsLeftOfCursor().left(oldContentsLeftOfCursor().length() - 1) &&
0341                QStringView {oldContentsLeftOfCursor()}.right(1) != QLatin1Char('\n')) {
0342         qCDebug(OkularCoreDebug) << "CharBackspace";
0343         m_editType = CharBackspace;
0344     } else if (newContentsLeftOfCursor() == oldContentsLeftOfCursor() && newContentsRightOfCursor() == oldContentsRightOfCursor().right(oldContentsRightOfCursor().length() - 1) &&
0345                QStringView {oldContentsRightOfCursor()}.left(1) != QLatin1Char('\n')) {
0346         qCDebug(OkularCoreDebug) << "CharDelete";
0347         m_editType = CharDelete;
0348     } else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor().left(newContentsLeftOfCursor().length() - 1) == oldContentsLeftOfCursor() &&
0349                QStringView {newContentsLeftOfCursor()}.right(1) != QLatin1Char('\n')) {
0350         qCDebug(OkularCoreDebug) << "CharInsert";
0351         m_editType = CharInsert;
0352     } else {
0353         qCDebug(OkularCoreDebug) << "OtherEdit";
0354         m_editType = OtherEdit;
0355     }
0356 }
0357 
0358 bool EditTextCommand::mergeWith(const QUndoCommand *uc)
0359 {
0360     EditTextCommand *euc = (EditTextCommand *)uc;
0361 
0362     // Only attempt merge of euc into this if our new state matches euc's old state and
0363     // the editTypes match and are not type OtherEdit
0364     if (m_newContents == euc->m_prevContents && m_newCursorPos == euc->m_prevCursorPos && m_editType == euc->m_editType && m_editType != OtherEdit) {
0365         m_newContents = euc->m_newContents;
0366         m_newCursorPos = euc->m_newCursorPos;
0367         return true;
0368     }
0369     return false;
0370 }
0371 
0372 QString EditTextCommand::oldContentsLeftOfCursor()
0373 {
0374     return m_prevContents.left(m_prevCursorPos);
0375 }
0376 
0377 QString EditTextCommand::oldContentsRightOfCursor()
0378 {
0379     return m_prevContents.right(m_prevContents.length() - m_prevCursorPos);
0380 }
0381 
0382 QString EditTextCommand::newContentsLeftOfCursor()
0383 {
0384     return m_newContents.left(m_newCursorPos);
0385 }
0386 
0387 QString EditTextCommand::newContentsRightOfCursor()
0388 {
0389     return m_newContents.right(m_newContents.length() - m_newCursorPos);
0390 }
0391 
0392 EditAnnotationContentsCommand::EditAnnotationContentsCommand(DocumentPrivate *docPriv, Annotation *annotation, int pageNumber, const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
0393     : EditTextCommand(newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos)
0394     , m_docPriv(docPriv)
0395     , m_annotation(annotation)
0396     , m_pageNumber(pageNumber)
0397 {
0398     setText(i18nc("Edit an annotation's text contents", "edit annotation contents"));
0399 }
0400 
0401 void EditAnnotationContentsCommand::undo()
0402 {
0403     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0404     m_docPriv->performSetAnnotationContents(m_prevContents, m_annotation, m_pageNumber);
0405     Q_EMIT m_docPriv->m_parent->annotationContentsChangedByUndoRedo(m_annotation, m_prevContents, m_prevCursorPos, m_prevAnchorPos);
0406 }
0407 
0408 void EditAnnotationContentsCommand::redo()
0409 {
0410     moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
0411     m_docPriv->performSetAnnotationContents(m_newContents, m_annotation, m_pageNumber);
0412     Q_EMIT m_docPriv->m_parent->annotationContentsChangedByUndoRedo(m_annotation, m_newContents, m_newCursorPos, m_newCursorPos);
0413 }
0414 
0415 int EditAnnotationContentsCommand::id() const
0416 {
0417     return 2;
0418 }
0419 
0420 bool EditAnnotationContentsCommand::mergeWith(const QUndoCommand *uc)
0421 {
0422     EditAnnotationContentsCommand *euc = (EditAnnotationContentsCommand *)uc;
0423     // Only attempt merge of euc into this if they modify the same annotation
0424     if (m_annotation == euc->m_annotation) {
0425         return EditTextCommand::mergeWith(uc);
0426     } else {
0427         return false;
0428     }
0429 }
0430 
0431 bool EditAnnotationContentsCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
0432 {
0433     auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
0434     if (a) {
0435         m_annotation = a;
0436     }
0437 
0438     return true;
0439 }
0440 
0441 EditFormTextCommand::EditFormTextCommand(Okular::DocumentPrivate *docPriv, Okular::FormFieldText *form, int pageNumber, const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
0442     : EditTextCommand(newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos)
0443     , m_docPriv(docPriv)
0444     , m_form(form)
0445     , m_pageNumber(pageNumber)
0446 {
0447     setText(i18nc("Edit an form's text contents", "edit form contents"));
0448 }
0449 
0450 void EditFormTextCommand::undo()
0451 {
0452     moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
0453     m_form->setText(m_prevContents);
0454     Q_EMIT m_docPriv->m_parent->formTextChangedByUndoRedo(m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos);
0455     m_docPriv->notifyFormChanges(m_pageNumber);
0456 }
0457 
0458 void EditFormTextCommand::redo()
0459 {
0460     moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
0461     m_form->setText(m_newContents);
0462     Q_EMIT m_docPriv->m_parent->formTextChangedByUndoRedo(m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos);
0463     m_docPriv->notifyFormChanges(m_pageNumber);
0464 }
0465 
0466 int EditFormTextCommand::id() const
0467 {
0468     return 3;
0469 }
0470 
0471 bool EditFormTextCommand::mergeWith(const QUndoCommand *uc)
0472 {
0473     EditFormTextCommand *euc = (EditFormTextCommand *)uc;
0474     // Only attempt merge of euc into this if they modify the same form
0475     if (m_form == euc->m_form) {
0476         return EditTextCommand::mergeWith(uc);
0477     } else {
0478         return false;
0479     }
0480 }
0481 
0482 bool EditFormTextCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
0483 {
0484     m_form = dynamic_cast<FormFieldText *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], m_form));
0485 
0486     return m_form;
0487 }
0488 
0489 EditFormListCommand::EditFormListCommand(Okular::DocumentPrivate *docPriv, FormFieldChoice *form, int pageNumber, const QList<int> &newChoices, const QList<int> &prevChoices)
0490     : m_docPriv(docPriv)
0491     , m_form(form)
0492     , m_pageNumber(pageNumber)
0493     , m_newChoices(newChoices)
0494     , m_prevChoices(prevChoices)
0495 {
0496     setText(i18nc("Edit a list form's choices", "edit list form choices"));
0497 }
0498 
0499 void EditFormListCommand::undo()
0500 {
0501     moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
0502     m_form->setCurrentChoices(m_prevChoices);
0503     Q_EMIT m_docPriv->m_parent->formListChangedByUndoRedo(m_pageNumber, m_form, m_prevChoices);
0504     m_docPriv->notifyFormChanges(m_pageNumber);
0505 }
0506 
0507 void EditFormListCommand::redo()
0508 {
0509     moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
0510     m_form->setCurrentChoices(m_newChoices);
0511     Q_EMIT m_docPriv->m_parent->formListChangedByUndoRedo(m_pageNumber, m_form, m_newChoices);
0512     m_docPriv->notifyFormChanges(m_pageNumber);
0513 }
0514 
0515 bool EditFormListCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
0516 {
0517     m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], m_form));
0518 
0519     return m_form;
0520 }
0521 
0522 EditFormComboCommand::EditFormComboCommand(Okular::DocumentPrivate *docPriv, FormFieldChoice *form, int pageNumber, const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
0523     : EditTextCommand(newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos)
0524     , m_docPriv(docPriv)
0525     , m_form(form)
0526     , m_pageNumber(pageNumber)
0527     , m_newIndex(-1)
0528     , m_prevIndex(-1)
0529 {
0530     setText(i18nc("Edit a combo form's selection", "edit combo form selection"));
0531 
0532     // Determine new and previous choice indices (if any)
0533     for (int i = 0; i < m_form->choices().size(); i++) {
0534         if (m_form->choices().at(i) == m_prevContents) {
0535             m_prevIndex = i;
0536         }
0537 
0538         if (m_form->choices().at(i) == m_newContents) {
0539             m_newIndex = i;
0540         }
0541     }
0542 }
0543 
0544 void EditFormComboCommand::undo()
0545 {
0546     if (m_prevIndex != -1) {
0547         m_form->setCurrentChoices(QList<int>() << m_prevIndex);
0548     } else {
0549         m_form->setEditChoice(m_prevContents);
0550     }
0551     moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
0552     Q_EMIT m_docPriv->m_parent->formComboChangedByUndoRedo(m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos);
0553     m_docPriv->notifyFormChanges(m_pageNumber);
0554 }
0555 
0556 void EditFormComboCommand::redo()
0557 {
0558     if (m_newIndex != -1) {
0559         m_form->setCurrentChoices(QList<int>() << m_newIndex);
0560     } else {
0561         m_form->setEditChoice(m_newContents);
0562     }
0563     moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
0564     Q_EMIT m_docPriv->m_parent->formComboChangedByUndoRedo(m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos);
0565     m_docPriv->notifyFormChanges(m_pageNumber);
0566 }
0567 
0568 int EditFormComboCommand::id() const
0569 {
0570     return 4;
0571 }
0572 
0573 bool EditFormComboCommand::mergeWith(const QUndoCommand *uc)
0574 {
0575     EditFormComboCommand *euc = (EditFormComboCommand *)uc;
0576     // Only attempt merge of euc into this if they modify the same form
0577     if (m_form == euc->m_form) {
0578         bool shouldMerge = EditTextCommand::mergeWith(uc);
0579         if (shouldMerge) {
0580             m_newIndex = euc->m_newIndex;
0581         }
0582         return shouldMerge;
0583     } else {
0584         return false;
0585     }
0586 }
0587 
0588 bool EditFormComboCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
0589 {
0590     m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], m_form));
0591 
0592     return m_form;
0593 }
0594 
0595 EditFormButtonsCommand::EditFormButtonsCommand(Okular::DocumentPrivate *docPriv, int pageNumber, const QList<FormFieldButton *> &formButtons, const QList<bool> &newButtonStates)
0596     : m_docPriv(docPriv)
0597     , m_pageNumber(pageNumber)
0598     , m_formButtons(formButtons)
0599     , m_newButtonStates(newButtonStates)
0600     , m_prevButtonStates(QList<bool>())
0601 {
0602     setText(i18nc("Edit the state of a group of form buttons", "edit form button states"));
0603     for (const FormFieldButton *formButton : std::as_const(m_formButtons)) {
0604         m_prevButtonStates.append(formButton->state());
0605         m_pageNumbers.append(formButton->page()->number());
0606     }
0607 }
0608 
0609 void EditFormButtonsCommand::undo()
0610 {
0611     clearFormButtonStates();
0612     QSet<int> extraPages;
0613     for (int i = 0; i < m_formButtons.size(); i++) {
0614         bool checked = m_prevButtonStates.at(i);
0615         if (checked) {
0616             m_formButtons.at(i)->setState(checked);
0617         }
0618         if (m_pageNumbers.at(i) != m_pageNumber) {
0619             extraPages << m_pageNumbers.at(i);
0620         }
0621     }
0622 
0623     Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons(m_formButtons);
0624     moveViewportIfBoundingRectNotFullyVisible(boundingRect, m_docPriv, m_pageNumber);
0625     Q_EMIT m_docPriv->m_parent->formButtonsChangedByUndoRedo(m_pageNumber, m_formButtons);
0626     m_docPriv->notifyFormChanges(m_pageNumber);
0627     for (auto page : std::as_const(extraPages)) {
0628         m_docPriv->notifyFormChanges(page);
0629     }
0630 }
0631 
0632 void EditFormButtonsCommand::redo()
0633 {
0634     clearFormButtonStates();
0635     QSet<int> extraPages;
0636     for (int i = 0; i < m_formButtons.size(); i++) {
0637         bool checked = m_newButtonStates.at(i);
0638         if (checked) {
0639             m_formButtons.at(i)->setState(checked);
0640         }
0641         if (m_pageNumbers.at(i) != m_pageNumber) {
0642             extraPages << m_pageNumbers.at(i);
0643         }
0644     }
0645 
0646     Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons(m_formButtons);
0647     moveViewportIfBoundingRectNotFullyVisible(boundingRect, m_docPriv, m_pageNumber);
0648     Q_EMIT m_docPriv->m_parent->formButtonsChangedByUndoRedo(m_pageNumber, m_formButtons);
0649     m_docPriv->notifyFormChanges(m_pageNumber);
0650     for (auto page : std::as_const(extraPages)) {
0651         m_docPriv->notifyFormChanges(page);
0652     }
0653 }
0654 
0655 bool EditFormButtonsCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
0656 {
0657     const QList<FormFieldButton *> oldFormButtons = m_formButtons;
0658     m_formButtons.clear();
0659     for (int i = 0; i < oldFormButtons.size(); i++) {
0660         FormFieldButton *button = dynamic_cast<FormFieldButton *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumbers[i]], oldFormButtons[i]));
0661         if (!button) {
0662             return false;
0663         }
0664         m_formButtons << button;
0665     }
0666 
0667     return true;
0668 }
0669 
0670 void EditFormButtonsCommand::clearFormButtonStates()
0671 {
0672     for (FormFieldButton *formButton : std::as_const(m_formButtons)) {
0673         formButton->setState(false);
0674     }
0675 }
0676 
0677 }