28
28
"""
29
29
30
30
from classes .logger import log
31
- from classes import info
32
31
import copy
33
- import os
34
32
import json
35
33
34
+
36
35
class UpdateWatcher :
37
36
""" Interface for classes that listen for 'undo' and 'redo' events. """
38
37
39
38
def updateStatusChanged (self , undo_status , redo_status ):
40
- """ Easily be notified each time there are 'undo' or 'redo' actions available in the UpdateManager. """
39
+ """ Easily be notified each time there are 'undo' or 'redo' actions
40
+ available in the UpdateManager. """
41
41
raise NotImplementedError ("updateStatus() not implemented in UpdateWatcher implementer." )
42
42
43
43
44
44
class UpdateInterface :
45
45
""" Interface for classes that listen for changes (insert, update, and delete). """
46
46
47
47
def changed (self , action ):
48
- """ This method is invoked each time the UpdateManager is changed. The action contains all the details of what changed,
48
+ """ This method is invoked each time the UpdateManager is changed.
49
+ The action contains all the details of what changed,
49
50
including the type of change (insert, update, or delete). """
50
51
raise NotImplementedError ("changed() not implemented in UpdateInterface implementer." )
51
52
52
53
53
54
class UpdateAction :
54
- """A data structure representing a single update manager action, including any necessary data to reverse the action."""
55
+ """A data structure representing a single update manager action,
56
+ including any necessary data to reverse the action."""
55
57
56
58
def __init__ (self , type = None , key = [], values = None , partial_update = False ):
57
59
self .type = type # insert, update, or delete
@@ -121,22 +123,23 @@ def load_json(self, value):
121
123
122
124
123
125
class UpdateManager :
124
- """ This class is used to track and distribute changes to listeners. Typically, only 1 instance of this class is needed,
125
- and many different listeners are connected with the add_listener() method. """
126
+ """ This class is used to track and distribute changes to listeners.
127
+ Typically, only 1 instance of this class is needed, and many different
128
+ listeners are connected with the add_listener() method. """
126
129
127
130
def __init__ (self ):
128
131
self .statusWatchers = [] # List of watchers
129
132
self .updateListeners = [] # List of listeners
130
133
self .actionHistory = [] # List of actions performed to current state
131
134
self .redoHistory = [] # List of actions undone
132
135
self .currentStatus = [None , None ] # Status of Undo and Redo buttons (true/false for should be enabled)
133
- self .ignore_history = False # Ignore saving actions to history, to prevent a huge undo/redo list
134
- self .last_action = None
136
+ self .ignore_history = False # Ignore saving actions to history, to prevent a huge undo/redo list
137
+ self .last_action = None # The last action processed
138
+ self .pending_action = None # Last action not added to actionHistory list
135
139
136
140
def load_history (self , project ):
137
141
"""Load history from project"""
138
- self .redoHistory .clear ()
139
- self .actionHistory .clear ()
142
+ self .reset ()
140
143
141
144
# Get history from project data
142
145
history = project .get ("history" )
@@ -183,22 +186,24 @@ def save_history(self, project, history_length):
183
186
log .info ("Saving undo history, skipped key: %s" % str (action .key ))
184
187
185
188
# Set history data in project
186
- self .ignore_history = True
187
- self .update (["history" ], { "redo" : redo_list , "undo" : undo_list })
188
- self .ignore_history = False
189
+ self .update_untracked (["history" ], {"redo" : redo_list , "undo" : undo_list })
189
190
190
191
def reset (self ):
191
- """ Reset the UpdateManager, and clear all UpdateActions and History. This does not clear listeners and watchers. """
192
+ """ Reset the UpdateManager, and clear all UpdateActions and History.
193
+ This does not clear listeners and watchers. """
192
194
self .actionHistory .clear ()
193
195
self .redoHistory .clear ()
196
+ self .pending_action = None
197
+ self .last_action = None
194
198
195
199
# Notify watchers of new history state
196
200
self .update_watchers ()
197
201
198
202
def add_listener (self , listener , index = - 1 ):
199
- """ Add a new listener (which will invoke the changed(action) method each time an UpdateAction is available). """
203
+ """ Add a new listener (which will invoke the changed(action) method
204
+ each time an UpdateAction is available). """
200
205
201
- if not listener in self .updateListeners :
206
+ if listener not in self .updateListeners :
202
207
if index <= - 1 :
203
208
# Add listener to end of list
204
209
self .updateListeners .append (listener )
@@ -209,9 +214,10 @@ def add_listener(self, listener, index=-1):
209
214
log .warning ("Cannot add existing listener: {}" .format (str (listener )))
210
215
211
216
def add_watcher (self , watcher ):
212
- """ Add a new watcher (which will invoke the updateStatusChanged() method each time a 'redo' or 'undo' action is available). """
217
+ """ Add a new watcher (which will invoke the updateStatusChanged() method
218
+ each time a 'redo' or 'undo' action is available). """
213
219
214
- if not watcher in self .statusWatchers :
220
+ if watcher not in self .statusWatchers :
215
221
self .statusWatchers .append (watcher )
216
222
else :
217
223
log .warning ("Cannot add existing watcher: {}" .format (str (watcher )))
@@ -262,6 +268,7 @@ def undo(self):
262
268
last_action = copy .deepcopy (self .actionHistory .pop ())
263
269
264
270
self .redoHistory .append (last_action )
271
+ self .pending_action = None
265
272
# Get reverse of last action and perform it
266
273
reverse_action = self .get_reverse_action (last_action )
267
274
self .dispatch_action (reverse_action )
@@ -278,6 +285,7 @@ def redo(self):
278
285
next_action .key = next_action .key [:- 1 ]
279
286
280
287
self .actionHistory .append (next_action )
288
+ self .pending_action = None
281
289
# Perform next redo action
282
290
self .dispatch_action (next_action )
283
291
@@ -297,55 +305,75 @@ def dispatch_action(self, action):
297
305
298
306
# Perform load action (loading all project data), clearing history for taking a new path
299
307
def load (self , values ):
300
- """ Load all project data via an UpdateAction into the UpdateManager (this action will then be distributed to all listeners) """
308
+ """ Load all project data via an UpdateAction into the UpdateManager
309
+ (this action will then be distributed to all listeners) """
301
310
302
311
self .last_action = UpdateAction ('load' , '' , values )
303
312
self .redoHistory .clear ()
304
313
self .actionHistory .clear ()
314
+ self .pending_action = None
305
315
self .dispatch_action (self .last_action )
306
316
307
317
# Perform new actions, clearing redo history for taking a new path
308
318
def insert (self , key , values ):
309
- """ Insert a new UpdateAction into the UpdateManager (this action will then be distributed to all listeners) """
319
+ """ Insert a new UpdateAction into the UpdateManager
320
+ (this action will then be distributed to all listeners) """
310
321
311
322
self .last_action = UpdateAction ('insert' , key , values )
312
- if not self .ignore_history :
323
+ if self .ignore_history :
324
+ self .pending_action = self .last_action
325
+ else :
313
326
self .redoHistory .clear ()
327
+ self .pending_action = None
314
328
self .actionHistory .append (self .last_action )
315
329
self .dispatch_action (self .last_action )
316
330
317
331
def update (self , key , values , partial_update = False ):
318
- """ Update the UpdateManager with an UpdateAction (this action will then be distributed to all listeners) """
332
+ """ Update the UpdateManager with an UpdateAction
333
+ (this action will then be distributed to all listeners) """
319
334
320
335
self .last_action = UpdateAction ('update' , key , values , partial_update )
321
- if not self .ignore_history :
336
+ if self .ignore_history :
337
+ self .pending_action = self .last_action
338
+ else :
322
339
if self .last_action .key and self .last_action .key [0 ] != "history" :
323
340
# Clear redo history for any update except a "history" update
324
341
self .redoHistory .clear ()
342
+ self .pending_action = None
325
343
self .actionHistory .append (self .last_action )
326
344
self .dispatch_action (self .last_action )
327
345
328
346
def update_untracked (self , key , values , partial_update = False ):
329
- """ Update the UpdateManager with an UpdateAction, without creating a new entry in the history table (this action will then be distributed to all listeners) """
347
+ """ Update the UpdateManager with an UpdateAction, without creating
348
+ a new entry in the history table
349
+ (this action will then be distributed to all listeners) """
330
350
previous_ignore = self .ignore_history
351
+ previous_pending = self .pending_action
331
352
self .ignore_history = True
332
353
self .update (key , values , partial_update )
333
354
self .ignore_history = previous_ignore
355
+ self .pending_action = previous_pending
334
356
335
357
def delete (self , key ):
336
- """ Delete an item from the UpdateManager with an UpdateAction (this action will then be distributed to all listeners) """
358
+ """ Delete an item from the UpdateManager with an UpdateAction
359
+ (this action will then be distributed to all listeners) """
337
360
338
361
self .last_action = UpdateAction ('delete' , key )
339
- if not self .ignore_history :
362
+ if self .ignore_history :
363
+ self .pending_action = self .last_action
364
+ else :
340
365
self .redoHistory .clear ()
366
+ self .pending_action = None
341
367
self .actionHistory .append (self .last_action )
342
368
self .dispatch_action (self .last_action )
343
369
344
370
def apply_last_action_to_history (self , previous_value ):
345
371
""" Apply the last action to the history """
346
- if self .last_action :
347
- self .last_action .set_old_values (previous_value )
348
- self .actionHistory .append (self .last_action )
372
+ if self .pending_action :
373
+ self .pending_action .set_old_values (previous_value )
374
+ self .actionHistory .append (self .pending_action )
375
+ self .last_action = self .pending_action
376
+ self .pending_action = None
349
377
350
378
# Notify watchers of new history state
351
379
self .update_watchers ()
0 commit comments