From 72e19bb7a97f336aa9d887308980427f10df8ebe Mon Sep 17 00:00:00 2001 From: Gerald Britton Date: Fri, 23 Apr 2010 19:02:24 +0000 Subject: [PATCH] Updates to undo/redo logic -- part 1 svn: r15277 --- src/UndoHistory.py | 38 ++++++++++++++++++++------------------ src/gen/db/undoredo.py | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/UndoHistory.py b/src/UndoHistory.py index fe86da99c..23d2f46ac 100644 --- a/src/UndoHistory.py +++ b/src/UndoHistory.py @@ -29,6 +29,7 @@ #------------------------------------------------------------------------ import time from gen.ggettext import gettext as _ +from itertools import chain #------------------------------------------------------------------------- # @@ -110,29 +111,27 @@ class UndoHistory(ManagedWindow.ManagedWindow): self.show() def _selection_changed(self, obj): + assert self.undodb.undo_count == self.undodb.undoindex + 1 (model, node) = self.selection.get_selected() if not node: return path = self.model.get_path(node) - - start = min(path[0], self.undodb.undoindex+1) - end = max(path[0], self.undodb.undoindex+1) + start = min(path[0], self.undodb.undo_count) + end = max(path[0], self.undodb.undo_count) self._paint_rows(0, len(self.model)-1, False) self._paint_rows(start, end, True) - if path[0] < self.undodb.undoindex+1: + if path[0] < self.undodb.undo_count: + # This transaction is an undo candidate self.redo_button.set_sensitive(False) self.undo_button.set_sensitive(self.undodb.undo_available()) - elif path[0] > self.undodb.undoindex+1: + else: # path[0] >= self.undodb.undo_count: + # This transaction is an redo candidate self.undo_button.set_sensitive(False) self.redo_button.set_sensitive(self.undodb.redo_available()) - else: #path[0] == self.undodb.undoindex+1 - self.undo_button.set_sensitive(self.undodb.undo_available()) - self.redo_button.set_sensitive(self.undodb.redo_available()) - def _paint_rows(self, start, end, selected=False): if selected: (fg, bg) = get_colors(self.tree, gtk.STATE_SELECTED) @@ -145,23 +144,26 @@ class UndoHistory(ManagedWindow.ManagedWindow): self.model.set(the_iter, 3, bg) def _response(self, obj, response_id): + assert self.undodb.undo_count == self.undodb.undoindex + 1 if response_id == gtk.RESPONSE_CLOSE: self.close(obj) elif response_id == gtk.RESPONSE_REJECT: + # Undo the selected entries (model, node) = self.selection.get_selected() if not node: return path = self.model.get_path(node) - nsteps = path[0]-self.undodb.undoindex-1 + nsteps = path[0]-self.undodb.undo_count-1 self._move(nsteps or -1) elif response_id == gtk.RESPONSE_ACCEPT: + # Redo the selected entries (model, node) = self.selection.get_selected() if not node: return path = self.model.get_path(node) - nsteps = path[0]-self.undodb.undoindex-1 + nsteps = path[0]-self.undodb.undo_count self._move(nsteps or 1) elif response_id == gtk.RESPONSE_APPLY: @@ -181,7 +183,7 @@ class UndoHistory(ManagedWindow.ManagedWindow): def clear(self): self.undodb.clear() - self.undodb.abort_possible = False + self.db.abort_possible = False self.update() if self.db.undo_callback: self.db.undo_callback(None) @@ -206,6 +208,7 @@ class UndoHistory(ManagedWindow.ManagedWindow): ) def _build_model(self): + assert self.undodb.undoindex+1 == len(self.undodb.undoq) self.model.clear() fg = bg = None @@ -217,13 +220,12 @@ class UndoHistory(ManagedWindow.ManagedWindow): time_text = time.ctime(self.undodb.undo_history_timestamp) self.model.append(row=[time_text, mod_text, fg, bg]) - # Get the not-None portion of transaction list - translist = filter(None, self.undodb.translist) - for transaction in translist: - time_text = time.ctime(transaction.timestamp) - mod_text = transaction.get_description() + # Add the undo and redo queues to the model + for txn in chain(self.undodb.undoq, reversed(self.undodb.redoq)): + time_text = time.ctime(txn.timestamp) + mod_text = txn.get_description() self.model.append(row=[time_text, mod_text, fg, bg]) - path = (self.undodb.undoindex+1,) + path = (self.undodb.undo_count,) self.selection.select_path(path) def update(self): diff --git a/src/gen/db/undoredo.py b/src/gen/db/undoredo.py index 203af9857..d24b7f31e 100644 --- a/src/gen/db/undoredo.py +++ b/src/gen/db/undoredo.py @@ -35,6 +35,7 @@ import time, os import cPickle as pickle from bsddb import db from gen.ggettext import gettext as _ +from collections import deque #------------------------------------------------------------------------- # @@ -53,8 +54,8 @@ import Errors DBERRS = (db.DBRunRecoveryError, db.DBAccessError, db.DBPageNotFoundError, db.DBInvalidArgError) -_SIGBASE = ('person', 'family', 'source', 'event', 'media', - 'place', 'repository', 'reference', 'note') +_SIGBASE = ('person', 'family', 'source', 'event', 'media', 'place', + 'repository', 'reference', 'note', 'undoq', 'redoq') #------------------------------------------------------------------------- # # DbUndo class @@ -91,6 +92,8 @@ class DbUndo(object): """ Clear the undo/redo list (but not the backing storage) """ + self.undoq = deque() + self.redoq = deque() self.translist = [] self.undoindex = -1 self.undo_history_timestamp = time.time() @@ -161,6 +164,7 @@ class DbUndo(object): txn.timestamp = time.time() # If we're within our undo limit, add this transaction + self.undoq.append(txn) self.undoindex += 1 if self.undoindex < DBUNDO: if self.undoindex >= len(self.translist): @@ -168,17 +172,21 @@ class DbUndo(object): else: self.translist[self.undoindex] = txn del self.translist[self.undoindex+1:] + self.redoq.clear() # Otherwise, we've exceeded our undo limit else: self.db.abort_possible = False self.undo_history_timestamp = time.time() self.translist[-1] = txn + self.redoq.clear() def undo_available(self): """ Return boolean of whether or not there's a possibility of undo. """ + #print "Undo available:", bool(self.undoq) + return len(self.undoq) if 0 <= self.undoindex < len(self.translist): return True return False @@ -187,6 +195,8 @@ class DbUndo(object): """ Return boolean of whether or not there's a possibility of redo. """ + #print "Redo available:", bool(self.redoq) + return len(self.redoq) if 0 <= self.undoindex+1 < len(self.translist): return True return False @@ -229,7 +239,11 @@ class DbUndo(object): Access the last committed transaction, and revert the data to the state before the transaction was committed. """ - transaction = self.translist[self.undoindex] + txn = self.undoq.pop() + self.redoq.append(txn) + #transaction = self.translist[self.undoindex] + #assert transaction == txn + transaction = txn db = self.db self.undoindex -= 1 subitems = transaction.get_recnos(reverse=True) @@ -262,12 +276,15 @@ class DbUndo(object): def __redo(self, db=None, update_history=True): """ - Accesse the last undone transaction, and revert the data to the state + Access the last undone transaction, and revert the data to the state before the transaction was undone. """ - + txn = self.redoq.pop() + self.undoq.append(txn) self.undoindex += 1 - transaction = self.translist[self.undoindex] + #transaction = self.translist[self.undoindex] + #assert transaction == txn + transaction = txn db = self.db subitems = transaction.get_recnos() @@ -287,8 +304,9 @@ class DbUndo(object): % transaction.get_description()) if db.redo_callback: - if self.redo_available(): - new_transaction = self.translist[self.undoindex+1] + if len(self.redoq) > 1: + #new_transaction = self.translist[self.undoindex+1] + new_transaction = self.redoq[-2] db.redo_callback(_("_Redo %s") % new_transaction.get_description()) else: @@ -331,7 +349,12 @@ class DbUndo(object): except DBERRS, msg: self.db._log_error() - raise Errors.DbError(msg) + raise Errors.DbError(msg) + + @property + def undo_count(self): + """Number of undo requests in the queue""" + return len(self.undoq) class DbUndoList(DbUndo): """