[ease] [editor] Allow the user to change backgrounds



commit 31ee76fcdca1b426c84497eb46b7438ca7ea67f4
Author: Nate Stedman <natesm gmail com>
Date:   Sat Jul 24 17:18:46 2010 -0400

    [editor] Allow the user to change backgrounds
    
    - Added slide/background pane with GtkBuilder
    - Undo support for all background changes
    - Load images as a background

 Makefile.am                  |    3 +-
 data/themes/build.sh         |    2 +-
 data/ui/inspector-slide.ui   |  315 +++++++++++++++++++++++++++++++++++++
 src/ease-color.vala          |   42 +++++-
 src/ease-editor-window.vala  |    6 +-
 src/ease-gradient.vala       |   55 +++++++
 src/ease-inspector-pane.vala |   15 +--
 src/ease-inspector.vala      |   19 ++-
 src/ease-json-parser.vala    |    8 +-
 src/ease-slide-actor.vala    |    7 +-
 src/ease-slide-pane.vala     |  351 ++++++++++++++++++++++++++++++++++++++----
 src/ease-slide.vala          |   63 ++++----
 src/ease-theme.vala          |    3 +
 src/ease-undo-action.vala    |   16 ++
 src/ease-undo-source.vala    |   29 ++++
 src/ease-utilities.vala      |    9 +
 16 files changed, 851 insertions(+), 92 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 6fee01c..823b546 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,7 @@ ease_SOURCES = \
 	src/ease-transitions.vala \
 	src/ease-undo-action.vala \
 	src/ease-undo-controller.vala \
+	src/ease-undo-source.vala \
 	src/ease-utilities.vala \
 	src/ease-welcome-actor.vala \
 	src/ease-welcome-window.vala \
@@ -95,7 +96,7 @@ doc: src/*.vala
 	valadoc --internal --private --pkg "json-glib-1.0" --pkg "gee-1.0"  --pkg "clutter-gtk-0.10" --pkg "libarchive" --directory=./doc --basedir=src ./src/*.vala
 	gnome-open doc/doc/Ease.html
 
-archive: themes/* examples/*
+archive: data/themes/* examples/*
 	sh data/themes/build.sh
 	sh examples/build.sh
 
diff --git a/data/themes/build.sh b/data/themes/build.sh
index 91546aa..ae8b6e7 100755
--- a/data/themes/build.sh
+++ b/data/themes/build.sh
@@ -2,7 +2,7 @@
 
 echo "  Archiving themes..."
 
-cd themes
+cd data/themes
 
 for THEME in `find ./* -maxdepth 0 -type d | sed "s/.\///g"`
 	do
diff --git a/data/ui/inspector-slide.ui b/data/ui/inspector-slide.ui
new file mode 100644
index 0000000..b4dcbab
--- /dev/null
+++ b/data/ui/inspector-slide.ui
@@ -0,0 +1,315 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkAlignment" id="root">
+    <property name="visible">True</property>
+    <property name="top_padding">4</property>
+    <property name="bottom_padding">4</property>
+    <property name="left_padding">4</property>
+    <property name="right_padding">4</property>
+    <child>
+      <object class="GtkVBox" id="vbox-root">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">10</property>
+        <child>
+          <object class="GtkVBox" id="vbox-style">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkHBox" id="hbox-style">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkLabel" id="style-label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Background Style</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="combobox-style">
+                <property name="height_request">30</property>
+                <property name="visible">True</property>
+                <signal name="changed" handler="ease_slide_pane_on_background_changed"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox-color">
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkHBox" id="hbox2">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkLabel" id="color-label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Background Color</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkColorButton" id="color-color">
+                <property name="height_request">30</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="color">#000000000000</property>
+                <signal name="color_set" handler="ease_slide_pane_on_color_set"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox-gradient">
+            <property name="orientation">vertical</property>
+            <property name="spacing">4</property>
+            <child>
+              <object class="GtkHBox" id="hbox3">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkLabel" id="gradient-label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Background Gradient</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox4">
+                <property name="visible">True</property>
+                <property name="spacing">4</property>
+                <child>
+                  <object class="GtkColorButton" id="color-startgradient">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="color">#000000000000</property>
+                    <signal name="color_set" handler="ease_slide_pane_on_color_set"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkColorButton" id="color-endgradient">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="color">#000000000000</property>
+                    <signal name="color_set" handler="ease_slide_pane_on_color_set"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBox" id="combo-gradient">
+                    <property name="height_request">30</property>
+                    <property name="visible">True</property>
+                    <signal name="changed" handler="ease_slide_pane_on_gradient_type_changed"/>
+                    <child>
+                      <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                      <attributes>
+                        <attribute name="text">0</attribute>
+                      </attributes>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button-reverse">
+                <property name="label" translatable="yes">Reverse Gradient</property>
+                <property name="height_request">30</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="xalign">0</property>
+                <signal name="clicked" handler="ease_slide_pane_on_reverse_gradient"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">2</property>
+                <child>
+                  <object class="GtkHBox" id="hbox5">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="label-angle">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Gradient Angle</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHScale" id="hscale-angle">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="adjustment">adjustment-angle</property>
+                    <property name="draw_value">False</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="padding">10</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox-image">
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkLabel" id="image-label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Background Image</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFileChooserButton" id="button-image">
+                <property name="height_request">30</property>
+                <property name="visible">True</property>
+                <signal name="file_set" handler="ease_slide_pane_on_file_set"/>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="adjustment-angle">
+    <property name="upper">6.2800000000000002</property>
+    <property name="step_increment">0.10000000000000001</property>
+    <property name="page_increment">1</property>
+    <property name="page_size">1</property>
+  </object>
+</interface>
diff --git a/src/ease-color.vala b/src/ease-color.vala
index e0b0fc2..0ac068f 100644
--- a/src/ease-color.vala
+++ b/src/ease-color.vala
@@ -29,6 +29,22 @@ public class Ease.Color : GLib.Object
 	 * The string placed between each channel in a string representation.
 	 */
 	private const string SPLIT = ",";
