[ease] [editor] Undo add/remove Elements.



commit 328287cdc8df551d780eebfd073773e7b1da0718
Author: Nate Stedman <natesm gmail com>
Date:   Sun Jul 25 04:54:33 2010 -0400

    [editor] Undo add/remove Elements.
    
    - Elements can be deleted.
    - Element deletion/additon can be undone
    - SlideActor and EditorEmbed automatically manage
      the addition and removal of Elements.

 Makefile.am                        |    1 +
 data/ui/editor-window.ui           |    1 +
 src/ease-editor-embed.vala         |  110 +++++++++++++++++++++++++-----------
 src/ease-editor-window.vala        |   18 +++++-
 src/ease-slide-actor.vala          |   50 ++++++++++++++++
 src/ease-slide.vala                |   50 ++++++++++++++++-
 src/ease-undo-actions-element.vala |   93 ++++++++++++++++++++++++++++++
 src/ease-undo-item.vala            |    2 +-
 8 files changed, 285 insertions(+), 40 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 22683b2..265fc01 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -50,6 +50,7 @@ ease_SOURCES = \
 	src/ease-transition-pane.vala \
 	src/ease-transitions.vala \
 	src/ease-undo-action.vala \
+	src/ease-undo-actions-element.vala \
 	src/ease-undo-controller.vala \
 	src/ease-undo-item.vala \
 	src/ease-undo-source.vala \
diff --git a/data/ui/editor-window.ui b/data/ui/editor-window.ui
index 9827465..8dc114c 100644
--- a/data/ui/editor-window.ui
+++ b/data/ui/editor-window.ui
@@ -154,6 +154,7 @@
                     <property name="visible">True</property>
                     <property name="use_underline">True</property>
                     <property name="use_stock">True</property>
+                    <signal name="activate" handler="ease_editor_window_on_delete"/>
                   </object>
                 </child>
               </object>
diff --git a/src/ease-editor-embed.vala b/src/ease-editor-embed.vala
index 7d8208c..da0cda4 100644
--- a/src/ease-editor-embed.vala
+++ b/src/ease-editor-embed.vala
@@ -249,26 +249,13 @@ public class Ease.EditorEmbed : ScrollableEmbed
 				a.button_release_event.disconnect(actor_released);
 				a.reactive = false;
 			}
+			
+			slide_actor.ease_actor_added.disconnect(on_ease_actor_added);
+			slide_actor.ease_actor_removed.disconnect(on_ease_actor_removed);
 		}
 		
 		// remove the selection rectangle
-		if (selection_rectangle != null)
-		{
-			if (selection_rectangle.get_parent() == contents)
-			{
-				contents.remove_actor(selection_rectangle);
-			}
-			foreach (var h in handles)
-			{
-				if (h.get_parent() == contents)
-				{
-					contents.remove_actor(h);
-				}
-				h.button_press_event.disconnect(handle_clicked);
-				h.button_release_event.disconnect(handle_released);
-			}
-			handles = null;
-		}
+		remove_selection_rect();
 		
 		// create a new SlideActor
 		slide_actor = new SlideActor.from_slide(document,
@@ -284,9 +271,36 @@ public class Ease.EditorEmbed : ScrollableEmbed
 			a.reactive = true;
 		}
 		
+		slide_actor.ease_actor_added.connect(on_ease_actor_added);
+		slide_actor.ease_actor_removed.connect(on_ease_actor_removed);
+		
 		contents.add_actor(slide_actor);
 		reposition_group();
 	}
