Updates to undo/redo logic -- part 1

svn: r15277
This commit is contained in:
Gerald Britton 2010-04-23 19:02:24 +00:00
parent 71575f91ea
commit 72e19bb7a9
2 changed files with 52 additions and 27 deletions

View File

@ -29,6 +29,7 @@
#------------------------------------------------------------------------ #------------------------------------------------------------------------
import time import time
from gen.ggettext import gettext as _ from gen.ggettext import gettext as _
from itertools import chain
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -110,29 +111,27 @@ class UndoHistory(ManagedWindow.ManagedWindow):
self.show() self.show()
def _selection_changed(self, obj): def _selection_changed(self, obj):
assert self.undodb.undo_count == self.undodb.undoindex + 1
(model, node) = self.selection.get_selected() (model, node) = self.selection.get_selected()
if not node: if not node:
return return
path = self.model.get_path(node) path = self.model.get_path(node)
start = min(path[0], self.undodb.undo_count)
start = min(path[0], self.undodb.undoindex+1) end = max(path[0], self.undodb.undo_count)
end = max(path[0], self.undodb.undoindex+1)
self._paint_rows(0, len(self.model)-1, False) self._paint_rows(0, len(self.model)-1, False)
self._paint_rows(start, end, True) 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.redo_button.set_sensitive(False)
self.undo_button.set_sensitive(self.undodb.undo_available()) 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.undo_button.set_sensitive(False)
self.redo_button.set_sensitive(self.undodb.redo_available()) 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): def _paint_rows(self, start, end, selected=False):
if selected: if selected:
(fg, bg) = get_colors(self.tree, gtk.STATE_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) self.model.set(the_iter, 3, bg)
def _response(self, obj, response_id): def _response(self, obj, response_id):
assert self.undodb.undo_count == self.undodb.undoindex + 1
if response_id == gtk.RESPONSE_CLOSE: if response_id == gtk.RESPONSE_CLOSE:
self.close(obj) self.close(obj)
elif response_id == gtk.RESPONSE_REJECT: elif response_id == gtk.RESPONSE_REJECT:
# Undo the selected entries
(model, node) = self.selection.get_selected() (model, node) = self.selection.get_selected()
if not node: if not node:
return return
path = self.model.get_path(node) 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) self._move(nsteps or -1)
elif response_id == gtk.RESPONSE_ACCEPT: elif response_id == gtk.RESPONSE_ACCEPT:
# Redo the selected entries
(model, node) = self.selection.get_selected() (model, node) = self.selection.get_selected()
if not node: if not node:
return return
path = self.model.get_path(node) 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) self._move(nsteps or 1)
elif response_id == gtk.RESPONSE_APPLY: elif response_id == gtk.RESPONSE_APPLY:
@ -181,7 +183,7 @@ class UndoHistory(ManagedWindow.ManagedWindow):
def clear(self): def clear(self):
self.undodb.clear() self.undodb.clear()
self.undodb.abort_possible = False self.db.abort_possible = False
self.update() self.update()
if self.db.undo_callback: if self.db.undo_callback:
self.db.undo_callback(None) self.db.undo_callback(None)
@ -206,6 +208,7 @@ class UndoHistory(ManagedWindow.ManagedWindow):
) )
def _build_model(self): def _build_model(self):
assert self.undodb.undoindex+1 == len(self.undodb.undoq)
self.model.clear() self.model.clear()
fg = bg = None fg = bg = None
@ -217,13 +220,12 @@ class UndoHistory(ManagedWindow.ManagedWindow):
time_text = time.ctime(self.undodb.undo_history_timestamp) time_text = time.ctime(self.undodb.undo_history_timestamp)
self.model.append(row=[time_text, mod_text, fg, bg]) self.model.append(row=[time_text, mod_text, fg, bg])
# Get the not-None portion of transaction list # Add the undo and redo queues to the model
translist = filter(None, self.undodb.translist) for txn in chain(self.undodb.undoq, reversed(self.undodb.redoq)):
for transaction in translist: time_text = time.ctime(txn.timestamp)
time_text = time.ctime(transaction.timestamp) mod_text = txn.get_description()
mod_text = transaction.get_description()
self.model.append(row=[time_text, mod_text, fg, bg]) 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) self.selection.select_path(path)
def update(self): def update(self):