+	
+	/**
+	 * A color with the values (1, 1, 1, 1).
+	 */
+	public static Color white
+	{
+		owned get { return new Color.rgb(1, 1, 1); }
+	}
+	
+	/**
+	 * A color with the values (0, 0, 0, 1).
+	 */
+	public static Color black
+	{
+		owned get { return new Color.rgb(0, 0, 0); }
+	}
 
 	/**
 	 * The red value of this color.
@@ -155,7 +171,7 @@ public class Ease.Color : GLib.Object
 		get
 		{
 			return { 0,
-			         (uint16)(255 * red),
+			         (uint16)(65535 * red),
 			         (uint16)(65535 * green),
 			         (uint16)(65535 * blue) };
 		}
@@ -234,7 +250,15 @@ public class Ease.Color : GLib.Object
 	 */
 	public string to_string()
 	{
-		return STR.printf(red, SPLIT, blue, SPLIT, green, SPLIT, alpha);
+		return STR.printf(red, SPLIT, green, SPLIT, blue, SPLIT, alpha);
+	}
+	
+	/**
+	 * Returns a copy of this Color
+	 */
+	public Color copy()
+	{
+		return new Color.rgba(red, green, blue, alpha);
 	}
 	
 	/**
@@ -246,4 +270,18 @@ public class Ease.Color : GLib.Object
 	{
 		cr.set_source_rgba(red, green, blue, alpha);
 	}
+	
+	/**
+	 * Returns an { link UndoAction} that will restore this Color to its current
+	 * state.
+	 */
+	public UndoAction undo_action()
+	{
+		var action = new UndoAction(this, "red");
+		action.add(this, "green");
+		action.add(this, "blue");
+		action.add(this, "alpha");
+		
+		return action;
+	}
 }
diff --git a/src/ease-editor-window.vala b/src/ease-editor-window.vala
index 8d0d9b3..0fd3bd2 100644
--- a/src/ease-editor-window.vala
+++ b/src/ease-editor-window.vala
@@ -152,6 +152,7 @@ public class Ease.EditorWindow : Gtk.Window
 		// the inspector
 		inspector = new Inspector();
 		(builder.get_object("Inspector Align") as Gtk.Alignment).add(inspector);
+		inspector.undo.connect((action) => add_undo_action(action));
 		
 		// main editor
 		embed = new EditorEmbed(document, this);
@@ -175,8 +176,9 @@ public class Ease.EditorWindow : Gtk.Window
 		menu.show_all();
 		
 		// final window setup
-		show_all();
-		embed.show();
+		slide_button_panel.show_all();
+		embed.show_all();
+		show();
 		inspector.hide();
 		slides_shown = true;
 		
