diff --git a/ChangeLog b/ChangeLog index 0c046f321..81a5058b0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2006-05-05 Alex Roitman + * src/UndoHistory.py: Add new file. + * src/ViewManager.py: undo history support. + * src/GrampsDb/_GrampsDbBase.py (Transaction.__init__): Add + timestamp to the transaction. + (GrampsDbBase.redo): Update undo history window. + (GrampsDbBase.undo): Update undo history window. + (GrampsDbBase.transaction_commit): Update undo history window. + (GrampsDbBase.transaction_commit): Set transaction timestamp. + (GrampsDbBase.__init__): Define undo_history_callback. + * src/Makefile.am (gdir_PYTHON): Ship new file. + 2006-05-05 Don Allingham * src/GrampsDb/_ReadGedcom.py: nickname changes * src/GrampsDb/_ReadXML.py: nickname changes diff --git a/po/ChangeLog b/po/ChangeLog index 30cb1d9dd..e0136caa7 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,6 @@ +2006-05-05 Alex Roitman + * POTFILES.in: Add new file. + 2006-05-04 Alex Roitman * POTFILES.in: Add new files. Update. diff --git a/po/POTFILES.in b/po/POTFILES.in index 7a3616c04..66a585352 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -53,6 +53,7 @@ src/ToolTips.py src/TransUtils.py src/TreeTips.py src/Utils.py +src/UndoHistory.py src/ViewManager.py src/ManagedWindow.py src/Config/_GrampsGconfKeys.py diff --git a/src/GrampsDb/_GrampsDbBase.py b/src/GrampsDb/_GrampsDbBase.py index 5c9af1964..300b0dc6b 100644 --- a/src/GrampsDb/_GrampsDbBase.py +++ b/src/GrampsDb/_GrampsDbBase.py @@ -255,6 +255,7 @@ class GrampsDbBase(GrampsDBCallback): self.name_group = None self.undo_callback = None self.redo_callback = None + self.undo_history_callback = None self.modified = 0 self.undoindex = -1 @@ -1223,6 +1224,7 @@ class GrampsDbBase(GrampsDBCallback): if not len(transaction) or self.readonly: return transaction.set_description(msg) + transaction.timestamp = time.time() self.undoindex += 1 if self.undoindex >= _UNDO_SIZE: # We overran the undo size. @@ -1281,6 +1283,8 @@ class GrampsDbBase(GrampsDBCallback): self.undo_callback(_("_Undo %s") % transaction.get_description()) if self.redo_callback: self.redo_callback(None) + if self.undo_history_callback: + self.undo_history_callback() def _do_emit(self, objtype, add_list, upd_list, del_list): if add_list: @@ -1302,7 +1306,7 @@ class GrampsDbBase(GrampsDBCallback): retlist.append(str(handle)) return retlist - def undo(self): + def undo(self,update_history=True): """ Accesses the last committed transaction, and reverts the data to the state before the transaction was committed. @@ -1319,7 +1323,7 @@ class GrampsDbBase(GrampsDBCallback): subitems = transaction.get_recnos() subitems.reverse() for record_id in subitems: - (key, handle, old_data, new_data) = transaction.get_record(record_id) + (key,handle,old_data,new_data) = transaction.get_record(record_id) if key == REFERENCE_KEY: self.undo_reference(old_data, handle) else: @@ -1340,9 +1344,11 @@ class GrampsDbBase(GrampsDBCallback): self.redo_callback(_("_Redo %s") % transaction.get_description()) + if update_history and self.undo_history_callback: + self.undo_history_callback() return True - def redo(self): + def redo(self,update_history=True): """ Accesses the last undone transaction, and reverts the data to the state before the transaction was undone. @@ -1360,7 +1366,7 @@ class GrampsDbBase(GrampsDBCallback): subitems = transaction.get_recnos() for record_id in subitems: - (key, handle, old_data, new_data) = transaction.get_record(record_id) + (key,handle,old_data,new_data) = transaction.get_record(record_id) if key == REFERENCE_KEY: self.undo_reference(new_data, handle) else: @@ -1388,6 +1394,8 @@ class GrampsDbBase(GrampsDBCallback): self.undo_callback(_("_Undo %s") % transaction.get_description()) + if update_history and self.undo_history_callback: + self.undo_history_callback() return True def undo_reference(self, data, handle): @@ -1967,6 +1975,7 @@ class Transaction: self.batch = batch self.no_magic = no_magic self.length = 0 + self.timestamp = 0 self.person_add = [] self.person_del = [] diff --git a/src/Makefile.am b/src/Makefile.am index 3a5f63ad2..e92173bda 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -77,7 +77,8 @@ gdir_PYTHON = \ Utils.py\ ViewManager.py\ SelectFamily.py\ - SelectSource.py + SelectSource.py\ + UndoHistory.py # Clean up all the byte-compiled files MOSTLYCLEANFILES = *pyc *pyo diff --git a/src/UndoHistory.py b/src/UndoHistory.py new file mode 100644 index 000000000..cc1b63e61 --- /dev/null +++ b/src/UndoHistory.py @@ -0,0 +1,155 @@ +# +# 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: ScratchPad.py 6485 2006-04-28 16:56:19Z rshura $ + +# Written by Alex Roitman + +#------------------------------------------------------------------------ +# +# standard python modules +# +#------------------------------------------------------------------------ +import time +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# 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.set_window( + gtk.Dialog("",uistate.window, + gtk.DIALOG_DESTROY_WITH_PARENT, + (gtk.STOCK_UNDO,gtk.RESPONSE_REJECT, + gtk.STOCK_REDO,gtk.RESPONSE_ACCEPT, + gtk.STOCK_CLEAR,gtk.RESPONSE_APPLY, + gtk.STOCK_CLOSE,gtk.RESPONSE_CLOSE, + ) + ), + None, self.title) + self.window.set_size_request(600,400) + self.window.connect('response', self._response) + + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) + self.list = gtk.TreeView() + self.model = gtk.ListStore(str, str) + self.selection = self.list.get_selection() + + self.list.set_model(self.model) + self.list.set_rules_hint(True) + self.list.append_column( + gtk.TreeViewColumn(_('Original time'), gtk.CellRendererText(), + text=0)) + self.list.append_column( + gtk.TreeViewColumn(_('Modification'), gtk.CellRendererText(), + text=1)) + + scrolled_window.add(self.list) + self.window.vbox.add(scrolled_window) + self.window.show_all() + + self._build_model() + + self.db.connect('database-changed',self.clear) + self.selection.connect('changed',self._move) + + def _response(self,obj,response_id): + if response_id == gtk.RESPONSE_CLOSE: + self.close() + elif response_id == gtk.RESPONSE_REJECT: + self._move(-1) + elif response_id == gtk.RESPONSE_ACCEPT: + self._move(1) + elif response_id == gtk.RESPONSE_APPLY: + self._clear_clicked() + + 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.db.undoindex = -1 + self.db.translist = [None] * len(self.db.translist) + 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,obj,steps=-1): + self._update_ui() + + def _update_ui(self): + pass + + def _build_model(self): + self.model.clear() + # Get the not-None portion of transaction list + translist = [item for item in self.db.translist if item] + translist.reverse() + for transaction in translist: + time_text = time.ctime(transaction.timestamp) + mod_text = transaction.get_description() + self.model.append(row=[time_text,mod_text]) + if self.db.undoindex < 0: + self.selection.unselect_all() + else: + path = (self.db.undoindex,) + self.selection.select_path(path) + + def update(self): + self._build_model() + self._update_ui() diff --git a/src/ViewManager.py b/src/ViewManager.py index 09b8ab49b..1e14b4fd6 100644 --- a/src/ViewManager.py +++ b/src/ViewManager.py @@ -78,6 +78,7 @@ import RecentFiles import NameDisplay import Mime import GrampsWidgets +import UndoHistory #------------------------------------------------------------------------- # @@ -107,8 +108,8 @@ uidefault = ''' - + @@ -359,7 +360,6 @@ class ViewManager: self._undo_action_list = [ ('Undo', gtk.STOCK_UNDO, _('_Undo'),'z', None, self.undo), - ('UndoHistory', 'stock_undo-history', _('_Undo History'), None, None, self.undo_history), ] self._redo_action_list = [ @@ -367,6 +367,11 @@ class ViewManager: self.redo), ] + self._undo_history_action_list = [ + ('UndoHistory', 'stock_undo-history', + _('Undo History'), None, None, self.undo_history), + ] + self._navigation_type = { PageView.NAVIGATION_NONE: (None, None), PageView.NAVIGATION_PERSON: (None, None), @@ -438,6 +443,7 @@ class ViewManager: self.fileactions = gtk.ActionGroup('FileWindow') self.undoactions = gtk.ActionGroup('Undo') self.redoactions = gtk.ActionGroup('Redo') + self.undohistoryactions = gtk.ActionGroup('UndoHistory') self.fileactions.add_actions(self._file_action_list) self.actiongroup.add_actions(self._action_action_list) @@ -449,12 +455,15 @@ class ViewManager: self.redoactions.add_actions(self._redo_action_list) self.redoactions.set_sensitive(False) + self.undohistoryactions.add_actions(self._undo_history_action_list) + merge_id = self.uimanager.add_ui_from_string(uidefault) self.uimanager.insert_action_group(self.fileactions, 1) self.uimanager.insert_action_group(self.actiongroup, 1) self.uimanager.insert_action_group(self.undoactions, 1) self.uimanager.insert_action_group(self.redoactions, 1) + self.uimanager.insert_action_group(self.undohistoryactions, 1) self.uimanager.ensure_update() def home_page_activate(self, obj): @@ -891,9 +900,6 @@ class ViewManager: log.error("Failed to open database.", exc_info=True) return False - # Undo/Redo always start with standard labels and insensitive state - #self.undo_callback(None) - #self.redo_callback(None) self.file_loaded = True self.actiongroup.set_visible(True) return True @@ -932,6 +938,7 @@ class ViewManager: self.change_page(None, None) self.state.db.undo_callback = self.change_undo_label self.state.db.redo_callback = self.change_redo_label + self.state.db.undo_history_callback = self.undo_history_update self.actiongroup.set_visible(True) self.window.window.set_cursor(None) return True @@ -941,7 +948,7 @@ class ViewManager: self.undoactions = gtk.ActionGroup('Undo') if label: self.undoactions.add_actions([ - ('Undo', gtk.STOCK_UNDO, label, 'z', None, self.undo)]) + ('Undo',gtk.STOCK_UNDO,label,'z',None,self.undo)]) else: self.undoactions.add_actions([ ('Undo', gtk.STOCK_UNDO, '_Undo', @@ -963,6 +970,19 @@ class ViewManager: self.redoactions.set_sensitive(False) self.uimanager.insert_action_group(self.redoactions, 1) + def undo_history_update(self): + """ + This function is called to update both the state of + the Undo History menu item (enable/disable) and + the contents of the Undo History window. + """ + try: + # Try updating undo history window if it exists + self.undo_history_window.update() + except AttributeError: + # Let it go: history window does not exist + pass + def setup_bookmarks(self): self.bookmarks = Bookmarks.Bookmarks(self.state, self.uistate, self.state.db.get_bookmarks()) @@ -1007,7 +1027,11 @@ class ViewManager: self.state.db.redo() def undo_history(self, obj): - print "UNDO HISTORY" + try: + self.undo_history_window = UndoHistory.UndoHistory(self.state, + self.uistate) + except Errors.WindowActiveError: + pass def export_data(self, obj): import Exporter