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