diff --git a/src/ease-gradient.vala b/src/ease-gradient.vala
index 80d9ca6..6595f7e 100644
--- a/src/ease-gradient.vala
+++ b/src/ease-gradient.vala
@@ -51,6 +51,14 @@ public class Ease.Gradient : GLib.Object
 	public double angle { get; set; }
 	
 	/**
+	 * Returns a copy of the default background gradient.
+	 */
+	public static Gradient default_background
+	{	
+		owned get { return new Gradient(Color.black, Color.white); }
+	}
+	
+	/**
 	 * Creates a new linear gradient, with the specified colors.
 	 */
 	public Gradient(Color start_color, Color end_color)
@@ -58,6 +66,7 @@ public class Ease.Gradient : GLib.Object
 		start = start_color;
 		end = end_color;
 		mode = GradientMode.LINEAR;
+		angle = 0;
 	}
 	
 	/**
@@ -67,6 +76,7 @@ public class Ease.Gradient : GLib.Object
 	{
 		this(start_color, end_color);
 		mode = GradientMode.LINEAR_MIRRORED;
+		angle = 0;
 	}
 	
 	/**
@@ -76,6 +86,7 @@ public class Ease.Gradient : GLib.Object
 	{
 		this(start_color, end_color);
 		mode = GradientMode.RADIAL;
+		angle = 0;
 	}
 	
 	/**
@@ -102,6 +113,17 @@ public class Ease.Gradient : GLib.Object
 	}
 	
 	/**
+	 * Returns a copy of this Gradient.
+	 */
+	public Gradient copy()
+	{
+		var grad = new Gradient(start.copy(), end.copy());
+		grad.mode = mode;
+		grad.angle = angle;
+		return grad;
+	}
+	
+	/**
 	 * Reverses the Gradient.
 	 */
 	public void flip()
@@ -211,4 +233,37 @@ public enum Ease.GradientMode
 		warning("%s is not a gradient type", str);
 		return LINEAR;
 	}
+	
+	/**
+	 * Returns a string description of the GradientMode
+	 */
+	public string description()
+	{
+		switch (this)
+		{
+			case LINEAR: return _("Linear");
+			case LINEAR_MIRRORED: return _("Mirrored Linear");
+			case RADIAL: return _("Radial");
+		}
+		return "undefined";
+	}
+	
+	/**
+	 * Creates a ListStore with the first column set as the description
+	 * and the second column set as the GradientMode.
+	 */
+	public static Gtk.ListStore list_store()
+	{
+		var store = new Gtk.ListStore(2, typeof(string), typeof(GradientMode));
+		Gtk.TreeIter itr;
+		
+		store.append(out itr);
+		store.set(itr, 0, LINEAR.description(), 1, LINEAR);
+		store.append(out itr);
+		store.set(itr, 0, LINEAR_MIRRORED.description(), 1, LINEAR_MIRRORED);
+		store.append(out itr);
+		store.set(itr, 0, RADIAL.description(), 1, RADIAL);
+		
+		return store;
+	}
 }
diff --git a/src/ease-inspector-pane.vala b/src/ease-inspector-pane.vala
index a80c71a..b9a2c9d 100644
--- a/src/ease-inspector-pane.vala
+++ b/src/ease-inspector-pane.vala
@@ -18,23 +18,16 @@
 /**
  * Base class for inspector panes
  */
-public abstract class Ease.InspectorPane : Gtk.VBox
+public abstract class Ease.InspectorPane : Gtk.VBox, UndoSource
 {
-	private Slide slide_priv;
-
-	public Slide slide
-	{
-		get { return slide_priv; }
-		set {
-			slide_priv = value;
-			slide_updated();
-		}
-	}
+	public Slide slide { get; set; }
 
 	public InspectorPane()
 	{
 		homogeneous = false;
 		spacing = 0;
+		
+		notify["slide"].connect((a, b) => slide_updated());
 	}
 	
 	/**
diff --git a/src/ease-inspector.vala b/src/ease-inspector.vala
index 2a43815..9a85051 100644
--- a/src/ease-inspector.vala
+++ b/src/ease-inspector.vala
@@ -18,7 +18,7 @@
 /**
  * Inspector widget for editing slide properties
  */
-public class Ease.Inspector : Gtk.Notebook
+public class Ease.Inspector : Gtk.Notebook, UndoSource
 {
 	private TransitionPane transition_pane;
 	private SlidePane slide_pane;
@@ -50,12 +50,17 @@ public class Ease.Inspector : Gtk.Notebook
 		slide_pane = new SlidePane();
 		
 		// add pages
-		append_page(slide_pane,
-		            new Gtk.Image.from_stock("gtk-page-setup",
-		                                     Gtk.IconSize.SMALL_TOOLBAR));
-		append_page(transition_pane,
-		            new Gtk.Image.from_stock("gtk-media-forward",
-		                                     Gtk.IconSize.SMALL_TOOLBAR));
+		append(slide_pane, "gtk-page-setup");
+		append(transition_pane, "gtk-media-forward");
+		slide_pane.show();
+		transition_pane.show_all();
+	}
+	
+	private void append(InspectorPane i, string stock_id)
+	{
+		append_page(i, new Gtk.Image.from_stock(stock_id,
+		                                        Gtk.IconSize.SMALL_TOOLBAR));
+		i.undo.connect((action) => undo(action));
 	}
 }
 
