changeset 47:14f1c051c35b

Moved undo into core.
author daveb
date Tue, 03 Aug 2010 16:57:06 +0930
parents ad3ba55ae57b
children 1b4c9ba58673
files doodle/core/undo.d doodle/undo/undo.d
diffstat 2 files changed, 148 insertions(+), 148 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doodle/core/undo.d	Tue Aug 03 16:57:06 2010 +0930
@@ -0,0 +1,148 @@
+module doodle.core.undo;
+
+import std.array;
+import std.date;
+
+// An abstract framework for undo/redo.
+// Assume the application works on one document at a time,
+// therefore a single undo/redo history.
+// Each change to the document is modelled as an Edit.
+// Previous edits are represented by an undo stack
+// and can be undone in order.
+// As edits are undone the are placed on a redo stack
+// and can be redone in order.
+// As edits are redone the are placed on the undo stack, etc.
+//
+// When a new edit is made an attempt to merge it with
+// the previous Edit is made. For example, typing characters
+// in short succession generates many Edits that may be
+// merged into one Edit.
+// When a new edit is made the redo stack is cleared.
+//
+// Application code must generate Edits from user interaction.
+// Typically application code will receive some input event and:
+// * Attempt to perform an action,
+// * If the action succeeds then encapsulate the action
+//   in an Edit and add it to the undo manager.
+// Note, not all interaction results in Edits, for example,
+// changing the view, zooming/scrolling, etc are not edits
+// and do not affect undo/redo
+
+abstract class Edit {
+    private {
+        string _description;
+        d_time _timeStamp;
+    }
+
+    this(in string description, d_time timeStamp) {
+        assert(description);
+        _description = description;
+        _timeStamp = timeStamp;
+    }
+
+    string description() const { return _description; }
+    d_time timeStamp() const { return _timeStamp; }
+
+    final bool merge(Edit subsequent) {
+        if (mergeImpl(subsequent)) {
+            // Adopt the new timestamp and description
+            _timeStamp = subsequent._timeStamp;
+            _description = subsequent._description;
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    void undo();
+    void redo();
+    protected bool mergeImpl(Edit other) { return false; }
+}
+
+interface IUndoManagerObserver {
+    // Each description is null if the associated bool is false
+    void undoRedoUpdate(in bool canUndo, in string undoDescription,
+                        in bool canRedo, in string redoDescription);
+}
+
+interface IUndoManager {
+    void addEdit(Edit edit);
+    void undo();
+    void redo();
+    void reset();
+
+    void addObserver(IUndoManagerObserver observer);
+    void removeObserver(IUndoManagerObserver observer);
+}
+
+class UndoManager : IUndoManager {
+    this(int maxUndoLevel = -1) {
+        _maxUndoLevel = maxUndoLevel;
+    }
+
+    void addEdit(Edit edit) {
+        _redoEdits.length = 0;
+
+        if (_undoEdits.empty || !_undoEdits.back.merge(edit)) {
+            _undoEdits ~= edit;
+            if (_maxUndoLevel >= 0 && _undoEdits.length > _maxUndoLevel) {
+                _undoEdits.length = _undoEdits.length - 1;
+            }
+        }
+
+        notifyObservers();
+    }
+
+    void undo() {
+        assert(canUndo);
+        auto edit = _undoEdits.back;
+        edit.undo;
+        _undoEdits.popBack;
+        _redoEdits ~= edit;
+
+        notifyObservers();
+    }
+
+    void redo() {
+        assert(canRedo);
+        auto edit = _redoEdits.back;
+        edit.redo;
+        _redoEdits.popBack;
+        _undoEdits ~= edit;
+
+        notifyObservers();
+    }
+
+    bool canUndo() const { return !_undoEdits.empty; }
+    bool canRedo() const { return !_redoEdits.empty; }
+
+    void reset() {
+        _undoEdits.length = _redoEdits.length = 0;
+        notifyObservers();
+    }
+
+    void addObserver(IUndoManagerObserver observer) {
+        _observers ~= observer;
+    }
+
+    void removeObserver(IUndoManagerObserver observer) {
+        // NYI
+    }
+
+    // IUndoManager overrides:
+
+    private {
+        int _maxUndoLevel;
+        Edit[] _undoEdits;
+        Edit[] _redoEdits;
+        IUndoManagerObserver[] _observers;          // FIXME, use a different container
+
+        void notifyObservers() {
+            foreach (o; _observers) {
+                o.undoRedoUpdate(canUndo, canUndo ? _undoEdits.back.description : null,
+                                 canRedo, canRedo ? _redoEdits.back.description : null);
+            }
+        }
+    }
+}
--- a/doodle/undo/undo.d	Tue Aug 03 16:55:45 2010 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-module doodle.undo.undo;
-
-import std.array;
-import std.date;
-
-// An abstract framework for undo/redo.
-// Assume the application works on one document at a time,
-// therefore a single undo/redo history.
-// Each change to the document is modelled as an Edit.
-// Previous edits are represented by an undo stack
-// and can be undone in order.
-// As edits are undone the are placed on a redo stack
-// and can be redone in order.
-// As edits are redone the are placed on the undo stack, etc.
-//
-// When a new edit is made an attempt to merge it with
-// the previous Edit is made. For example, typing characters
-// in short succession generates many Edits that may be
-// merged into one Edit.
-// When a new edit is made the redo stack is cleared.
-//
-// Application code must generate Edits from user interaction.
-// Typically application code will receive some input event and:
-// * Attempt to perform an action,
-// * If the action succeeds then encapsulate the action
-//   in an Edit and add it to the undo manager.
-// Note, not all interaction results in Edits, for example,
-// changing the view, zooming/scrolling, etc are not edits
-// and do not affect undo/redo
-
-abstract class Edit {
-    private {
-        string _description;
-        d_time _timeStamp;
-    }
-
-    this(in string description, d_time timeStamp) {
-        assert(description);
-        _description = description;
-        _timeStamp = timeStamp;
-    }
-
-    string description() const { return _description; }
-    d_time timeStamp() const { return _timeStamp; }
-
-    final bool merge(Edit subsequent) {
-        if (mergeImpl(subsequent)) {
-            // Adopt the new timestamp and description
-            _timeStamp = subsequent._timeStamp;
-            _description = subsequent._description;
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    void undo();
-    void redo();
-    protected bool mergeImpl(Edit other) { return false; }
-}
-
-interface IUndoManagerObserver {
-    // Each description is null if the associated bool is false
-    void undoRedoUpdate(in bool canUndo, in string undoDescription,
-                        in bool canRedo, in string redoDescription);
-}
-
-interface IUndoManager {
-    void addEdit(Edit edit);
-    void undo();
-    void redo();
-    void reset();
-
-    void addObserver(IUndoManagerObserver observer);
-    void removeObserver(IUndoManagerObserver observer);
-}
-
-class UndoManager : IUndoManager {
-    this(int maxUndoLevel = -1) {
-        _maxUndoLevel = maxUndoLevel;
-    }
-
-    void addEdit(Edit edit) {
-        _redoEdits.length = 0;
-
-        if (_undoEdits.empty || !_undoEdits.back.merge(edit)) {
-            _undoEdits ~= edit;
-            if (_maxUndoLevel >= 0 && _undoEdits.length > _maxUndoLevel) {
-                _undoEdits.length = _undoEdits.length - 1;
-            }
-        }
-
-        notifyObservers();
-    }
-
-    void undo() {
-        assert(canUndo);
-        auto edit = _undoEdits.back;
-        edit.undo;
-        _undoEdits.popBack;
-        _redoEdits ~= edit;
-
-        notifyObservers();
-    }
-
-    void redo() {
-        assert(canRedo);
-        auto edit = _redoEdits.back;
-        edit.redo;
-        _redoEdits.popBack;
-        _undoEdits ~= edit;
-
-        notifyObservers();
-    }
-
-    bool canUndo() const { return !_undoEdits.empty; }
-    bool canRedo() const { return !_redoEdits.empty; }
-
-    void reset() {
-        _undoEdits.length = _redoEdits.length = 0;
-        notifyObservers();
-    }
-
-    void addObserver(IUndoManagerObserver observer) {
-        _observers ~= observer;
-    }
-
-    void removeObserver(IUndoManagerObserver observer) {
-        // NYI
-    }
-
-    // IUndoManager overrides:
-
-    private {
-        int _maxUndoLevel;
-        Edit[] _undoEdits;
-        Edit[] _redoEdits;
-        IUndoManagerObserver[] _observers;          // FIXME, use a different container
-
-        void notifyObservers() {
-            foreach (o; _observers) {
-                o.undoRedoUpdate(canUndo, canUndo ? _undoEdits.back.description : null,
-                                 canRedo, canRedo ? _redoEdits.back.description : null);
-            }
-        }
-    }
-}