[meld] Rework undo behaviour; support undo/redo after save (closes bgo#156984)
- From: Kai Willadsen <kaiw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [meld] Rework undo behaviour; support undo/redo after save (closes bgo#156984)
- Date: Fri, 25 Jun 2010 07:52:19 +0000 (UTC)
commit d3265e173cf7ba12bb93a9de1ff2c81202004915
Author: Kai Willadsen <kai willadsen gmail com>
Date: Wed Mar 17 12:13:21 2010 +1000
Rework undo behaviour; support undo/redo after save (closes bgo#156984)
The current undo stack implementation clears all undo information on
save, because its modification tracking doesn't allow for easily
repositioning the modification point within the stack. This commit adds
per-buffer tracking of modification points within the stack, and
repositions these as appropriate on save. In addition, we now emit
signals when the modified state of a buffer (as defined by the undo
stack) changes.
meld/filediff.py | 35 ++++++++---------------------------
meld/undo.py | 44 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 51 insertions(+), 28 deletions(-)
---
diff --git a/meld/filediff.py b/meld/filediff.py
index f8a5816..8989a62 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -203,6 +203,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
t.connect("focus-in-event", self.on_current_diff_changed)
t.connect("focus-out-event", self.on_current_diff_changed)
self.linediffer.connect("diffs-changed", self.on_diffs_changed)
+ self.undosequence.connect("checkpointed", self.on_undo_checkpointed)
def on_focus_change(self):
self.keymask = 0
@@ -568,12 +569,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
def on_text_insert_text(self, buffer, it, text, textlen):
if not self.undosequence_busy:
- self.undosequence.begin_group()
- pane = self.textbuffer.index(buffer)
- if self.bufferdata[pane].modified != 1:
- self.undosequence.add_action( BufferModifiedAction(buffer, self) )
self.undosequence.add_action( BufferInsertionAction(buffer, it.get_offset(), text) )
- self.undosequence.end_group()
def on_text_delete_range(self, buffer, it0, it1):
text = buffer.get_text(it0, it1, 0)
@@ -581,11 +577,10 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
assert self.deleted_lines_pending == -1
self.deleted_lines_pending = text.count("\n")
if not self.undosequence_busy:
- self.undosequence.begin_group()
- if self.bufferdata[pane].modified != 1:
- self.undosequence.add_action( BufferModifiedAction(buffer, self) )
self.undosequence.add_action( BufferDeletionAction(buffer, it0.get_offset(), text) )
- self.undosequence.end_group()
+
+ def on_undo_checkpointed(self, undosequence, buf, checkpointed):
+ self.set_buffer_modified(buf, not checkpointed)
#
#
@@ -706,6 +701,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
self.scheduler.add_task( self._set_files_internal(files).next )
def _load_files(self, files, textbuffers, panetext):
+ self.undosequence.clear()
yield _("[%s] Set num panes") % self.label_text
self.set_num_panes( len(files) )
self._disconnect_buffer_handlers()
@@ -786,9 +782,10 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
t.text.append("\n")
panetext[t.pane] = "".join(t.text)
yield 1
+ for b in self.textbuffer:
+ self.undosequence.checkpoint(b)
def _diff_files(self, files, panetext):
- self.undosequence.clear()
yield _("[%s] Computing differences") % self.label_text
panetext = [self._filter_text(p) for p in panetext]
lines = map(lambda x: x.split("\n"), panetext)
@@ -1029,8 +1026,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
return melddoc.RESULT_ERROR
if self._save_text_to_filename(bufdata.filename, text):
self.emit("file-changed", bufdata.filename)
- self.undosequence.clear()
- self.set_buffer_modified(buf, 0)
+ self.undosequence.checkpoint(buf)
return melddoc.RESULT_OK
else:
return melddoc.RESULT_ERROR
@@ -1498,18 +1494,3 @@ class BufferDeletionAction(BufferAction):
super(BufferDeletionAction, self).__init__(buf, offset, text)
self.undo = self.insert
self.redo = self.delete
-
-################################################################################
-#
-# BufferModifiedAction
-#
-################################################################################
-class BufferModifiedAction(object):
- """A helper to set modified flag on a text buffer"""
- def __init__(self, buf, app):
- self.buffer, self.app = buf, app
- self.app.set_buffer_modified(self.buffer, 1)
- def undo(self):
- self.app.set_buffer_modified(self.buffer, 0)
- def redo(self):
- self.app.set_buffer_modified(self.buffer, 1)
diff --git a/meld/undo.py b/meld/undo.py
index c0dac55..ad50993 100644
--- a/meld/undo.py
+++ b/meld/undo.py
@@ -51,7 +51,8 @@ class UndoSequence(gobject.GObject):
__gsignals__ = {
'can-undo': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
- 'can-redo': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,))
+ 'can-redo': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
+ 'checkpointed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT, gobject.TYPE_BOOLEAN,)),
}
def __init__(self):
@@ -60,6 +61,7 @@ class UndoSequence(gobject.GObject):
gobject.GObject.__init__(self)
self.actions = []
self.next_redo = 0
+ self.checkpoints = {}
self.group = None
def clear(self):
@@ -79,6 +81,7 @@ class UndoSequence(gobject.GObject):
self.emit('can-redo', 0)
self.actions = []
self.next_redo = 0
+ self.checkpoints = {}
self.group = None
def can_undo(self):
@@ -100,6 +103,15 @@ class UndoSequence(gobject.GObject):
which are called by this sequence during an undo or redo.
"""
if self.group is None:
+ if self.checkpointed(action.buffer):
+ self.checkpoints[action.buffer][1] = self.next_redo
+ self.emit('checkpointed', action.buffer, False)
+ else:
+ # If we go back in the undo stack before the checkpoint starts,
+ # and then modify the buffer, we lose the checkpoint altogether
+ start, end = self.checkpoints.get(action.buffer, (None, None))
+ if start is not None and start > self.next_redo:
+ self.checkpoints[action.buffer] = (None, None)
could_undo = self.can_undo()
could_redo = self.can_redo()
self.actions[self.next_redo:] = []
@@ -118,6 +130,9 @@ class UndoSequence(gobject.GObject):
Raises an AssertionError if the sequence is not undoable.
"""
assert self.next_redo > 0
+ buf = self.actions[self.next_redo - 1].buffer
+ if self.checkpointed(buf):
+ self.emit('checkpointed', buf, False)
could_redo = self.can_redo()
self.next_redo -= 1
self.actions[self.next_redo].undo()
@@ -125,6 +140,8 @@ class UndoSequence(gobject.GObject):
self.emit('can-undo', 0)
if not could_redo:
self.emit('can-redo', 1)
+ if self.checkpointed(buf):
+ self.emit('checkpointed', buf, True)
def redo(self):
"""Redo an action.
@@ -132,6 +149,9 @@ class UndoSequence(gobject.GObject):
Raises and AssertionError if the sequence is not undoable.
"""
assert self.next_redo < len(self.actions)
+ buf = self.actions[self.next_redo].buffer
+ if self.checkpointed(buf):
+ self.emit('checkpointed', buf, False)
could_undo = self.can_undo()
a = self.actions[self.next_redo]
self.next_redo += 1
@@ -140,6 +160,28 @@ class UndoSequence(gobject.GObject):
self.emit('can-undo', 1)
if not self.can_redo():
self.emit('can-redo', 0)
+ if self.checkpointed(buf):
+ self.emit('checkpointed', buf, True)
+
+ def checkpoint(self, buf):
+ start = self.next_redo
+ while start > 0 and self.actions[start - 1].buffer != buf:
+ start -= 1
+ end = self.next_redo
+ while end < len(self.actions) and self.actions[end + 1].buffer != buf:
+ end += 1
+ if end == len(self.actions):
+ end = None
+ self.checkpoints[buf] = [start, end]
+ self.emit('checkpointed', buf, True)
+
+ def checkpointed(self, buf):
+ # While the main undo sequence should always have checkpoints
+ # recorded, grouped subsequences won't.
+ start, end = self.checkpoints.get(buf, (None, None))
+ if start is None:
+ return False
+ return start <= self.next_redo <= (end or len(self.actions))
def begin_group(self):
"""Group several actions into a single logical action.
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]