diff --git a/src/ease-json-parser.vala b/src/ease-json-parser.vala
index cbf28c1..0e66e5b 100644
--- a/src/ease-json-parser.vala
+++ b/src/ease-json-parser.vala
@@ -97,18 +97,20 @@ public static class Ease.JSONParser
 		{
 			slide.background_image =
 				obj.get_string_member(Theme.BACKGROUND_IMAGE);
+			slide.background_image_source =
+				obj.get_string_member("background-image-source");
 		}
 		if (obj.has_member(Theme.BACKGROUND_COLOR))
 		{
 			slide.background_color =
 				new Color.from_string(
-					obj.get_string_member(Theme.BACKGROUND_COLOR));
+				obj.get_string_member(Theme.BACKGROUND_COLOR));
 		}
 		if (obj.has_member(Theme.BACKGROUND_GRADIENT))
 		{
 			slide.background_gradient =
 				new Gradient.from_string(
-					obj.get_string_member(Theme.BACKGROUND_GRADIENT));
+				obj.get_string_member(Theme.BACKGROUND_GRADIENT));
 		}
 		slide.background_type = BackgroundType.from_string(
 			obj.get_string_member(Theme.BACKGROUND_TYPE));
@@ -211,6 +213,8 @@ public static class Ease.JSONParser
 		{
 			obj.set_string_member(Theme.BACKGROUND_IMAGE,
 			                      slide.background_image);
+			obj.set_string_member("background-image-source",
+			                      slide.background_image_source);
 		}
 		if (slide.background_color != null)
 		{
diff --git a/src/ease-slide-actor.vala b/src/ease-slide-actor.vala
index 0f8a87f..b2c7704 100644
--- a/src/ease-slide-actor.vala
+++ b/src/ease-slide-actor.vala
@@ -162,6 +162,7 @@ public class Ease.SlideActor : Clutter.Group
 
 		// set the background
 		set_background();
+		add_actor(background);
 
 		contents = new Clutter.Group();
 
@@ -171,6 +172,9 @@ public class Ease.SlideActor : Clutter.Group
 		}
 
 		add_actor(contents);
