/
ConfigurableHistoryIterator.class.st
175 lines (134 loc) · 4.56 KB
/
ConfigurableHistoryIterator.class.st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"
I am an history iterator you can configure with two blocks. One to define what to do during the redo action and the second to define what to do during the undo action.
Then you can register objects to me and ask to undo and redo actions, I will then use my undo and redo actions with my stored object to execute it.
Examples
--------------------
| saved history |
history := self
undo: [ :integer | saved := integer ]
redo: [ :integer | saved := integer asString ].
history hasPrevious. ""false""
history hasNext. ""false""
history register: 3.
history current. ""3""
history hasNext. ""false""
history hasPrevious. ""true""
history undo.
saved. ""3""
history hasPrevious. ""false""
history undo. ""EXCEPTION: NothingToUndo""
history undoIfEmpty: [ saved := nil ].
saved. ""nil""
history register: 3.
history register: 4.
history undo.
history undo.
history hasNext. ""true""
history redo.
saved. ""'3'""
history redo.
saved. ""'4'""
history redo. ""EXCEPTION: NothingToRedo""
history redoIfEmpty: [ saved := nil ].
saved. ""nil""
Internal Representation and Key Implementation Points.
--------------------
Instance Variables
redoAction: <aValuable> A valuable to execute when we redo an action.
redoStack: <aStack> A stack containing the elements that can be redone.
undoAction: <aValuable> A valuable to execute when we undo an action.
undoStack: <aStack> A stack containing the elements that can be undone.
I work with a system of two stacks:
- One stack to store objects to undo
- One stack to store ojbects to redo
When the user wants to undo something, I'll pop the undo stack, evaluate the undo action with it and push it on the redo stack.
When the user wants to redo something, I'll pop the redo stack, evaluate the redo action with it and push it on the undo stack.
When the user add a new item on the undo stack, I flush the redo stack.
"
Class {
#name : #ConfigurableHistoryIterator,
#superclass : #Object,
#instVars : [
'undoStack',
'redoStack',
'undoAction',
'redoAction'
],
#category : #'System-History-Iterators'
}
{ #category : #'instance creation' }
ConfigurableHistoryIterator class >> undo: aBlockClosure redo: aBlockClosure2 [
^ self new
undoAction: aBlockClosure;
redoAction: aBlockClosure2;
yourself
]
{ #category : #accessing }
ConfigurableHistoryIterator >> current [
"I return the current element of the iteration. In case there is nothing to undo, I raise an exception."
self hasPrevious ifFalse: [ NothingToUndo signal ].
^ undoStack top
]
{ #category : #testing }
ConfigurableHistoryIterator >> hasNext [
"Return true if there is at least one action to redo."
^ redoStack isNotEmpty
]
{ #category : #testing }
ConfigurableHistoryIterator >> hasPrevious [
"Return true if there is at least one action to undo."
^ undoStack isNotEmpty
]
{ #category : #initialization }
ConfigurableHistoryIterator >> initialize [
super initialize.
undoStack := Stack new.
redoStack := Stack new
]
{ #category : #actions }
ConfigurableHistoryIterator >> redo [
"Redo the next action, in case there is none, raise an exception."
self redoIfEmpty: [ NothingToRedo signal ]
]
{ #category : #accessing }
ConfigurableHistoryIterator >> redoAction: anObject [
redoAction := anObject
]
{ #category : #actions }
ConfigurableHistoryIterator >> redoIfEmpty: aBlockClosure [
"Redo the next action and register it in the undo list. In case there is no next action, execute the block as parameter."
| element |
self hasNext ifFalse: [ ^ aBlockClosure value ].
element := redoStack pop.
redoAction value: element.
undoStack push: element
]
{ #category : #adding }
ConfigurableHistoryIterator >> register: anObject [
"Register an object to the undo list. This will flush the redo list."
undoStack push: anObject.
redoStack removeAll
]
{ #category : #accessing }
ConfigurableHistoryIterator >> size [
"Return the size of the previous history"
^ undoStack size
]
{ #category : #actions }
ConfigurableHistoryIterator >> undo [
"Undo the previous action, in case there is none, raise an exception."
self undoIfEmpty: [ NothingToUndo signal ]
]
{ #category : #accessing }
ConfigurableHistoryIterator >> undoAction: anObject [
undoAction := anObject
]
{ #category : #actions }
ConfigurableHistoryIterator >> undoIfEmpty: aBlockClosure [
"Redo the previous action and register it in the redo list. In case there is no previous action, execute the block as parameter."
| element |
self hasPrevious ifFalse: [ ^ aBlockClosure value ].
element := undoStack pop.
undoAction value: element.
redoStack push: element
]