+	
+	/**
+	 * Removes the selection rectangle and handles.
+	 */
+	private void remove_selection_rect()
+	{
+		if (selection_rectangle != null)
+		{
+			if (selection_rectangle.get_parent() == contents)
+			{
+				contents.remove_actor(selection_rectangle);
+			}
+			foreach (var h in handles)
+			{
+				if (h.get_parent() == contents)
+				{
+					contents.remove_actor(h);
+				}
+				h.button_press_event.disconnect(handle_clicked);
+				h.button_release_event.disconnect(handle_released);
+			}
+			handles = null;
+		}
+	}
 
 	/**
 	 * Repositions the EditorEmbed's { link SlideActor}.
@@ -314,7 +328,7 @@ public class Ease.EditorEmbed : ScrollableEmbed
 		                            ? height / 2 - h / 2
 		                            : 0);
 		              
-		if (selection_rectangle != null)
+		if (selection_rectangle != null && selected != null)
 		{
 			position_selection();
 		}
@@ -409,21 +423,8 @@ public class Ease.EditorEmbed : ScrollableEmbed
 	 */
 	private void select_actor(Actor sender)
 	{
-		// if editing another Actor, finish that edit
-		if (selected != null && is_editing)
-		{
-			selected.end_edit(this);
-			is_editing = false;
-		}
-		
-		// remove the selection rectangle and handles
-		if (selection_rectangle != null)
-		{
-			if (selection_rectangle.get_parent() == contents)
-			{
-				contents.remove_actor(selection_rectangle);
-			}
-		}
+		// deselect anything that is currently selected
+		deselect_actor();
 		
 		selected = sender as Actor;
 		
@@ -453,6 +454,27 @@ public class Ease.EditorEmbed : ScrollableEmbed
 	}
 	
 	/**
+	 * Deselects the currently selected { link Actor}.
+	 *
+	 * This method is safe to call if nothing is selected.
+	 */
+	private void deselect_actor()
+	{
+		// if editing another Actor, finish that edit
+		if (selected != null && is_editing)
+		{
+			selected.end_edit(this);
+			is_editing = false;
+		}
+		
+		// deselect
+		selected = null;
+		
+		// remove the selection rectangle and handles
+		remove_selection_rect();
+	}
+	
+	/**
 	 * Signal handler for releasing an { link Actor}.
 	 * 
 	 * This handler is attached to the button_release_event of all
@@ -639,5 +661,27 @@ public class Ease.EditorEmbed : ScrollableEmbed
 		if (selected == null) return;	
 		if (!selected.element.set_color(color)) return;
 	}
+	
+	/**
+	 * Handles { link SlideActor.on_ease_actor_removed}. Deselects the current
+	 * { link Actor} if necessary, and disconnects handlers.
+	 */
+	public void on_ease_actor_removed(Actor actor)
+	{
+		if (selected == actor) deselect_actor();
+		actor.button_press_event.disconnect(actor_clicked);
+		actor.button_release_event.disconnect(actor_released);
+		actor.reactive = false;
+	}
+	
+	/**
+	 * Handles { link SlideActor.on_ease_actor_added}. Connects handlers.
+	 */
+	public void on_ease_actor_added(Actor actor)
+	{
+		actor.button_press_event.connect(actor_clicked);
+		actor.button_release_event.connect(actor_released);
+		actor.reactive = true;
+	}
 }
 
diff --git a/src/ease-editor-window.vala b/src/ease-editor-window.vala
index 651b04c..ee16187 100644
--- a/src/ease-editor-window.vala
+++ b/src/ease-editor-window.vala
@@ -255,6 +255,16 @@ public class Ease.EditorWindow : Gtk.Window
 	{
 		Gtk.main_quit ();
 	}
+	
+	[CCode (instance_pos = -1)]
+	public void on_delete(Gtk.Widget sender)
+	{
+		if (embed.selected == null) return;
+		
+		var i = slide.index_of(embed.selected.element);
+		add_undo_action(new ElementRemoveUndoAction(slide.element_at(i)));
+		slide.remove_at(i);
+	}
 
 	[CCode (instance_pos = -1)]
 	public void new_slide_handler(Gtk.Widget? sender)