+		
+		// TODO: this completely _destroys_ performance.
+		slide.changed.connect((s) => set_background());
 	}
 	
 	/**
@@ -263,9 +267,6 @@ public class Ease.SlideActor : Clutter.Group
 		
 		background.width = width_px;
 		background.height = height_px;
-
-		add_actor(background);
-		lower_child(background, null);
 	}
 
 	/**
diff --git a/src/ease-slide-pane.vala b/src/ease-slide-pane.vala
index a47d8d8..2cc9dc5 100644
--- a/src/ease-slide-pane.vala
+++ b/src/ease-slide-pane.vala
@@ -20,41 +20,332 @@
  */
 public class Ease.SlidePane : InspectorPane
 {
-	public Gtk.ComboBox effect;
-	public Gtk.SpinButton duration;
-	public Gtk.ComboBox variant;
-	public Gtk.Label variant_label;
-	public Gtk.ComboBox start_transition;
-	public Gtk.SpinButton delay;
+	private const string UI_FILE_PATH = "inspector-slide.ui";
+	private const string BG_DIALOG_TITLE = _("Select Background Image");
+	
+	private Gtk.ComboBox background;
+	private Gtk.ListStore store;
+	private Gtk.ComboBox gradient_type;
+	private Gtk.ListStore grad_store = GradientMode.list_store();
+	private Gtk.VBox box_color;
+	private Gtk.VBox box_gradient;
+	private Gtk.VBox box_image;
+	
+	private Gtk.ColorButton bg_color;
+	private Gtk.ColorButton grad_color1;
+	private Gtk.ColorButton grad_color2;
+	private Gtk.FileChooserButton bg_image;
+	
+	private bool silence_undo;
 
 	public SlidePane()
 	{	
 		base();
 		
-		// effect selection
-		var vbox = new Gtk.VBox(false, 0);
-		var hbox = new Gtk.HBox(false, 0);
-		var align = new Gtk.Alignment(0, 0, 0, 0);
-		align.add(new Gtk.Label(_("Effect")));
-		vbox.pack_start(align, false, false, 0);
-		effect = new Gtk.ComboBox();
-		align = new Gtk.Alignment(0, 0, 1, 1);
-		align.add(effect);
-		vbox.pack_start(align, false, false, 0);
-		hbox.pack_start(vbox, true, true, 5);
-		
-		// effect duration
-		vbox = new Gtk.VBox(false, 0);
-		align = new Gtk.Alignment(0, 0, 0, 0);
-		align.add(new Gtk.Label(_("Duration")));
-		vbox.pack_start(align, false, false, 0);
-		duration = new Gtk.SpinButton.with_range(0, 10, 0.25);
-		duration.digits = 2;
-		align = new Gtk.Alignment(0, 0.5f, 1, 1);
-		align.add(duration);
-		vbox.pack_start(align, true, true, 0);
-		hbox.pack_start(vbox, false, false, 5);
-		pack_start(hbox, false, false, 5);
+		// load the GtkBuilder file
+		var builder = new Gtk.Builder();
+		try
+		{
+			builder.add_from_file(data_path(Path.build_filename(Temp.UI_DIR,
+				                                                UI_FILE_PATH)));
+		}
+		catch (Error e) { error("Error loading UI: %s", e.message); }
+		
+		// connect signals
+		builder.connect_signals(this);
+		
+		// add the root of the builder file to this widget
+		pack_start(builder.get_object("root") as Gtk.Widget, true, true, 0);
+		
+		// get controls
+		box_color = builder.get_object("vbox-color") as Gtk.VBox;
+		box_gradient = builder.get_object("vbox-gradient") as Gtk.VBox;
+		box_image = builder.get_object("vbox-image") as Gtk.VBox;
+		bg_color = builder.get_object("color-color") as Gtk.ColorButton;
+		grad_color1 =
+			builder.get_object("color-startgradient") as Gtk.ColorButton;
+		grad_color2 =
+			builder.get_object("color-endgradient") as Gtk.ColorButton;
+		bg_image =
+			builder.get_object("button-image") as Gtk.FileChooserButton;
+		gradient_type =
+			builder.get_object("combo-gradient") as Gtk.ComboBox;
+		
+		// set up the gradient type combobox
+		gradient_type.model = grad_store;
+		
+		// get the combobox
+		background = builder.get_object("combobox-style") as Gtk.ComboBox;
+		
+		// build a liststore for the combobox
+		store = new Gtk.ListStore(2, typeof(string), typeof(BackgroundType));
+		Gtk.TreeIter iter;
+		foreach (var b in BackgroundType.TYPES)
+		{
+			store.append(out iter);
+			store.set(iter, 0, b.description(), 1, b);
+		}
+		
+		var render = new Gtk.CellRendererText();
+		
+		background.pack_start(render, true);
+		background.set_attributes(render, "text", 0);
+		
+		background.model = store;
+	}
+	
+	private void emit_undo(UndoAction action)
+	{
+		if (!silence_undo) undo(action);
+	}
+	
+	[CCode (instance_pos = -1)]
+	public void on_background_changed(Gtk.Widget? sender)
+	{
+		Gtk.TreeIter itr;
+		store.get_iter_first(out itr);
+		
+		// find the correct position
+		for (int i = 0; i < background.active; i++) store.iter_next(ref itr);
+		
+		// get the background type at that position
+		BackgroundType type;
+		store.get(itr, 1, out type);
+		
+		// create an undo action
+		var action = new UndoAction(slide, "background-type");
+		
+		// ease can't provide a default for images, so one must be requested
+		if (type == BackgroundType.IMAGE && slide.background_image == null)
+		{
+			var dialog = new Gtk.FileChooserDialog(BG_DIALOG_TITLE,
+			                                       widget_window(this),
+			                                       Gtk.FileChooserAction.OPEN,
+			                                       "gtk-cancel",
+			                                       Gtk.ResponseType.CANCEL,
+			                                       "gtk-open",
+			                                       Gtk.ResponseType.ACCEPT);
+			switch (dialog.run())
+			{
+				case Gtk.ResponseType.ACCEPT:
+					try
+					{
+						var fname = dialog.get_filename();
+						slide.background_image_source = fname;
+						var i = slide.parent.add_media_file(fname);
+						slide.background_image = i;
+					}
+					catch (GLib.Error e)
+					{
+						critical("Error adding background image: %s",
+						         e.message);
+					}
+					dialog.destroy();
+					break;
+				case Gtk.ResponseType.CANCEL:
+					action.apply();
+					dialog.destroy();
+					return;
+			}
+		}
+		
+		// when the action is applied, if the slide is still current, change ui
+		var local = slide;
+		action.applied.connect((a) => {
+			if (local == slide)
+			{
+				silence_undo = true;
+				background.set_active(slide.background_type);
+				display_bg_ui(slide.background_type);
+				silence_undo = false;
+			}
+		});
+		
+		// add properties to the UndoAction and report it to the controller
+		switch (type)
+		{
+			case BackgroundType.COLOR:
+				action.add(slide, "background-color");
+				break;
+			case BackgroundType.GRADIENT:
+				action.add(slide, "background-gradient");
+				break;
+			case BackgroundType.IMAGE:
+				action.add(slide, "background-image");
+				break;
+		}
+		emit_undo(action);
+		
+		// switch to that background type
+		display_bg_ui(type);
+	}
+	
+	[CCode (instance_pos = -1)]
+	public void on_gradient_type_changed(Gtk.ComboBox? sender)
+	{
+		emit_undo(new UndoAction(slide.background_gradient, "mode"));
+		slide.background_gradient.mode = (GradientMode)sender.get_active();
+		slide.changed(slide);
+	}
+	
+	[CCode (instance_pos = -1)]
+	public void on_color_set(Gtk.ColorButton? sender)
+	{
+		if (sender == bg_color)
+		{
+			emit_undo(slide.background_color.undo_action());
+			slide.background_color.gdk = sender.color;
+		}
+		else if (sender == grad_color1)
+		{
+			emit_undo(slide.background_gradient.start.undo_action());
+			slide.background_gradient.start.gdk = sender.color;
+		}
+		else if (sender == grad_color2)
+		{
+			emit_undo(slide.background_gradient.end.undo_action());
+			slide.background_gradient.end.gdk = sender.color;
+		}
+		slide.changed(slide);
+	}
+	
+	[CCode (instance_pos = -1)]
+	public void on_file_set(Gtk.FileChooserButton? sender)
+	{
+		var action = new UndoAction(slide, "background-image");
+		action.add(slide, "background-image-source");
+		
+		// slide might change in the meantime
+		var local = slide;
+		
+		// set the button's filename when the action is applied
+		action.applied.connect((a) => {
+			// if slide changes, this is still ok
+			if (slide.background_image_source != null)
+			{
+				bg_image.set_filename(slide.background_image_source);
+			}
+			else
+			{
+				bg_image.unselect_all();
+			}
+			local.changed(local);
+			display_bg_ui(slide.background_type);
+		});
+		
+		try
+		{
+			slide.background_image_source = sender.get_filename();
+			var i = slide.parent.add_media_file(sender.get_filename());
+			slide.background_image = i;
+		}
+		catch (GLib.Error e)
+		{
+			critical("Error adding background image: %s", e.message);
+		}
+		
+		slide.changed(slide);
+		
+		emit_undo(action);
+	}
+	
+	[CCode (instance_pos = -1)]
+	public void on_reverse_gradient(Gtk.Widget? sender)
+	{
+		// create an undo action
+		var action = new UndoAction(slide.background_gradient, "start");
+		action.add(slide.background_gradient, "end");
+		
+		// flip the gradient
+		slide.background_gradient.flip();
+		
+		// update the ui
+		grad_color1.set_color(slide.background_gradient.start.gdk);
+		grad_color2.set_color(slide.background_gradient.end.gdk);
+		slide.changed(slide);
+		
+		// add the undo action
+		emit_undo(action);
+	}
+	
+	protected override void slide_updated()
+	{
+		silence_undo = true;
+		
+		// set the combo box to the slide's active background type
+		background.set_active(slide.background_type);
+		
+		// set the gradient box to the correct mode
+		if (slide.background_gradient != null)
+		{
+			gradient_type.set_active(slide.background_gradient.mode);
+		}
+		
+		display_bg_ui(slide.background_type);
+		
+		silence_undo = false;
+	}
+	
+	private void display_bg_ui(BackgroundType type)
+	{
+		switch (type)
+		{
+			case BackgroundType.COLOR:
+				box_color.show_all();
+				box_gradient.hide_all();
+				box_image.hide_all();
+				
+				if (slide.background_color == null)
+				{
+					slide.background_color = Color.white;
+				}
+				slide.background_type = BackgroundType.COLOR;
+				
+				bg_color.set_color(slide.background_color.gdk);
+				
+				slide.changed(slide);
+				
+				break;
+			
+			case BackgroundType.GRADIENT:
+				box_color.hide_all();
+				box_gradient.show_all();
+				box_image.hide_all();
+				
+				if (slide.background_gradient == null)
+				{
+					slide.background_gradient = new Gradient(Color.black,
+					                                         Color.white);
+					gradient_type.set_active(slide.background_gradient.mode);
+				}
+				slide.background_type = BackgroundType.GRADIENT;
+				
+				grad_color1.set_color(slide.background_gradient.start.gdk);
+				grad_color2.set_color(slide.background_gradient.end.gdk);
+				
+				slide.changed(slide);
+				
+				break;
+			
+			case BackgroundType.IMAGE:
+				box_color.hide_all();
+				box_gradient.hide_all();
+				box_image.show_all();
+				
+				slide.background_type = BackgroundType.IMAGE;
+				if (slide.background_image_source != null)
+				{
+					bg_image.set_filename(slide.background_image_source);
+				}
+				else
+				{
+					bg_image.unselect_all();
+				}
+				
+				slide.changed(slide);
+				
+				break;
+		}
 	}
 }
 