View File

@ -35,6 +35,7 @@ import time, os
import cPickle as pickle import cPickle as pickle
from bsddb import db from bsddb import db
from gen.ggettext import gettext as _ from gen.ggettext import gettext as _
from collections import deque
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -53,8 +54,8 @@ import Errors
DBERRS = (db.DBRunRecoveryError, db.DBAccessError, DBERRS = (db.DBRunRecoveryError, db.DBAccessError,
db.DBPageNotFoundError, db.DBInvalidArgError) db.DBPageNotFoundError, db.DBInvalidArgError)
_SIGBASE = ('person', 'family', 'source', 'event', 'media', _SIGBASE = ('person', 'family', 'source', 'event', 'media', 'place',
'place', 'repository', 'reference', 'note') 'repository', 'reference', 'note', 'undoq', 'redoq')
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# DbUndo class # DbUndo class
@ -91,6 +92,8 @@ class DbUndo(object):
""" """
Clear the undo/redo list (but not the backing storage) Clear the undo/redo list (but not the backing storage)
""" """
self.undoq = deque()
self.redoq = deque()
self.translist = [] self.translist = []
self.undoindex = -1 self.undoindex = -1
self.undo_history_timestamp = time.time() self.undo_history_timestamp = time.time()
@ -161,6 +164,7 @@ class DbUndo(object):
txn.timestamp = time.time() txn.timestamp = time.time()
# If we're within our undo limit, add this transaction # If we're within our undo limit, add this transaction
self.undoq.append(txn)
self.undoindex += 1 self.undoindex += 1
if self.undoindex < DBUNDO: if self.undoindex < DBUNDO:
if self.undoindex >= len(self.translist): if self.undoindex >= len(self.translist):
@ -168,17 +172,21 @@ class DbUndo(object):
else: else:
self.translist[self.undoindex] = txn self.translist[self.undoindex] = txn
del self.translist[self.undoindex+1:] del self.translist[self.undoindex+1:]
self.redoq.clear()
# Otherwise, we've exceeded our undo limit # Otherwise, we've exceeded our undo limit
else: else:
self.db.abort_possible = False self.db.abort_possible = False
self.undo_history_timestamp = time.time() self.undo_history_timestamp = time.time()
self.translist[-1] = txn self.translist[-1] = txn
self.redoq.clear()
def undo_available(self): def undo_available(self):
""" """
Return boolean of whether or not there's a possibility of undo. 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): if 0 <= self.undoindex < len(self.translist):
return True return True
return False return False
@ -187,6 +195,8 @@ class DbUndo(object):
""" """
Return boolean of whether or not there's a possibility of redo. 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): if 0 <= self.undoindex+1 < len(self.translist):
return True return True
return False return False
@ -229,7 +239,11 @@ class DbUndo(object):
Access the last committed transaction, and revert the data to the Access the last committed transaction, and revert the data to the
state before the transaction was committed. 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 db = self.db
self.undoindex -= 1 self.undoindex -= 1
subitems = transaction.get_recnos(reverse=True) subitems = transaction.get_recnos(reverse=True)
@ -262,12 +276,15 @@ class DbUndo(object):
def __redo(self, db=None, update_history=True): 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. before the transaction was undone.
""" """
txn = self.redoq.pop()
self.undoq.append(txn)
self.undoindex += 1 self.undoindex += 1
transaction = self.translist[self.undoindex] #transaction = self.translist[self.undoindex]
#assert transaction == txn
transaction = txn
db = self.db db = self.db
subitems = transaction.get_recnos() subitems = transaction.get_recnos()
@ -287,8 +304,9 @@ class DbUndo(object):
% transaction.get_description()) % transaction.get_description())
if db.redo_callback: if db.redo_callback:
if self.redo_available(): if len(self.redoq) > 1:
new_transaction = self.translist[self.undoindex+1] #new_transaction = self.translist[self.undoindex+1]
new_transaction = self.redoq[-2]
db.redo_callback(_("_Redo %s") db.redo_callback(_("_Redo %s")
% new_transaction.get_description()) % new_transaction.get_description())
else: else:
@ -331,7 +349,12 @@ class DbUndo(object):
except DBERRS, msg: except DBERRS, msg:
self.db._log_error() 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): class DbUndoList(DbUndo):
""" """