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 }