diff --git a/src/ease-slide.vala b/src/ease-slide.vala
index 375bbab..79d7f21 100644
--- a/src/ease-slide.vala
+++ b/src/ease-slide.vala
@@ -63,58 +63,39 @@ public class Ease.Slide : GLib.Object
 	 */
 	public double advance_delay { get; set; }
 	
+	/**
+	 * The background type of this slide.
+	 */
 	public BackgroundType background_type { get; set; }
 	
 	/**
 	 * The background color, if this slide uses a solid color for a background.
 	 *
-	 * Setting this property sets { link background_type} to
+	 * To use this property, { link background_type} must also be set to
 	 * { link BackgroundType.COLOR}.
 	 */
-	public Color background_color
-	{
-		get { return background_color_priv; }
-		set
-		{
-			background_color_priv = value;
-			background_type = BackgroundType.COLOR;
-		}
-	}
-	private Color background_color_priv;
+	public Color? background_color { get; set; }
 	
 	/**
 	 * The background gradient, if this slide uses a gradient for a background.
 	 *
-	 * Setting this property sets { link background_type} to
+	 * To use this property, { link background_type} must also be set to
 	 * { link BackgroundType.GRADIENT}.
 	 */
-	public Gradient background_gradient
-	{
-		get { return background_gradient_priv; }
-		set
-		{
-			background_gradient_priv = value;
-			background_type = BackgroundType.GRADIENT;
-		}
-	}
-	private Gradient background_gradient_priv;
+	public Gradient? background_gradient { get; set; }
 	
 	/**
 	 * The background image, if this slide uses an image for a background.
 	 *
-	 * Setting this property sets { link background_type} to
+	 * To use this property, { link background_type} must also be set to
 	 * { link BackgroundType.IMAGE}.
 	 */
