gramps/src/UndoHistory.py
Gerald Britton 72e19bb7a9 Updates to undo/redo logic -- part 1
svn: r15277
2010-04-23 19:02:24 +00:00

249 lines
8.7 KiB
Python

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
#
# This program is free software; you can redistribute it and/or modiy
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
# Written by Alex Roitman
#------------------------------------------------------------------------
#
# standard python modules
#
#------------------------------------------------------------------------
import time
from gen.ggettext import gettext as _
from itertools import chain
#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
import gtk
import gobject
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from QuestionDialog import QuestionDialog
import ManagedWindow
#-------------------------------------------------------------------------
#
# UndoHistory class
#
#-------------------------------------------------------------------------
class UndoHistory(ManagedWindow.ManagedWindow):
"""
The UndoHistory provides a list view with all the editing
steps available for undo/redo. Selecting a line in the list
will revert/advance to the appropriate step in editing history.
"""
def __init__(self, dbstate, uistate):
self.title = _("Undo History")
ManagedWindow.ManagedWindow.__init__(self, uistate, [], self.__class__)
self.db = dbstate.db
self.undodb = self.db.undodb
self.dbstate = dbstate
window = gtk.Dialog("", uistate.window,
gtk.DIALOG_DESTROY_WITH_PARENT, None)
self.undo_button = window.add_button(gtk.STOCK_UNDO,
gtk.RESPONSE_REJECT)
self.redo_button = window.add_button(gtk.STOCK_REDO,
gtk.RESPONSE_ACCEPT)
self.clear_button = window.add_button(gtk.STOCK_CLEAR,
gtk.RESPONSE_APPLY)
self.close_button = window.add_button(gtk.STOCK_CLOSE,
gtk.RESPONSE_CLOSE)
self.set_window(window, None, self.title)
self.window.set_size_request(400, 200)
self.window.connect('response', self._response)
scrolled_window = gtk.ScrolledWindow()
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.tree = gtk.TreeView()
self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_STRING)
self.selection = self.tree.get_selection()
self.renderer = gtk.CellRendererText()
self.tree.set_model(self.model)
self.tree.set_rules_hint(True)
self.tree.append_column(
gtk.TreeViewColumn(_('Original time'), self.renderer,
text=0, foreground=2, background=3))
self.tree.append_column(
gtk.TreeViewColumn(_('Action'), self.renderer,
text=1, foreground=2, background=3))
scrolled_window.add(self.tree)
self.window.vbox.add(scrolled_window)
self.window.show_all()
self._build_model()
self._update_ui()
self.selection.connect('changed', self._selection_changed)
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.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.undo_count:
# This transaction is an undo candidate
self.redo_button.set_sensitive(False)
self.undo_button.set_sensitive(self.undodb.undo_available())
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())
def _paint_rows(self, start, end, selected=False):
if selected:
(fg, bg) = get_colors(self.tree, gtk.STATE_SELECTED)
else:
fg = bg = None
for idx in range(start, end+1):
the_iter = self.model.get_iter( (idx,) )
self.model.set(the_iter, 2, fg)
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.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.undo_count
self._move(nsteps or 1)
elif response_id == gtk.RESPONSE_APPLY:
self._clear_clicked()
elif response_id == gtk.RESPONSE_DELETE_EVENT:
self.close(obj)
def build_menu_names(self, obj):
return (self.title, None)
def _clear_clicked(self, obj=None):
QuestionDialog(_("Delete confirmation"),
_("Are you sure you want to clear the Undo history?"),
_("Clear"),
self.clear,
self.window)
def clear(self):
self.undodb.clear()
self.db.abort_possible = False
self.update()
if self.db.undo_callback:
self.db.undo_callback(None)
if self.db.redo_callback:
self.db.redo_callback(None)
def _move(self, steps=-1):
if steps == 0 :
return
func = self.db.undo if steps < 0 else self.db.redo
for step in range(abs(steps)):
func(False)
self.update()
def _update_ui(self):
self._paint_rows(0, len(self.model)-1, False)
self.undo_button.set_sensitive(self.undodb.undo_available())
self.redo_button.set_sensitive(self.undodb.redo_available())
self.clear_button.set_sensitive(
self.undodb.undo_available() or self.undodb.redo_available()
)
def _build_model(self):
assert self.undodb.undoindex+1 == len(self.undodb.undoq)
self.model.clear()
fg = bg = None
if self.undodb.undo_history_timestamp:
if self.db.abort_possible:
mod_text = _('Database opened')
else:
mod_text = _('History cleared')
time_text = time.ctime(self.undodb.undo_history_timestamp)
self.model.append(row=[time_text, mod_text, fg, bg])
# 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.undo_count,)
self.selection.select_path(path)
def update(self):
self._build_model()
self._update_ui()
def gtk_color_to_str(color):
color_str = u"#%02x%02x%02x" % (color.red/256,
color.green/256,
color.blue/256)
return color_str
def get_colors(obj, state):
fg_color = obj.style.fg[state]
bg_color = obj.style.bg[state]
fg_color_str = gtk_color_to_str(fg_color)
bg_color_str = gtk_color_to_str(bg_color)
return (fg_color_str, bg_color_str)