1 /*
2 SPDX-FileCopyrightText: 2013 Jon Mease <jon.mease@gmail.com>
3
4 Work sponsored by the LiMux project of the city of Munich:
5 SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "documentcommands_p.h"
11
12 #include "annotations.h"
13 #include "debug_p.h"
14 #include "document_p.h"
15 #include "form.h"
16 #include "page.h"
17 #include "page_p.h"
18 #include "utils_p.h"
19
20 #include <KLocalizedString>
21
22 namespace Okular
23 {
moveViewportIfBoundingRectNotFullyVisible(Okular::NormalizedRect boundingRect,DocumentPrivate * docPriv,int pageNumber)24 void moveViewportIfBoundingRectNotFullyVisible(Okular::NormalizedRect boundingRect, DocumentPrivate *docPriv, int pageNumber)
25 {
26 const Rotation pageRotation = docPriv->m_parent->page(pageNumber)->rotation();
27 const QTransform rotationMatrix = Okular::buildRotationMatrix(pageRotation);
28 boundingRect.transform(rotationMatrix);
29 if (!docPriv->isNormalizedRectangleFullyVisible(boundingRect, pageNumber)) {
30 DocumentViewport searchViewport(pageNumber);
31 searchViewport.rePos.enabled = true;
32 searchViewport.rePos.normalizedX = (boundingRect.left + boundingRect.right) / 2.0;
33 searchViewport.rePos.normalizedY = (boundingRect.top + boundingRect.bottom) / 2.0;
34 docPriv->m_parent->setViewport(searchViewport, nullptr, true);
35 }
36 }
37
buildBoundingRectangleForButtons(const QList<Okular::FormFieldButton * > & formButtons)38 Okular::NormalizedRect buildBoundingRectangleForButtons(const QList<Okular::FormFieldButton *> &formButtons)
39 {
40 // Initialize coordinates of the bounding rect
41 double left = 1.0;
42 double top = 1.0;
43 double right = 0.0;
44 double bottom = 0.0;
45
46 for (const FormFieldButton *formButton : formButtons) {
47 left = qMin<double>(left, formButton->rect().left);
48 top = qMin<double>(top, formButton->rect().top);
49 right = qMax<double>(right, formButton->rect().right);
50 bottom = qMax<double>(bottom, formButton->rect().bottom);
51 }
52 Okular::NormalizedRect boundingRect(left, top, right, bottom);
53 return boundingRect;
54 }
55
AddAnnotationCommand(Okular::DocumentPrivate * docPriv,Okular::Annotation * annotation,int pageNumber)56 AddAnnotationCommand::AddAnnotationCommand(Okular::DocumentPrivate *docPriv, Okular::Annotation *annotation, int pageNumber)
57 : m_docPriv(docPriv)
58 , m_annotation(annotation)
59 , m_pageNumber(pageNumber)
60 , m_done(false)
61 {
62 setText(i18nc("Add an annotation to the page", "add annotation"));
63 }
64
~AddAnnotationCommand()65 AddAnnotationCommand::~AddAnnotationCommand()
66 {
67 if (!m_done) {
68 delete m_annotation;
69 }
70 }
71
undo()72 void AddAnnotationCommand::undo()
73 {
74 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
75 m_docPriv->performRemovePageAnnotation(m_pageNumber, m_annotation);
76 m_done = false;
77 }
78
redo()79 void AddAnnotationCommand::redo()
80 {
81 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
82 m_docPriv->performAddPageAnnotation(m_pageNumber, m_annotation);
83 m_done = true;
84 }
85
refreshInternalPageReferences(const QVector<Okular::Page * > & newPagesVector)86 bool AddAnnotationCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
87 {
88 if (m_done) {
89 // We don't always update m_annotation because even if the annotation has been added to the document
90 // it can have been removed later so the annotation pointer is stored inside a following RemoveAnnotationCommand
91 // and thus doesn't need updating because it didn't change
92 // because of the document reload
93 auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
94 if (a)
95 m_annotation = a;
96 }
97
98 return true;
99 }
100
RemoveAnnotationCommand(Okular::DocumentPrivate * doc,Okular::Annotation * annotation,int pageNumber)101 RemoveAnnotationCommand::RemoveAnnotationCommand(Okular::DocumentPrivate *doc, Okular::Annotation *annotation, int pageNumber)
102 : m_docPriv(doc)
103 , m_annotation(annotation)
104 , m_pageNumber(pageNumber)
105 , m_done(false)
106 {
107 setText(i18nc("Remove an annotation from the page", "remove annotation"));
108 }
109
~RemoveAnnotationCommand()110 RemoveAnnotationCommand::~RemoveAnnotationCommand()
111 {
112 if (m_done) {
113 delete m_annotation;
114 }
115 }
116
undo()117 void RemoveAnnotationCommand::undo()
118 {
119 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
120 m_docPriv->performAddPageAnnotation(m_pageNumber, m_annotation);
121 m_done = false;
122 }
123
redo()124 void RemoveAnnotationCommand::redo()
125 {
126 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
127 m_docPriv->performRemovePageAnnotation(m_pageNumber, m_annotation);
128 m_done = true;
129 }
130
refreshInternalPageReferences(const QVector<Okular::Page * > & newPagesVector)131 bool RemoveAnnotationCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
132 {
133 if (!m_done) {
134 // We don't always update m_annotation because it can happen that the annotation remove has been undo
135 // and that annotation addition has also been undone so the annotation pointer is stored inside
136 // a previous AddAnnotationCommand and thus doesn't need updating because it didn't change
137 // because of the document reload
138 auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
139 if (a)
140 m_annotation = a;
141 }
142
143 return true;
144 }
145
ModifyAnnotationPropertiesCommand(DocumentPrivate * docPriv,Annotation * annotation,int pageNumber,const QDomNode & oldProperties,const QDomNode & newProperties)146 ModifyAnnotationPropertiesCommand::ModifyAnnotationPropertiesCommand(DocumentPrivate *docPriv, Annotation *annotation, int pageNumber, const QDomNode &oldProperties, const QDomNode &newProperties)
147 : m_docPriv(docPriv)
148 , m_annotation(annotation)
149 , m_pageNumber(pageNumber)
150 , m_prevProperties(oldProperties)
151 , m_newProperties(newProperties)
152 {
153 setText(i18nc("Modify an annotation's internal properties (Color, line-width, etc.)", "modify annotation properties"));
154 }
155
undo()156 void ModifyAnnotationPropertiesCommand::undo()
157 {
158 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
159 m_annotation->setAnnotationProperties(m_prevProperties);
160 m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
161 }
162
redo()163 void ModifyAnnotationPropertiesCommand::redo()
164 {
165 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
166 m_annotation->setAnnotationProperties(m_newProperties);
167 m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
168 }
169
refreshInternalPageReferences(const QVector<Okular::Page * > & newPagesVector)170 bool ModifyAnnotationPropertiesCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
171 {
172 // Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
173 auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
174 if (a)
175 m_annotation = a;
176
177 return true;
178 }
179
TranslateAnnotationCommand(DocumentPrivate * docPriv,Annotation * annotation,int pageNumber,const Okular::NormalizedPoint & delta,bool completeDrag)180 TranslateAnnotationCommand::TranslateAnnotationCommand(DocumentPrivate *docPriv, Annotation *annotation, int pageNumber, const Okular::NormalizedPoint &delta, bool completeDrag)
181 : m_docPriv(docPriv)
182 , m_annotation(annotation)
183 , m_pageNumber(pageNumber)
184 , m_delta(delta)
185 , m_completeDrag(completeDrag)
186 {
187 setText(i18nc("Translate an annotation's position on the page", "translate annotation"));
188 }
189
undo()190 void TranslateAnnotationCommand::undo()
191 {
192 moveViewportIfBoundingRectNotFullyVisible(translateBoundingRectangle(minusDelta()), m_docPriv, m_pageNumber);
193 m_annotation->translate(minusDelta());
194 m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
195 }
196
redo()197 void TranslateAnnotationCommand::redo()
198 {
199 moveViewportIfBoundingRectNotFullyVisible(translateBoundingRectangle(m_delta), m_docPriv, m_pageNumber);
200 m_annotation->translate(m_delta);
201 m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
202 }
203
id() const204 int TranslateAnnotationCommand::id() const
205 {
206 return 1;
207 }
208
mergeWith(const QUndoCommand * uc)209 bool TranslateAnnotationCommand::mergeWith(const QUndoCommand *uc)
210 {
211 TranslateAnnotationCommand *tuc = (TranslateAnnotationCommand *)uc;
212
213 if (tuc->m_annotation != m_annotation)
214 return false;
215
216 if (m_completeDrag) {
217 return false;
218 }
219 m_delta = Okular::NormalizedPoint(tuc->m_delta.x + m_delta.x, tuc->m_delta.y + m_delta.y);
220 m_completeDrag = tuc->m_completeDrag;
221 return true;
222 }
223
minusDelta()224 Okular::NormalizedPoint TranslateAnnotationCommand::minusDelta()
225 {
226 return Okular::NormalizedPoint(-m_delta.x, -m_delta.y);
227 }
228
translateBoundingRectangle(const Okular::NormalizedPoint & delta)229 Okular::NormalizedRect TranslateAnnotationCommand::translateBoundingRectangle(const Okular::NormalizedPoint &delta)
230 {
231 Okular::NormalizedRect annotBoundingRect = m_annotation->boundingRectangle();
232 double left = qMin<double>(annotBoundingRect.left, annotBoundingRect.left + delta.x);
233 double right = qMax<double>(annotBoundingRect.right, annotBoundingRect.right + delta.x);
234 double top = qMin<double>(annotBoundingRect.top, annotBoundingRect.top + delta.y);
235 double bottom = qMax<double>(annotBoundingRect.bottom, annotBoundingRect.bottom + delta.y);
236 Okular::NormalizedRect boundingRect(left, top, right, bottom);
237 return boundingRect;
238 }
239
refreshInternalPageReferences(const QVector<Page * > & newPagesVector)240 bool TranslateAnnotationCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
241 {
242 // Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
243 auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
244 if (a)
245 m_annotation = a;
246
247 return true;
248 }
249
AdjustAnnotationCommand(Okular::DocumentPrivate * docPriv,Okular::Annotation * annotation,int pageNumber,const Okular::NormalizedPoint & delta1,const Okular::NormalizedPoint & delta2,bool completeDrag)250 AdjustAnnotationCommand::AdjustAnnotationCommand(Okular::DocumentPrivate *docPriv, Okular::Annotation *annotation, int pageNumber, const Okular::NormalizedPoint &delta1, const Okular::NormalizedPoint &delta2, bool completeDrag)
251 : m_docPriv(docPriv)
252 , m_annotation(annotation)
253 , m_pageNumber(pageNumber)
254 , m_delta1(delta1)
255 , m_delta2(delta2)
256 , m_completeDrag(completeDrag)
257 {
258 setText(i18nc("Change an annotation's size", "adjust annotation"));
259 }
260
undo()261 void AdjustAnnotationCommand::undo()
262 {
263 const NormalizedPoint minusDelta1 = Okular::NormalizedPoint(-m_delta1.x, -m_delta1.y);
264 const NormalizedPoint minusDelta2 = Okular::NormalizedPoint(-m_delta2.x, -m_delta2.y);
265 moveViewportIfBoundingRectNotFullyVisible(adjustBoundingRectangle(minusDelta1, minusDelta2), m_docPriv, m_pageNumber);
266 m_annotation->adjust(minusDelta1, minusDelta2);
267 m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
268 }
269
redo()270 void AdjustAnnotationCommand::redo()
271 {
272 moveViewportIfBoundingRectNotFullyVisible(adjustBoundingRectangle(m_delta1, m_delta2), m_docPriv, m_pageNumber);
273 m_annotation->adjust(m_delta1, m_delta2);
274 m_docPriv->performModifyPageAnnotation(m_pageNumber, m_annotation, true);
275 }
276
id() const277 int AdjustAnnotationCommand::id() const
278 {
279 return 5;
280 }
281
mergeWith(const QUndoCommand * uc)282 bool AdjustAnnotationCommand::mergeWith(const QUndoCommand *uc)
283 {
284 AdjustAnnotationCommand *tuc = (AdjustAnnotationCommand *)uc;
285
286 if (tuc->m_annotation != m_annotation)
287 return false;
288
289 if (m_completeDrag) {
290 return false;
291 }
292 m_delta1 = Okular::NormalizedPoint(tuc->m_delta1.x + m_delta1.x, tuc->m_delta1.y + m_delta1.y);
293 m_delta2 = Okular::NormalizedPoint(tuc->m_delta2.x + m_delta2.x, tuc->m_delta2.y + m_delta2.y);
294 m_completeDrag = tuc->m_completeDrag;
295 return true;
296 }
297
adjustBoundingRectangle(const Okular::NormalizedPoint & delta1,const Okular::NormalizedPoint & delta2)298 Okular::NormalizedRect AdjustAnnotationCommand::adjustBoundingRectangle(const Okular::NormalizedPoint &delta1, const Okular::NormalizedPoint &delta2)
299 {
300 const Okular::NormalizedRect annotBoundingRect = m_annotation->boundingRectangle();
301 const double left = qMin<double>(annotBoundingRect.left, annotBoundingRect.left + delta1.x);
302 const double right = qMax<double>(annotBoundingRect.right, annotBoundingRect.right + delta2.x);
303 const double top = qMin<double>(annotBoundingRect.top, annotBoundingRect.top + delta1.y);
304 const double bottom = qMax<double>(annotBoundingRect.bottom, annotBoundingRect.bottom + delta2.y);
305 return Okular::NormalizedRect(left, top, right, bottom);
306 }
307
refreshInternalPageReferences(const QVector<Page * > & newPagesVector)308 bool AdjustAnnotationCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
309 {
310 // Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
311 auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
312 if (a)
313 m_annotation = a;
314
315 return true;
316 }
317
EditTextCommand(const QString & newContents,int newCursorPos,const QString & prevContents,int prevCursorPos,int prevAnchorPos)318 EditTextCommand::EditTextCommand(const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
319 : m_newContents(newContents)
320 , m_newCursorPos(newCursorPos)
321 , m_prevContents(prevContents)
322 , m_prevCursorPos(prevCursorPos)
323 , m_prevAnchorPos(prevAnchorPos)
324 {
325 setText(i18nc("Generic text edit command", "edit text"));
326
327 //// Determine edit type
328 // If There was a selection then edit was not a simple single character backspace, delete, or insert
329 if (m_prevCursorPos != m_prevAnchorPos) {
330 qCDebug(OkularCoreDebug) << "OtherEdit, selection";
331 m_editType = OtherEdit;
332 } else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor() == oldContentsLeftOfCursor().left(oldContentsLeftOfCursor().length() - 1) && oldContentsLeftOfCursor().rightRef(1) != "\n") {
333 qCDebug(OkularCoreDebug) << "CharBackspace";
334 m_editType = CharBackspace;
335 } else if (newContentsLeftOfCursor() == oldContentsLeftOfCursor() && newContentsRightOfCursor() == oldContentsRightOfCursor().right(oldContentsRightOfCursor().length() - 1) && oldContentsRightOfCursor().leftRef(1) != "\n") {
336 qCDebug(OkularCoreDebug) << "CharDelete";
337 m_editType = CharDelete;
338 } else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor().left(newContentsLeftOfCursor().length() - 1) == oldContentsLeftOfCursor() && newContentsLeftOfCursor().rightRef(1) != "\n") {
339 qCDebug(OkularCoreDebug) << "CharInsert";
340 m_editType = CharInsert;
341 } else {
342 qCDebug(OkularCoreDebug) << "OtherEdit";
343 m_editType = OtherEdit;
344 }
345 }
346
mergeWith(const QUndoCommand * uc)347 bool EditTextCommand::mergeWith(const QUndoCommand *uc)
348 {
349 EditTextCommand *euc = (EditTextCommand *)uc;
350
351 // Only attempt merge of euc into this if our new state matches euc's old state and
352 // the editTypes match and are not type OtherEdit
353 if (m_newContents == euc->m_prevContents && m_newCursorPos == euc->m_prevCursorPos && m_editType == euc->m_editType && m_editType != OtherEdit) {
354 m_newContents = euc->m_newContents;
355 m_newCursorPos = euc->m_newCursorPos;
356 return true;
357 }
358 return false;
359 }
360
oldContentsLeftOfCursor()361 QString EditTextCommand::oldContentsLeftOfCursor()
362 {
363 return m_prevContents.left(m_prevCursorPos);
364 }
365
oldContentsRightOfCursor()366 QString EditTextCommand::oldContentsRightOfCursor()
367 {
368 return m_prevContents.right(m_prevContents.length() - m_prevCursorPos);
369 }
370
newContentsLeftOfCursor()371 QString EditTextCommand::newContentsLeftOfCursor()
372 {
373 return m_newContents.left(m_newCursorPos);
374 }
375
newContentsRightOfCursor()376 QString EditTextCommand::newContentsRightOfCursor()
377 {
378 return m_newContents.right(m_newContents.length() - m_newCursorPos);
379 }
380
EditAnnotationContentsCommand(DocumentPrivate * docPriv,Annotation * annotation,int pageNumber,const QString & newContents,int newCursorPos,const QString & prevContents,int prevCursorPos,int prevAnchorPos)381 EditAnnotationContentsCommand::EditAnnotationContentsCommand(DocumentPrivate *docPriv, Annotation *annotation, int pageNumber, const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
382 : EditTextCommand(newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos)
383 , m_docPriv(docPriv)
384 , m_annotation(annotation)
385 , m_pageNumber(pageNumber)
386 {
387 setText(i18nc("Edit an annotation's text contents", "edit annotation contents"));
388 }
389
undo()390 void EditAnnotationContentsCommand::undo()
391 {
392 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
393 m_docPriv->performSetAnnotationContents(m_prevContents, m_annotation, m_pageNumber);
394 emit m_docPriv->m_parent->annotationContentsChangedByUndoRedo(m_annotation, m_prevContents, m_prevCursorPos, m_prevAnchorPos);
395 }
396
redo()397 void EditAnnotationContentsCommand::redo()
398 {
399 moveViewportIfBoundingRectNotFullyVisible(m_annotation->boundingRectangle(), m_docPriv, m_pageNumber);
400 m_docPriv->performSetAnnotationContents(m_newContents, m_annotation, m_pageNumber);
401 emit m_docPriv->m_parent->annotationContentsChangedByUndoRedo(m_annotation, m_newContents, m_newCursorPos, m_newCursorPos);
402 }
403
id() const404 int EditAnnotationContentsCommand::id() const
405 {
406 return 2;
407 }
408
mergeWith(const QUndoCommand * uc)409 bool EditAnnotationContentsCommand::mergeWith(const QUndoCommand *uc)
410 {
411 EditAnnotationContentsCommand *euc = (EditAnnotationContentsCommand *)uc;
412 // Only attempt merge of euc into this if they modify the same annotation
413 if (m_annotation == euc->m_annotation) {
414 return EditTextCommand::mergeWith(uc);
415 } else {
416 return false;
417 }
418 }
419
refreshInternalPageReferences(const QVector<Page * > & newPagesVector)420 bool EditAnnotationContentsCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
421 {
422 auto a = newPagesVector[m_pageNumber]->annotation(m_annotation->uniqueName());
423 if (a)
424 m_annotation = a;
425
426 return true;
427 }
428
EditFormTextCommand(Okular::DocumentPrivate * docPriv,Okular::FormFieldText * form,int pageNumber,const QString & newContents,int newCursorPos,const QString & prevContents,int prevCursorPos,int prevAnchorPos)429 EditFormTextCommand::EditFormTextCommand(Okular::DocumentPrivate *docPriv, Okular::FormFieldText *form, int pageNumber, const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
430 : EditTextCommand(newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos)
431 , m_docPriv(docPriv)
432 , m_form(form)
433 , m_pageNumber(pageNumber)
434 {
435 setText(i18nc("Edit an form's text contents", "edit form contents"));
436 }
437
undo()438 void EditFormTextCommand::undo()
439 {
440 moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
441 m_form->setText(m_prevContents);
442 emit m_docPriv->m_parent->formTextChangedByUndoRedo(m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos);
443 m_docPriv->notifyFormChanges(m_pageNumber);
444 }
445
redo()446 void EditFormTextCommand::redo()
447 {
448 moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
449 m_form->setText(m_newContents);
450 emit m_docPriv->m_parent->formTextChangedByUndoRedo(m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos);
451 m_docPriv->notifyFormChanges(m_pageNumber);
452 }
453
id() const454 int EditFormTextCommand::id() const
455 {
456 return 3;
457 }
458
mergeWith(const QUndoCommand * uc)459 bool EditFormTextCommand::mergeWith(const QUndoCommand *uc)
460 {
461 EditFormTextCommand *euc = (EditFormTextCommand *)uc;
462 // Only attempt merge of euc into this if they modify the same form
463 if (m_form == euc->m_form) {
464 return EditTextCommand::mergeWith(uc);
465 } else {
466 return false;
467 }
468 }
469
refreshInternalPageReferences(const QVector<Page * > & newPagesVector)470 bool EditFormTextCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
471 {
472 m_form = dynamic_cast<FormFieldText *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], m_form));
473
474 return m_form;
475 }
476
EditFormListCommand(Okular::DocumentPrivate * docPriv,FormFieldChoice * form,int pageNumber,const QList<int> & newChoices,const QList<int> & prevChoices)477 EditFormListCommand::EditFormListCommand(Okular::DocumentPrivate *docPriv, FormFieldChoice *form, int pageNumber, const QList<int> &newChoices, const QList<int> &prevChoices)
478 : m_docPriv(docPriv)
479 , m_form(form)
480 , m_pageNumber(pageNumber)
481 , m_newChoices(newChoices)
482 , m_prevChoices(prevChoices)
483 {
484 setText(i18nc("Edit a list form's choices", "edit list form choices"));
485 }
486
undo()487 void EditFormListCommand::undo()
488 {
489 moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
490 m_form->setCurrentChoices(m_prevChoices);
491 emit m_docPriv->m_parent->formListChangedByUndoRedo(m_pageNumber, m_form, m_prevChoices);
492 m_docPriv->notifyFormChanges(m_pageNumber);
493 }
494
redo()495 void EditFormListCommand::redo()
496 {
497 moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
498 m_form->setCurrentChoices(m_newChoices);
499 emit m_docPriv->m_parent->formListChangedByUndoRedo(m_pageNumber, m_form, m_newChoices);
500 m_docPriv->notifyFormChanges(m_pageNumber);
501 }
502
refreshInternalPageReferences(const QVector<Page * > & newPagesVector)503 bool EditFormListCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
504 {
505 m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], m_form));
506
507 return m_form;
508 }
509
EditFormComboCommand(Okular::DocumentPrivate * docPriv,FormFieldChoice * form,int pageNumber,const QString & newContents,int newCursorPos,const QString & prevContents,int prevCursorPos,int prevAnchorPos)510 EditFormComboCommand::EditFormComboCommand(Okular::DocumentPrivate *docPriv, FormFieldChoice *form, int pageNumber, const QString &newContents, int newCursorPos, const QString &prevContents, int prevCursorPos, int prevAnchorPos)
511 : EditTextCommand(newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos)
512 , m_docPriv(docPriv)
513 , m_form(form)
514 , m_pageNumber(pageNumber)
515 , m_newIndex(-1)
516 , m_prevIndex(-1)
517 {
518 setText(i18nc("Edit a combo form's selection", "edit combo form selection"));
519
520 // Determine new and previous choice indices (if any)
521 for (int i = 0; i < m_form->choices().size(); i++) {
522 if (m_form->choices().at(i) == m_prevContents) {
523 m_prevIndex = i;
524 }
525
526 if (m_form->choices().at(i) == m_newContents) {
527 m_newIndex = i;
528 }
529 }
530 }
531
undo()532 void EditFormComboCommand::undo()
533 {
534 if (m_prevIndex != -1) {
535 m_form->setCurrentChoices(QList<int>() << m_prevIndex);
536 } else {
537 m_form->setEditChoice(m_prevContents);
538 }
539 moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
540 emit m_docPriv->m_parent->formComboChangedByUndoRedo(m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos);
541 m_docPriv->notifyFormChanges(m_pageNumber);
542 }
543
redo()544 void EditFormComboCommand::redo()
545 {
546 if (m_newIndex != -1) {
547 m_form->setCurrentChoices(QList<int>() << m_newIndex);
548 } else {
549 m_form->setEditChoice(m_newContents);
550 }
551 moveViewportIfBoundingRectNotFullyVisible(m_form->rect(), m_docPriv, m_pageNumber);
552 emit m_docPriv->m_parent->formComboChangedByUndoRedo(m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos);
553 m_docPriv->notifyFormChanges(m_pageNumber);
554 }
555
id() const556 int EditFormComboCommand::id() const
557 {
558 return 4;
559 }
560
mergeWith(const QUndoCommand * uc)561 bool EditFormComboCommand::mergeWith(const QUndoCommand *uc)
562 {
563 EditFormComboCommand *euc = (EditFormComboCommand *)uc;
564 // Only attempt merge of euc into this if they modify the same form
565 if (m_form == euc->m_form) {
566 bool shouldMerge = EditTextCommand::mergeWith(uc);
567 if (shouldMerge) {
568 m_newIndex = euc->m_newIndex;
569 }
570 return shouldMerge;
571 } else {
572 return false;
573 }
574 }
575
refreshInternalPageReferences(const QVector<Page * > & newPagesVector)576 bool EditFormComboCommand::refreshInternalPageReferences(const QVector<Page *> &newPagesVector)
577 {
578 m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], m_form));
579
580 return m_form;
581 }
582
EditFormButtonsCommand(Okular::DocumentPrivate * docPriv,int pageNumber,const QList<FormFieldButton * > & formButtons,const QList<bool> & newButtonStates)583 EditFormButtonsCommand::EditFormButtonsCommand(Okular::DocumentPrivate *docPriv, int pageNumber, const QList<FormFieldButton *> &formButtons, const QList<bool> &newButtonStates)
584 : m_docPriv(docPriv)
585 , m_pageNumber(pageNumber)
586 , m_formButtons(formButtons)
587 , m_newButtonStates(newButtonStates)
588 , m_prevButtonStates(QList<bool>())
589 {
590 setText(i18nc("Edit the state of a group of form buttons", "edit form button states"));
591 for (const FormFieldButton *formButton : qAsConst(m_formButtons)) {
592 m_prevButtonStates.append(formButton->state());
593 }
594 }
595
undo()596 void EditFormButtonsCommand::undo()
597 {
598 clearFormButtonStates();
599 for (int i = 0; i < m_formButtons.size(); i++) {
600 bool checked = m_prevButtonStates.at(i);
601 if (checked)
602 m_formButtons.at(i)->setState(checked);
603 }
604
605 Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons(m_formButtons);
606 moveViewportIfBoundingRectNotFullyVisible(boundingRect, m_docPriv, m_pageNumber);
607 emit m_docPriv->m_parent->formButtonsChangedByUndoRedo(m_pageNumber, m_formButtons);
608 m_docPriv->notifyFormChanges(m_pageNumber);
609 }
610
redo()611 void EditFormButtonsCommand::redo()
612 {
613 clearFormButtonStates();
614 for (int i = 0; i < m_formButtons.size(); i++) {
615 bool checked = m_newButtonStates.at(i);
616 if (checked)
617 m_formButtons.at(i)->setState(checked);
618 }
619
620 Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons(m_formButtons);
621 moveViewportIfBoundingRectNotFullyVisible(boundingRect, m_docPriv, m_pageNumber);
622 emit m_docPriv->m_parent->formButtonsChangedByUndoRedo(m_pageNumber, m_formButtons);
623 m_docPriv->notifyFormChanges(m_pageNumber);
624 }
625
refreshInternalPageReferences(const QVector<Okular::Page * > & newPagesVector)626 bool EditFormButtonsCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
627 {
628 const QList<FormFieldButton *> oldFormButtons = m_formButtons;
629 m_formButtons.clear();
630 for (FormFieldButton *oldFormButton : oldFormButtons) {
631 FormFieldButton *button = dynamic_cast<FormFieldButton *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], oldFormButton));
632 if (!button)
633 return false;
634 m_formButtons << button;
635 }
636
637 return true;
638 }
639
clearFormButtonStates()640 void EditFormButtonsCommand::clearFormButtonStates()
641 {
642 for (FormFieldButton *formButton : qAsConst(m_formButtons)) {
643 formButton->setState(false);
644 }
645 }
646
647 }
648