-	public string background_image
-	{
-		get { return background_image_priv; }
-		set
-		{
-			background_image_priv = value;
-			background_type = BackgroundType.IMAGE;
-		}
-	}
-	private string background_image_priv;
+	public string background_image { get; set; }
+	
+	/**
+	 * The original path to the background image. This path is used in the UI.
+	 */
+	public string background_image_source { get; set; }
 	
 	/**
 	 * The absolute path of the background image, if one is set.
@@ -394,6 +375,8 @@ public enum Ease.BackgroundType
 	GRADIENT,
 	IMAGE;
 	
+	public const BackgroundType[] TYPES = { COLOR, GRADIENT, IMAGE};
+	
 	/**
 	 * Returns a string representation of this BackgroundType.
 	 */
@@ -423,5 +406,19 @@ public enum Ease.BackgroundType
 		warning("%s is not a gradient type", str);
 		return COLOR;
 	}
+	
+	/**
+	 * Returns a description of the BackgroundType.
+	 */
+	public string description()
+	{
+		switch (this)
+		{
+			case COLOR: return _("Solid Color");
+			case GRADIENT: return _("Gradient");
+			case IMAGE: return _("Image");
+		}
+		return "undefined";
+	}
 }
 
diff --git a/src/ease-theme.vala b/src/ease-theme.vala
index e0fd28d..a051472 100644
--- a/src/ease-theme.vala
+++ b/src/ease-theme.vala
@@ -327,13 +327,16 @@ public class Ease.Theme : GLib.Object
 			case BACKGROUND_TYPE_COLOR:
 				slide.background_color = new Color.
 					from_string(master_get(master, BACKGROUND_COLOR));
