5#include <QtCore/qdebug.h>
7#if QT_CONFIG(undogroup)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
68
69
70
71
72
73
74
75
77QUndoCommand::QUndoCommand(
const QString &text, QUndoCommand *parent)
78 : QUndoCommand(parent)
84
85
86
87
88
89
90
91
93QUndoCommand::QUndoCommand(QUndoCommand *parent)
95 d =
new QUndoCommandPrivate;
96 if (parent !=
nullptr)
97 parent->d->child_list.append(
this);
101
102
103
104
106QUndoCommand::~QUndoCommand()
108 qDeleteAll(d->child_list);
113
114
115
116
117
118
119
120
121
122
124bool QUndoCommand::isObsolete()
const
130
131
132
133
134
135
137void QUndoCommand::setObsolete(
bool obsolete)
139 d->obsolete = obsolete;
143
144
145
146
147
148
149
150
151
152
153
154
155
157int QUndoCommand::id()
const
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
181bool QUndoCommand::mergeWith(
const QUndoCommand *command)
188
189
190
191
192
193
194
195
196
198void QUndoCommand::redo()
200 for (
int i = 0; i < d->child_list.size(); ++i)
201 d->child_list.at(i)->redo();
205
206
207
208
209
210
211
212
213
214
216void QUndoCommand::undo()
218 for (
int i = d->child_list.size() - 1; i >= 0; --i)
219 d->child_list.at(i)->undo();
223
224
225
226
227
228
229
231QString QUndoCommand::text()
const
237
238
239
240
241
242
243
244
245
246
248QString QUndoCommand::actionText()
const
250 return d->actionText;
254
255
256
257
258
259
260
261
262
263
264
265
266
268void QUndoCommand::setText(
const QString &text)
270 int cdpos = text.indexOf(u'\n');
272 d->text = text.left(cdpos);
273 d->actionText = text.mid(cdpos + 1);
276 d->actionText = text;
281
282
283
284
285
286
288int QUndoCommand::childCount()
const
290 return d->child_list.size();
294
295
296
297
298
299
301const QUndoCommand *QUndoCommand::child(
int index)
const
303 if (index < 0 || index >= d->child_list.size())
305 return d->child_list.at(index);
308#if QT_CONFIG(undostack)
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
412
413
414
416void QUndoStackPrivate::setIndex(
int idx,
bool clean)
420 bool was_clean = index == clean_index;
422 const bool indexChanged = idx != index;
425 emit q->indexChanged(index);
428 const ActionState newUndoState{q->canUndo(), q->undoText()};
429 if (indexChanged || newUndoState != undoActionState) {
430 undoActionState = newUndoState;
431 emit q->canUndoChanged(undoActionState.enabled);
432 emit q->undoTextChanged(undoActionState.text);
435 const ActionState newRedoState{q->canRedo(), q->redoText()};
436 if (indexChanged || newRedoState != redoActionState) {
437 redoActionState = newRedoState;
438 emit q->canRedoChanged(redoActionState.enabled);
439 emit q->redoTextChanged(redoActionState.text);
445 bool is_clean = index == clean_index;
446 if (is_clean != was_clean)
447 emit q->cleanChanged(is_clean);
451
452
453
454
455
457bool QUndoStackPrivate::checkUndoLimit()
459 if (undo_limit <= 0 || !macro_stack.isEmpty() || undo_limit >= command_list.size())
462 int del_count = command_list.size() - undo_limit;
464 for (
int i = 0; i < del_count; ++i)
465 delete command_list.takeFirst();
468 if (clean_index != -1) {
469 if (clean_index < del_count)
472 clean_index -= del_count;
479
480
481
482
483
484
486QUndoStack::QUndoStack(QObject *parent)
487 : QObject(*(
new QUndoStackPrivate), parent)
489#if QT_CONFIG(undogroup)
490 if (QUndoGroup *group = qobject_cast<QUndoGroup*>(parent))
491 group->addStack(
this);
496
497
498
499
500
502QUndoStack::~QUndoStack()
504#if QT_CONFIG(undogroup)
506 if (d->group !=
nullptr)
507 d->group->removeStack(
this);
513
514
515
516
517
518
519
520
521
522
523
525void QUndoStack::clear()
529 if (d->command_list.isEmpty())
532 bool was_clean = isClean();
534 d->macro_stack.clear();
535 qDeleteAll(d->command_list);
536 d->command_list.clear();
541 emit indexChanged(0);
542 emit canUndoChanged(
false);
543 emit undoTextChanged(QString());
544 emit canRedoChanged(
false);
545 emit redoTextChanged(QString());
548 emit cleanChanged(
true);
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
579void QUndoStack::push(QUndoCommand *cmd)
582 if (!cmd->isObsolete())
585 bool macro = !d->macro_stack.isEmpty();
587 QUndoCommand *cur =
nullptr;
589 QUndoCommand *macro_cmd = d->macro_stack.constLast();
590 if (!macro_cmd->d->child_list.isEmpty())
591 cur = macro_cmd->d->child_list.constLast();
594 cur = d->command_list.at(d->index - 1);
595 while (d->index < d->command_list.size())
596 delete d->command_list.takeLast();
597 if (d->clean_index > d->index)
601 bool try_merge = cur !=
nullptr
603 && cur->id() == cmd->id()
604 && (macro || d->index != d->clean_index);
606 if (try_merge && cur->mergeWith(cmd)) {
610 if (cur->isObsolete())
611 delete d->macro_stack.constLast()->d->child_list.takeLast();
613 if (cur->isObsolete()) {
614 delete d->command_list.takeLast();
616 d->setIndex(d->index - 1,
false);
618 emit indexChanged(d->index);
619 emit canUndoChanged(canUndo());
620 emit undoTextChanged(undoText());
621 emit canRedoChanged(canRedo());
622 emit redoTextChanged(redoText());
625 }
else if (cmd->isObsolete()) {
629 d->macro_stack.constLast()->d->child_list.append(cmd);
631 d->command_list.append(cmd);
633 d->setIndex(d->index + 1,
false);
639
640
641
642
643
644
645
646
647
648
649
651void QUndoStack::setClean()
654 if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
655 qWarning(
"QUndoStack::setClean(): cannot set clean in the middle of a macro");
659 d->setIndex(d->index,
true);
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
679void QUndoStack::resetClean()
682 const bool was_clean = isClean();
685 emit cleanChanged(
false);
689
690
691
692
693
694
695
696
697
700
701
702
703
705bool QUndoStack::isClean()
const
707 Q_D(
const QUndoStack);
708 if (!d->macro_stack.isEmpty())
710 return d->clean_index == d->index;
714
715
716
717
718
719
720
721
722
723
725int QUndoStack::cleanIndex()
const
727 Q_D(
const QUndoStack);
728 return d->clean_index;
732
733
734
735
736
737
738
739
740
741
742
743
744
746void QUndoStack::undo()
752 if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
753 qWarning(
"QUndoStack::undo(): cannot undo in the middle of a macro");
757 int idx = d->index - 1;
758 QUndoCommand *cmd = d->command_list.at(idx);
760 if (!cmd->isObsolete())
763 if (cmd->isObsolete()) {
764 delete d->command_list.takeAt(idx);
766 if (d->clean_index > idx)
770 d->setIndex(idx,
false);
774
775
776
777
778
779
780
781
782
783
784
785
786
788void QUndoStack::redo()
791 if (d->index == d->command_list.size())
794 if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
795 qWarning(
"QUndoStack::redo(): cannot redo in the middle of a macro");
800 QUndoCommand *cmd = d->command_list.at(idx);
802 if (!cmd->isObsolete())
805 if (cmd->isObsolete()) {
806 delete d->command_list.takeAt(idx);
808 if (d->clean_index > idx)
811 d->setIndex(idx,
false);
813 d->setIndex(d->index + 1,
false);
818
819
820
821
822
824int QUndoStack::count()
const
826 Q_D(
const QUndoStack);
827 return d->command_list.size();
831
832
833
834
835
836
838int QUndoStack::index()
const
840 Q_D(
const QUndoStack);
845
846
847
848
849
850
852void QUndoStack::setIndex(
int idx)
855 if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
856 qWarning(
"QUndoStack::setIndex(): cannot set index in the middle of a macro");
862 else if (idx > d->command_list.size())
863 idx = d->command_list.size();
867 QUndoCommand *cmd = d->command_list.at(i);
869 if (!cmd->isObsolete())
872 if (cmd->isObsolete()) {
873 delete d->command_list.takeAt(i);
875 if (d->clean_index > i)
885 QUndoCommand *cmd = d->command_list.at(--i);
888 if (cmd->isObsolete()) {
889 delete d->command_list.takeAt(i);
891 if (d->clean_index > i)
896 d->setIndex(idx,
false);
900
901
902
903
904
905
906
907
908
911
912
913
914
915
916
917
918
919
921bool QUndoStack::canUndo()
const
923 Q_D(
const QUndoStack);
924 if (!d->macro_stack.isEmpty())
930
931
932
933
934
935
936
937
938
941
942
943
944
945
946
947
948
949
951bool QUndoStack::canRedo()
const
953 Q_D(
const QUndoStack);
954 if (!d->macro_stack.isEmpty())
956 return d->index < d->command_list.size();
960
961
962
963
964
965
966
967
968
971
972
973
974
976QString QUndoStack::undoText()
const
978 Q_D(
const QUndoStack);
979 if (!d->macro_stack.isEmpty())
982 return d->command_list.at(d->index - 1)->actionText();
987
988
989
990
991
992
993
994
995
998
999
1000
1001
1003QString QUndoStack::redoText()
const
1005 Q_D(
const QUndoStack);
1006 if (!d->macro_stack.isEmpty())
1008 if (d->index < d->command_list.size())
1009 return d->command_list.at(d->index)->actionText();
1016
1017
1018
1019
1020void QUndoStackPrivate::setPrefixedText(QAction *action,
const QString &prefix,
const QString &defaultText,
const QString &text)
1022 if (defaultText.isEmpty()) {
1024 if (!prefix.isEmpty() && !text.isEmpty())
1030 action->setText(defaultText);
1032 action->setText(prefix.arg(text));
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1050QAction *QUndoStack::createUndoAction(QObject *parent,
const QString &prefix)
const
1052 QAction *action =
new QAction(parent);
1053 action->setEnabled(canUndo());
1055 QString effectivePrefix = prefix;
1056 QString defaultText;
1057 if (prefix.isEmpty()) {
1058 effectivePrefix = tr(
"Undo %1");
1059 defaultText = tr(
"Undo",
"Default text for undo action");
1062 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, undoText());
1064 connect(
this, &QUndoStack::canUndoChanged, action, &QAction::setEnabled);
1065 connect(
this, &QUndoStack::undoTextChanged, action, [=](
const QString &text) {
1066 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
1068 connect(action, &QAction::triggered,
this, &QUndoStack::undo);
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1087QAction *QUndoStack::createRedoAction(QObject *parent,
const QString &prefix)
const
1089 QAction *action =
new QAction(parent);
1090 action->setEnabled(canRedo());
1092 QString effectivePrefix = prefix;
1093 QString defaultText;
1094 if (prefix.isEmpty()) {
1095 effectivePrefix = tr(
"Redo %1");
1096 defaultText = tr(
"Redo",
"Default text for redo action");
1099 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, redoText());
1101 connect(
this, &QUndoStack::canRedoChanged, action, &QAction::setEnabled);
1102 connect(
this, &QUndoStack::redoTextChanged, action, [=](
const QString &text) {
1103 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
1105 connect(action, &QAction::triggered,
this, &QUndoStack::redo);
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1142void QUndoStack::beginMacro(
const QString &text)
1145 QUndoCommand *cmd =
new QUndoCommand();
1148 if (d->macro_stack.isEmpty()) {
1149 while (d->index < d->command_list.size())
1150 delete d->command_list.takeLast();
1151 if (d->clean_index > d->index)
1152 d->clean_index = -1;
1153 d->command_list.append(cmd);
1155 d->macro_stack.constLast()->d->child_list.append(cmd);
1157 d->macro_stack.append(cmd);
1159 if (d->macro_stack.size() == 1) {
1160 emit canUndoChanged(
false);
1161 emit undoTextChanged(QString());
1162 emit canRedoChanged(
false);
1163 emit redoTextChanged(QString());
1168
1169
1170
1171
1172
1173
1174
1176void QUndoStack::endMacro()
1179 if (Q_UNLIKELY(d->macro_stack.isEmpty())) {
1180 qWarning(
"QUndoStack::endMacro(): no matching beginMacro()");
1184 d->macro_stack.removeLast();
1186 if (d->macro_stack.isEmpty()) {
1187 d->checkUndoLimit();
1188 d->setIndex(d->index + 1,
false);
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204const QUndoCommand *QUndoStack::command(
int index)
const
1206 Q_D(
const QUndoStack);
1208 if (index < 0 || index >= d->command_list.size())
1210 return d->command_list.at(index);
1214
1215
1216
1217
1219QString QUndoStack::text(
int idx)
const
1221 Q_D(
const QUndoStack);
1223 if (idx < 0 || idx >= d->command_list.size())
1225 return d->command_list.at(idx)->text();
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1243void QUndoStack::setUndoLimit(
int limit)
1247 if (Q_UNLIKELY(!d->command_list.isEmpty())) {
1248 qWarning(
"QUndoStack::setUndoLimit(): an undo limit can only be set when the stack is empty");
1252 if (limit == d->undo_limit)
1254 d->undo_limit = limit;
1255 d->checkUndoLimit();
1258int QUndoStack::undoLimit()
const
1260 Q_D(
const QUndoStack);
1262 return d->undo_limit;
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1282void QUndoStack::setActive(
bool active)
1284#if !QT_CONFIG(undogroup)
1289 if (d->group !=
nullptr) {
1291 d->group->setActiveStack(
this);
1292 else if (d->group->activeStack() ==
this)
1293 d->group->setActiveStack(
nullptr);
1298bool QUndoStack::isActive()
const
1300#if !QT_CONFIG(undogroup)
1303 Q_D(
const QUndoStack);
1304 return d->group ==
nullptr || d->group->activeStack() ==
this;
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1323
1324
1325
1326
1327
1328
1329
1330
1333
1334
1335
1336
1337
1338
1341
1342
1343
1344
1345
1346
1349
1350
1351
1352
1353
1354
1357
1358
1359
1360
1361
1362
1366#include "moc_qundostack.cpp"