@@ -330,8 +340,8 @@ public class Ease.EditorWindow : Gtk.Window
 		var text = document.theme.create_custom_text();
 		text.x = document.width / 2 - text.width / 2;
 		text.y = document.height / 2 - text.height / 2;
-		slide.add_element(0, text);
-		embed.recreate_slide();
+		slide.add(text);
+		add_undo_action(new ElementAddUndoAction(text));
 		embed.select_element(text);
 	}
 	
@@ -366,8 +376,8 @@ public class Ease.EditorWindow : Gtk.Window
 				e.filename = document.add_media_file(dialog.get_filename());
 				
 				// add the element
-				slide.add_element(0, e);
-				embed.recreate_slide();
+				slide.add(e);
+				add_undo_action(new ElementAddUndoAction(e));
 				embed.select_element(e);
 			}
 			catch (Error e)
diff --git a/src/ease-slide-actor.vala b/src/ease-slide-actor.vala
index 64df96e..72e3cd4 100644
--- a/src/ease-slide-actor.vala
+++ b/src/ease-slide-actor.vala
@@ -120,6 +120,16 @@ public class Ease.SlideActor : Clutter.Group
 	private const int REFLECTION_OPACITY = 70;
 	
 	/**
+	 * Emitted when a subactor of this SlideActor is removed.
+	 */
+	public signal void ease_actor_removed(Actor actor);
+	
+	/**
+	 * Emitted when a subactor is added to this SlideActor.
+	 */
+	public signal void ease_actor_added(Actor actor);
+	
+	/**
 	 * Creates a SlideActor from a { link Slide} and a { link Document}.
 	 * This calls the with_dimensions() constructor with the Document's
 	 * dimensions.
@@ -174,6 +184,9 @@ public class Ease.SlideActor : Clutter.Group
 		add_actor(contents);
 		
 		slide.background_changed.connect((s) => set_background());
+		
+		slide.element_added.connect(on_element_added);
+		slide.element_removed.connect(on_element_removed);
 	}
 	
 	/**
@@ -198,6 +211,43 @@ public class Ease.SlideActor : Clutter.Group
 	}
 	
 	/**
+	 * Handles { link Slide.element_added}.
+	 */
+	public void on_element_added(Slide slide, Element element, int index)
+	{
+		var actor = element.actor(context);
+		contents.add_actor(actor);
+		contents.lower_child(actor, null);
+		
+		// raise the actor to its proper position
+		int i = 0;
+		Clutter.Actor raise;
+		foreach (var a in contents)
+		{
+			if (i >= index) break;
+			raise = a;
+		}
+		
+		ease_actor_added(actor as Actor);
+	}
+	
+	/**
+	 * Handles { link Slide.element_removed}.
+	 */
+	public void on_element_removed(Slide slide, Element element, int index)
+	{
+		foreach (var a in contents)
+		{
+			if ((a as Actor).element == element)
+			{
+				contents.remove_actor(a);
+				ease_actor_removed(a as Actor);
+				break;
+			}
+		}
+	}
+	
+	/**
 	 * Resets all transformations on this SlideActor.
 	 */
 	public void reset(Clutter.Group container)
diff --git a/src/ease-slide.vala b/src/ease-slide.vala
index 2e85347..aefea96 100644
--- a/src/ease-slide.vala
+++ b/src/ease-slide.vala
@@ -208,6 +208,16 @@ public class Ease.Slide : GLib.Object
 	public signal void background_changed(Slide self);
 	
 	/**
+	 * Emitted when an { link Element} is added to this Slide.
+	 */
+	public signal void element_added(Slide self, Element element, int index);
+	
+	/**
+	 * Emitted when an { link Element} is added to this Slide.
+	 */
+	public signal void element_removed(Slide self, Element element, int index);
+	
+	/**
 	 * Create a new Slide.
 	 */
 	public Slide()