+				slide.background_type = BackgroundType.COLOR;
 				break;
 			case BACKGROUND_TYPE_GRADIENT:
 				slide.background_gradient = new Gradient.
 					from_string(master_get(master, BACKGROUND_GRADIENT));
+				slide.background_type = BackgroundType.GRADIENT;
 				break;
 			case BACKGROUND_TYPE_IMAGE:
 				slide.background_image = master_get(master, BACKGROUND_IMAGE);
+				slide.background_type = BackgroundType.IMAGE;
 				break;
 				
 		}
diff --git a/src/ease-undo-action.vala b/src/ease-undo-action.vala
index fa39ba7..472022a 100644
--- a/src/ease-undo-action.vala
+++ b/src/ease-undo-action.vala
@@ -26,6 +26,11 @@ public class Ease.UndoAction : Object
 	private Gee.LinkedList<UndoPair> pairs = new Gee.LinkedList<UndoPair>();
 	
 	/**
+	 * Emitted after the action is applied.
+	 */
+	public signal void applied(UndoAction sender);
+	
+	/**
 	 * Creates an UndoAction.
 	 *
 	 * This should be followed up with calls to add() if the action has
@@ -51,6 +56,16 @@ public class Ease.UndoAction : Object
 	}
 	
 	/**
+	 * Adds all properties of the given UndoAction to this action.
+	 *
+	 * @param action An UndoAction to add properties from.
+	 */
+	public void combine(UndoAction action)
+	{
+		foreach (var p in action.pairs) pairs.add(p);
+	}
+	
+	/**
 	 * Applies the { link UndoAction}, restoring previous settings.
 	 *
 	 * Returns an UndoAction that will redo the undo action.
@@ -58,6 +73,7 @@ public class Ease.UndoAction : Object
 	public UndoAction apply()
 	{
 		foreach (var pair in pairs) pair.apply();
+		applied(this);
 		return this;
 	}
 	
diff --git a/src/ease-undo-source.vala b/src/ease-undo-source.vala
new file mode 100644
index 0000000..17e0f41
--- /dev/null
+++ b/src/ease-undo-source.vala
@@ -0,0 +1,29 @@
+/*  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/>.
+*/
+
+/**
+ * Provides a signal to notify a controller of UndoActions.
+ */
+public interface Ease.UndoSource : GLib.Object
+{
+	/**
+	 * Classes that implement the UndoSource interface should use this signal
+	 * to notify a parent controller (typically { link EditorWindow}) of a new
+	 * UndoAction.
+	 */
+	public signal void undo(UndoAction action);
+}
diff --git a/src/ease-utilities.vala b/src/ease-utilities.vala
index 060906c..0ecafb8 100644
--- a/src/ease-utilities.vala
+++ b/src/ease-utilities.vala
@@ -282,6 +282,15 @@ namespace Ease
 	{
 		return Transformations.gdk_color_to_clutter_color(theme_color(color));
 	}
+	
+	/**
+	 * Returns the parent window of the specified widget.
+	 */
+	public Gtk.Window widget_window(Gtk.Widget widg)
+	{
+		while (widg.get_parent() != null) widg = widg.get_parent();
+		return widg as Gtk.Window;
+	}
 
 	public double dmax(double a, double b)
 	{



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