96
|
1 module doodle.main.undo_manager;
|
|
2
|
|
3 version(none) {
|
|
4
|
|
5 class UndoManager : IUndoManager {
|
|
6 this() {
|
|
7 }
|
|
8
|
|
9 void addObserver(IUndoObserver observer) {
|
|
10 _observers.add(observer);
|
|
11 }
|
|
12
|
|
13 void removeObserver(IUndoObserver observer) {
|
|
14 _observers.remove(observer);
|
|
15 }
|
|
16
|
|
17 void reset() {
|
|
18 assert(!inTransaction());
|
|
19 _past.clear();
|
|
20 _future.clear();
|
|
21 foreach(IUndoObserver obs; _observers) {
|
|
22 obs.canUndo(false, "");
|
|
23 obs.canRedo(false, "");
|
|
24 }
|
|
25 }
|
|
26
|
|
27 void undo() {
|
|
28 assert(canUndo());
|
|
29 Transaction t = _past.pop();
|
|
30 t.undo();
|
|
31 _future.push(t);
|
|
32 }
|
|
33
|
|
34 void redo() {
|
|
35 assert(canRedo());
|
|
36 Transaction t = _future.pop();
|
|
37 t.redo();
|
|
38 _past.push(t);
|
|
39 }
|
|
40
|
|
41 bool canUndo() {
|
|
42 assert(!inTransaction());
|
|
43 return !_past.empty();
|
|
44 }
|
|
45
|
|
46 bool canRedo() {
|
|
47 assert(!inTransaction());
|
|
48 return !_future.empty();
|
|
49 }
|
|
50
|
|
51 void beginTransaction(char[] description) {
|
|
52 assert(!inTransaction());
|
|
53 _current_transaction = new Transaction(description);
|
|
54 }
|
|
55
|
|
56 void cancelTransaction() {
|
|
57 assert(inTransaction());
|
|
58 _current_transaction.cancel();
|
|
59 _current_transaction = null;
|
|
60 }
|
|
61
|
|
62 void endTransaction() {
|
|
63 assert(inTransaction());
|
|
64 _current_transaction.finalise();
|
|
65
|
|
66 if (!_future.empty()) {
|
|
67 _future.clear();
|
|
68 foreach(IUndoObserver obs; _observers) {
|
|
69 obs.canRedo(false, "");
|
|
70 }
|
|
71 }
|
|
72
|
|
73 _past.push(_current_transaction);
|
|
74
|
|
75 foreach(IUndoObserver obs; _observers) {
|
|
76 bs.canUndo(true, _current_transaction.name());
|
|
77 }
|
|
78
|
|
79 _current_transaction = null;
|
|
80 }
|
|
81
|
|
82 // IUndoManager implementations:
|
|
83
|
|
84 void addAction(Action action) {
|
|
85 assert(inTransaction());
|
|
86 _current_transaction.add(action);
|
|
87 }
|
|
88
|
|
89 private {
|
|
90 bool inTransaction() {
|
|
91 return _current_transaction !is null;
|
|
92 }
|
|
93
|
|
94 class Transaction {
|
|
95 enum State {
|
|
96 Accumulating,
|
|
97 Finalised,
|
|
98 Canceled
|
|
99 }
|
|
100
|
|
101 this(char[] description) {
|
|
102 _description = description;
|
|
103 _state = Accumulating;
|
|
104 }
|
|
105
|
|
106 char[] description() {
|
|
107 return _description;
|
|
108 }
|
|
109
|
|
110 void add(Action action) {
|
|
111 assert(_state == State.Accumulating);
|
|
112 _actions.addTail(action);
|
|
113 }
|
|
114
|
|
115 void finalise() {
|
|
116 assert(_state == State.Accumulating);
|
|
117 assert(!_actions.empty());
|
|
118 _finalised = true;
|
|
119 }
|
|
120
|
|
121 void cancel() {
|
|
122 assert(_state == State.Accumulating);
|
|
123 foreach_reverse(UndoAction ua; _actions) {
|
|
124 ua.undo();
|
|
125 }
|
|
126 }
|
|
127
|
|
128 void redo() {
|
|
129 assert(_finalised);
|
|
130 foreach (UndoAction ua; _actions) {
|
|
131 ua.redo();
|
|
132 }
|
|
133 }
|
|
134
|
|
135 void undo() {
|
|
136 assert(_finalised);
|
|
137 foreach_reverse(UndoAction ua; _actions) {
|
|
138 ua.undo();
|
|
139 }
|
|
140 }
|
|
141
|
|
142 private {
|
|
143 char[] _description;
|
|
144 List!(Action) _actions;
|
|
145 State _state;
|
|
146 }
|
|
147 }
|
|
148
|
|
149 Transaction _current_transaction;
|
|
150 Stack!(Transaction) _past;
|
|
151 Stack!(Transaction) _future;
|
|
152 Set!(IUndoObserver) _observers;
|
|
153 }
|
|
154 }
|
|
155
|
|
156 }
|