@@ -358,6 +368,7 @@ public class Ease.Slide : GLib.Object
 	{
 		e.parent = this;
 		elements.insert(index, e);
+		element_added(this, e, index);
 	}
 	
 	/**
@@ -367,8 +378,43 @@ public class Ease.Slide : GLib.Object
 	 */
 	public void add(Element e)
 	{
-		e.parent = this;
-		elements.insert(count, e);
+		add_element(count, e);
+	}
+	
+	/**
+	 * Removes an { link Element} from this slide.
+	 */
+	public void remove_element(Element e)
+	{
+		var index = index_of(e);
+		elements.remove(e);
+		element_removed(this, e, index); 
+	}
+	
+	/**
+	 * Removed an { link Element} from this slide, by index.
+	 */
+	public void remove_at(int index)
+	{
+		var e = elements.get(index);
+		elements.remove_at(index);
+		element_removed(this, e, index);
+	}
+	
+	/**
+	 * Returns the index of the specified { link Element}
+	 */
+	public int index_of(Element e)
+	{
+		return elements.index_of(e);
+	}
+	
+	/**
+	 * Returns the { link Element} at the specified index.
+	 */
+	public Element element_at(int i)
+	{
+		return elements.get(i);
 	}
 	
 	/** 
diff --git a/src/ease-undo-actions-element.vala b/src/ease-undo-actions-element.vala
new file mode 100644
index 0000000..96f0061
--- /dev/null
+++ b/src/ease-undo-actions-element.vala
@@ -0,0 +1,93 @@
+/*  Ease, a GTK presentation application
+    Copyright (C) 2010 Nate Stedman
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * Undos the addition of an { link Element} to a { link Slide}.
+ */
+public class Ease.ElementAddUndoAction : UndoItem
+{
+	/**
+	 * The { link Element} that was added.
+	 */
+	private Element element;
+	
+	/**
+	 * Creates an ElementAddUndoAction.
+	 *
+	 * @param e The element that was added.
+	 */
+	public ElementAddUndoAction(Element e)
+	{
+		element = e;
+	}
+	
+	/**
+	 * Applies the action, removing the { link Element}.
+	 */
+	public override UndoItem apply()
+	{
+		var action = new ElementRemoveUndoAction(element);
+		element.parent.remove_element(element);
+		return action;
+	}
+}
+
+/**
+ * Undos the removal of an { link Element} from a { link Slide}.
+ */
+public class Ease.ElementRemoveUndoAction : UndoItem
+{
+	/**
+	 * The { link Element} that was removed.
+	 */
+	private Element element;
+	
+	/**
+	 * The { link Slide} that the Element was removed from.
+	 */
+	private Slide slide;
+	
+	/**
+	 * The index of the Element in the Slide's stack.
+	 */
+	int index;
+	
+	/**
+	 * Creates an ElementRemoveUndoAction. Note that this method references
+	 * { link Element.parent}. Therefore, the action must be constructed
+	 * before the Element is actually removed.
+	 *
+	 * @param e The element that was added.
+	 */
+	public ElementRemoveUndoAction(Element e)
+	{
+		element = e;
+		slide = e.parent;
+		index = e.parent.index_of(e);
+	}
+	
+	/**
+	 * Applies the action, restoring the { link Element}.
+	 */
+	public override UndoItem apply()
+	{
+		slide.add_element(index, element);
+		return new ElementAddUndoAction(element);
+	}
+}
+
+
diff --git a/src/ease-undo-item.vala b/src/ease-undo-item.vala
index 0b12c0c..d5faf4a 100644
--- a/src/ease-undo-item.vala
+++ b/src/ease-undo-item.vala
@@ -29,7 +29,7 @@ public abstract class Ease.UndoItem : GLib.Object
 	public signal void applied(UndoAction sender);
 	
 	/**
-	 * Applies the { link Item}, restoring previous state.
+	 * Applies the { link UndoItem}, restoring previous state.
 	 *
 	 * Returns an UndoItem that will redo the undo action.
 	 */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]