[ease/themes: 24/25] [themes] New themes system runs without crashing.



commit 821c23db16cf40d544f0276e989fee8ef36e9448
Author: Nate Stedman <natesm gmail com>
Date:   Wed Jul 21 13:23:24 2010 -0400

    [themes] New themes system runs without crashing.

 Makefile.am                      |    1 +
 data/Makefile.am                 |    8 +-
 data/theme-defaults.json         |   42 ++++
 src/ease-document.vala           |   22 ++-
 src/ease-editor-window.vala      |   10 +-
 src/ease-json-parser.vala        |   34 ---
 src/ease-slide.vala              |   11 +
 src/ease-text-element.vala       |   44 ++--
 src/ease-theme.vala              |  436 +++++++++++++++++++++++++++++++++++++-
 src/ease-welcome-window.vala     |    9 +-
 themes/Pink/Media/Background.svg |   11 -
 themes/Pink/Theme.json           |   67 ------
 themes/White/Theme.json          |   70 +------
 13 files changed, 539 insertions(+), 226 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 55edf9f..9a92585 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,6 +9,7 @@ bin_PROGRAMS = ease
 AM_CPPFLAGS = \
 	$(EASE_CFLAGS) \
 	-include $(CONFIG_HEADER) \
+	-O0 \
 	$(NULL)
 
 ease_SOURCES = \
diff --git a/data/Makefile.am b/data/Makefile.am
index a76ec48..ba8b5f5 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -10,5 +10,11 @@ svg_DATA = $(wildcard $(top_srcdir)/data/svg/*.svg)
 uidir = $(datadir)/ease/ui
 ui_DATA = $(wildcard $(top_srcdir)/data/ui/*.ui)
 
-EXTRA_DIST = $(ui_DATA) $(svg_DATA) $(desktop_in_files)
+jsondir = $(datadir)/ease
+json_DATA = theme-defaults.json
 
+EXTRA_DIST = \
+    $(ui_DATA) \
+    $(svg_DATA) \
+    $(desktop_in_files) \
+    $(json_DATA)
diff --git a/data/theme-defaults.json b/data/theme-defaults.json
new file mode 100644
index 0000000..655a1a8
--- /dev/null
+++ b/data/theme-defaults.json
@@ -0,0 +1,42 @@
+{
+	"master-defaults" : {
+		"background-color" : "#FFFFFFFF"
+	},
+	
+	"element-defaults" : {
+		"text-font" : "Bitstream Vera Sans",
+		"text-size" : "12",
+		"text-style" : "Normal",
+		"text-variant" : "Normal",
+		"text-weight" : "500",
+		"text-align" : "left",
+		"text-color" : "#000000FF",
+		
+		"padding-top" : "10",
+		"padding-left" : "10",
+		"padding-right" : "10",
+		"padding-bottom" : "10"
+	},
+	
+	"elements" : {
+		"header-text" : {
+			"height" : "30",
+			"text-size" : "24"
+		},
+		
+		"author-text" : {
+			"padding-top" : "0",
+			"height" : "20",
+			"text-size" : "18",
+			"text-align" : "center"
+		},
+		
+		"title-text" : {
+			"height" : "30",
+			"text-size" : "28",
+			"text-align" : "center"
+		}
+	},
+	
+	"masters" : {}
+}
diff --git a/src/ease-document.vala b/src/ease-document.vala
index d941f18..d9580c7 100644
--- a/src/ease-document.vala
+++ b/src/ease-document.vala
@@ -18,7 +18,7 @@
 /**
  * The internal representation of Ease documents. Contains { link Slide}s.
  *
- * The Ease Document class is generated from XML and writes back to XML
+ * The Ease Document class is generated from JSON and writes back to JSON
  * when saved.
  */
 public class Ease.Document : SlideSet
@@ -26,7 +26,12 @@ public class Ease.Document : SlideSet
 	/**
 	 * The default master title for newly created { link Slide}s.
 	 */
-	private const string DEFAULT_SLIDE = "Standard";
+	public const string DEFAULT_SLIDE = Theme.CONTENT_HEADER;
+	
+	/**
+	 * The default master slide for the first slide.
+	 */
+	private const string DEFAULT_FIRST = Theme.TITLE;
 
 	/**
 	 * The { link Theme} linked to this Document.
@@ -65,21 +70,22 @@ public class Ease.Document : SlideSet
 	 */
 	public Document.from_theme(Theme doc_theme, int w, int h) throws GLib.Error
 	{
+		assert(doc_theme != null);
+		
 		width = w;
 		height = h;
 		theme = doc_theme;
 		
-		assert (doc_theme != null);
 		// allocate a temp directory for the new document
 		path = Temp.request();
 		
 		// copy media to the new path
-		doc_theme.copy_media(path);
-		// get the master
-		var master = theme.slide_by_title(DEFAULT_SLIDE);
+		theme.copy_media(path);
 		
-		// add the first slide
-		append_slide(new Slide.from_master(master, this, width, height, true));
+		// get the master for the first slide
+		var slide = theme.create_slide(DEFAULT_FIRST, width, height);
+		slide.parent = this;
+		append_slide(slide);
 	}
 	
 	/**
diff --git a/src/ease-editor-window.vala b/src/ease-editor-window.vala
index ab09c3b..a63b931 100644
--- a/src/ease-editor-window.vala
+++ b/src/ease-editor-window.vala
@@ -236,17 +236,15 @@ public class Ease.EditorWindow : Gtk.Window
 	[CCode (instance_pos = -1)]
 	public void new_slide_handler(Gtk.Widget? sender)
 	{
-		var master = document.theme.slide_by_title(slide.title);
-		
-		var slide = new Slide.from_master(master, document,
-		                                  document.width,
-		                                  document.height, true);
+		var slide = document.theme.create_slide(document.DEFAULT_SLIDE,
+		                                        document.width,
+		                                        document.height);
 		
 		var index = document.index_of(slide) + 1;
 		
 		document.add_slide(index, slide);
 		slide_button_panel.add_slide(index, slide);
-		}
+	}
 	
 	[CCode (instance_pos = -1)]
 	public void play_handler(Gtk.Widget sender)
diff --git a/src/ease-json-parser.vala b/src/ease-json-parser.vala
index d3b262d..14fb45a 100644
--- a/src/ease-json-parser.vala
+++ b/src/ease-json-parser.vala
@@ -60,40 +60,6 @@ public static class Ease.JSONParser
 		return document;
 	}
 	
-	/**
-	 * Parses a theme JSON file, creating a { link Theme}.
-	 *
-	 * @param path The path to the { link Theme}
-	 */
-	public static Theme theme(string path) throws GLib.Error
-	{
-		string filename = absolute_path(path);
-		var theme = new Theme();
-		theme.path = Temp.extract(filename);
-	
-		var parser = new Json.Parser();
-		
-		// attempt to load the file
-		parser.load_from_file(Path.build_filename(theme.path, "Theme.json"));
-		
-		// grab the root object
-		var root = parser.get_root().get_object();
-		
-		// set theme properties
-		theme.title = root.get_string_member("title");
-		
-		// add all slides
-		var slides = root.get_array_member("slides");
-		
-		for (var i = 0; i < slides.get_length(); i++)
-		{
-			var node = slides.get_object_element(i);
-			theme.add_slide(theme.length, parse_slide(node));
-		}
-		
-		return theme;
-	}
-	
 	private static Slide parse_slide(Json.Object obj)
 	{
 		var slide = new Slide();
diff --git a/src/ease-slide.vala b/src/ease-slide.vala
index 469a98a..6c59161 100644
--- a/src/ease-slide.vala
+++ b/src/ease-slide.vala
@@ -217,6 +217,17 @@ public class Ease.Slide : GLib.Object
 	}
 	
 	/**
+	 * Adds an { link Element} to this slide at the end index.
+	 * 
+	 * @param e The element to add;.
+	 */
+	public void add(Element e)
+	{
+		e.parent = this;
+		elements.insert(count, e);
+	}
+	
+	/**
 	 * Creates HTML markup for this Slide.
 	 * 
 	 * The <div> tag for this Slide is appended to the "HTML" parameter.
diff --git a/src/ease-text-element.vala b/src/ease-text-element.vala
index 359e7cc..5bc2d16 100644
--- a/src/ease-text-element.vala
+++ b/src/ease-text-element.vala
@@ -160,8 +160,8 @@ public class Ease.TextElement : Element
 	 */
 	public string font_name
 	{
-		owned get { return data.get("font_name"); }
-		set { data.set("font_name", value); }
+		owned get { return data.get(Theme.TEXT_FONT); }
+		set { data.set(Theme.TEXT_FONT, value); }
 	}
 	
 	/**
@@ -171,7 +171,7 @@ public class Ease.TextElement : Element
 	{
 		get
 		{
-			switch (data.get("font_style"))
+			switch (data.get(Theme.TEXT_STYLE))
 			{
 				case "Oblique":
 					return Pango.Style.OBLIQUE;
@@ -186,13 +186,13 @@ public class Ease.TextElement : Element
 			switch (value)
 			{
 				case Pango.Style.OBLIQUE:
-					data.set("font_style", "Oblique");
+					data.set(Theme.TEXT_STYLE, "Oblique");
 					break;
 				case Pango.Style.ITALIC:
-					data.set("font_style", "Italic");
+					data.set(Theme.TEXT_STYLE, "Italic");
 					break;
 				case Pango.Style.NORMAL:
-					data.set("font_style", "Normal");
+					data.set(Theme.TEXT_STYLE, "Normal");
 					break;
 			}
 		}
@@ -205,15 +205,15 @@ public class Ease.TextElement : Element
 	{
 		get
 		{
-			return data.get("font_variant") == "Normal"
+			return data.get(Theme.TEXT_VARIANT) == "Normal"
 			     ? Pango.Variant.NORMAL
 			     : Pango.Variant.SMALL_CAPS;
 		}
 		set
 		{
-			data.set("font_name",
-			             value == Pango.Variant.NORMAL ?
-			                      "Normal" : "Small Caps");
+			data.set(Theme.TEXT_VARIANT,
+			          value == Pango.Variant.NORMAL ?
+			          "Normal" : "Small Caps");
 		}
 	}
 	
@@ -224,12 +224,11 @@ public class Ease.TextElement : Element
 	{
 		get
 		{
-			var str = "font_name";
-			return (Pango.Weight)(data.get(str).to_int());
+			return (Pango.Weight)(data.get(Theme.TEXT_WEIGHT).to_int());
 		}
 		set
 		{
-			data.set("font_weight", ((int)value).to_string());
+			data.set(Theme.TEXT_WEIGHT, ((int)value).to_string());
 		}
 	}
 	
@@ -244,7 +243,7 @@ public class Ease.TextElement : Element
 		owned get
 		{
 			var desc = new Pango.FontDescription();
-			desc.set_family(data.get("font_name"));
+			desc.set_family(data.get(Theme.TEXT_FONT));
 			desc.set_style(font_style);
 			desc.set_weight(font_weight);
 			desc.set_variant(font_variant);
@@ -254,7 +253,7 @@ public class Ease.TextElement : Element
 		}
 		set
 		{
-			data.set("font_name", value.get_family());
+			data.set(Theme.TEXT_FONT, value.get_family());
 			font_style = value.get_style();
 			font_weight = value.get_weight();
 			font_variant = value.get_variant();
@@ -269,7 +268,7 @@ public class Ease.TextElement : Element
 	{
 		get
 		{
-			switch (data.get("align"))
+			switch (data.get(Theme.TEXT_ALIGN))
 			{
 				case "right":
 					return Pango.Alignment.RIGHT;
@@ -284,13 +283,16 @@ public class Ease.TextElement : Element
 			switch (value)
 			{
 				case Pango.Alignment.RIGHT:
-					data.set("font_style", "right");
+					data.set(Theme.TEXT_ALIGN, "right");
 					break;
 				case Pango.Alignment.CENTER:
-					data.set("font_style", "center");
+					data.set(Theme.TEXT_ALIGN, "center");
 					break;
 				case Pango.Alignment.LEFT:
-					data.set("font_style", "left");
+					data.set(Theme.TEXT_ALIGN, "left");
+					break;
+				default:
+					error("Illegal alignment value: %s", value.to_string());
 					break;
 			}
 		}
@@ -306,11 +308,11 @@ public class Ease.TextElement : Element
 	{
 		get
 		{
-			return data.get("font_size").to_int();
+			return data.get(Theme.TEXT_SIZE).to_int();
 		}
 		set
 		{
-			data.set("font_size", @"$value");
+			data.set(Theme.TEXT_SIZE, value.to_string());
 		}
 	}
 }
diff --git a/src/ease-theme.vala b/src/ease-theme.vala
index ad3f891..e421346 100644
--- a/src/ease-theme.vala
+++ b/src/ease-theme.vala
@@ -16,18 +16,446 @@
 */
 
 /**
- * Internal representation of Ease themes
+ * Internal representation of Ease themes.
  */
-public class Ease.Theme : SlideSet
+public class Ease.Theme : GLib.Object
 {
+	// file paths
+	private const string DEFAULTS_PATH = "theme-defaults.json";
+	private const string JSON_PATH = "Theme.json";
+	private const string MEDIA_PATH = "Media";
+	
+	// json root elements
+	private const string MASTERS = "masters";
+	private const string ELEMENTS = "elements";
+	private const string MASTER_DEF = "master-defaults";
+	private const string ELEMENT_DEF = "element-defaults";
+	
+	// master slides
+	public const string TITLE = "title";
+	public const string CONTENT = "content";
+	public const string CONTENT_HEADER = "content-header";
+	public const string CONTENT_DUAL = "content-dual";
+	public const string CONTENT_DUAL_HEADER = "content-dual-header";
+	public const string MEDIA = "media";
+	public const string MEDIA_HEADER = "media-header";
+	
+	// text content types
+	private const string TITLE_TEXT = "title-text";
+	private const string AUTHOR_TEXT = "author-text";
+	private const string CONTENT_TEXT = "content-text";
+	private const string HEADER_TEXT = "header-text";
+	
+	// text properties
+	public const string TEXT_FONT = "text-font";
+	public const string TEXT_SIZE = "text-size";
+	public const string TEXT_STYLE = "text-style";
+	public const string TEXT_VARIANT = "text-variant";
+	public const string TEXT_WEIGHT = "text-weight";
+	public const string TEXT_ALIGN = "text-align";
+	public const string TEXT_COLOR = "text-color";
+	
+	/**
+	 * The text properties, excluding color, which must be set in a custom way.
+	 */
+	private const string[] TEXT_PROPS = {
+		TEXT_FONT,
+		TEXT_SIZE,
+		TEXT_STYLE,
+		TEXT_VARIANT,
+		TEXT_WEIGHT,
+		TEXT_ALIGN
+	};
+	
+	// media content types
+	public const string CONTENT_MEDIA = "content-media";
+	
+	// generic element properties
+	public const string PAD_LEFT = "padding-left";
+	public const string PAD_RIGHT = "padding-right";
+	public const string PAD_TOP = "padding-top";
+	public const string PAD_BOTTOM = "padding-bottom";
+	public const string WIDTH = "width";
+	public const string HEIGHT = "height";
+	
 	/**
 	 * The title of the Theme.
 	 */
-	public string title { get; set; }
+	public string title;
+	
+	/**
+	 * The path to the theme's extracted files.
+	 */
+	public string path { get; set; }
+	
+	/**
+	 * A map of internal master slide settings overriden by the theme.
+	 */
+	private Gee.Map<string, Gee.Map<string, string>> masters;
+	
+	/**
+	 * A map of internal element settings overriden by the theme.
+	 */
+	private Gee.Map<string, Gee.Map<string, string>> elements;
+	
+	/**
+	 * A map of master slide settings, used as a fallback for all masters when
+	 * the specified master does not provide the given property.
+	 *
+	 * For example, the background properties are typically the same
+	 * throughout the theme. This is an efficient place for those properties.
+	 */
+	private Gee.Map<string, string> master_defaults;
+	
+	/**
+	 * A map of element settings, used as a fallback for all elements when
+	 * the specified element does not provide the given property.
+	 *
+	 * For example, the text-font property is often the same throughout the
+	 * theme. This is an efficient place for properties like that.
+	 */
+	private Gee.Map<string, string> element_defaults;
+	
+	/**
+	 * A Theme containing default values for elements and master slides.
+	 */
+	private static Theme defaults
+	{
+		get
+		{
+			if (defaults_store != null) return defaults_store;
+			return defaults_store = new Theme.json(data_path(DEFAULTS_PATH));
+		}
+	}
+	
+	/**
+	 * Storage for "defaults" property.
+	 */
+	private static Theme defaults_store;
 
 	/**
 	 * Creates an empty Theme.
+	 *
+	 * @param path The path to the theme's archive.
+	 */
+	public Theme(string archive_path)
+	{
+		// extract the theme
+		try
+		{
+			path = Temp.extract(archive_path);
+		}
+		catch (GLib.Error e)
+		{
+			error_dialog(_("Error Loading Theme"),
+			             (_("Error loading theme: %s") + "\n\n" + e.message).
+			             printf(path));
+			return;
+		}
+		
+		load_from_json(Path.build_filename(path, JSON_PATH));
+		
+		// TODO: load theme title
+		title = "Hello Themes!";
+	}
+	
+	/**
+	 * Loads a Theme from pure JSON, (no archive).
+	 *
+	 * This constructor is used to load the defaults.
+	 *
+	 * @param json_path The path to the JSON file.
+	 */
+	private Theme.json(string json_path)
+	{
+		load_from_json(json_path);
+	}
+	
+	/**
+	 * Loads a Theme's information from JSON
+	 *
+	 * This function is used to load the defaults and  to load each
+	 * extracted theme.
+	 *
+	 * @param json_path The path to the JSON file.
+	 */
+	private void load_from_json(string json_path)
+	{
+		var parser = new Json.Parser();
+		try
+		{
+			parser.load_from_file(json_path);
+		}
+		catch (Error e)
+		{
+			error(_("Error loading theme: %s"), e.message);
+		}
+		// create collections
+		masters = new Gee.HashMap<string, Gee.Map<string, string>>();
+		elements = new Gee.HashMap<string, Gee.Map<string, string>>();
+		master_defaults = new Gee.HashMap<string, string>();
+		element_defaults = new Gee.HashMap<string, string>();
+		
+		// get the document's root element
+		unowned Json.Node node = parser.get_root();
+		if (node == null) return;
+		var root = node.get_object();
+		if (root == null) return;
+		
+		// find all masters and element overrides
+		fill_map(root, MASTERS, masters);
+		fill_map(root, ELEMENTS, elements);
+		
+		if (root.has_member(MASTER_DEF))
+			fill_single_map(root.get_object_member(MASTER_DEF),
+			                master_defaults);
+		if (root.has_member(ELEMENT_DEF))
+			fill_single_map(root.get_object_member(ELEMENT_DEF),
+			                element_defaults);
+	}
+	/**
+	 * Copies all files under Media/ to a new directory.
+	 *
+	 * @param target The path to copy media files to.
+	 */
+	public void copy_media(string target) throws GLib.Error
+	{
+		var origin_path = Path.build_filename(path, MEDIA_PATH);
+		
+		var target_path = Path.build_filename(target, MEDIA_PATH);
+		
+		// TODO: non-system implementation of recursive copy
+		Posix.system("cp -r %s %s".printf(origin_path, target_path));
+	}
+	
+	/**
+	 * Creates a slide from a theme master.
+	 *
+	 * @param master The string identifier for the master to use. This should be
+	 * a string constant of this class (TITLE, CONTENT, etc.)
+	 * @param width The width of the slide.
+	 * @param height The height of the slide.
+	 */
+	public Slide? create_slide(string master, int width, int height)
+	{
+		Slide slide = new Slide();
+		
+		switch (master)
+		{
+			case TITLE:
+				// create the presentation's title
+				int left = element_get(TITLE_TEXT, PAD_LEFT).to_int(),
+				    h = element_get(TITLE_TEXT, HEIGHT).to_int();
+				slide.add(create_text(
+					TITLE_TEXT,
+					left,
+					height / 2 - h - element_get(TITLE_TEXT, PAD_BOTTOM).to_int(),
+					width - left - element_get(TITLE_TEXT, PAD_RIGHT).to_int(),
+					h
+				));
+				
+				// create the presentation's author field
+				left = element_get(AUTHOR_TEXT, PAD_LEFT).to_int();
+				slide.add(create_text(
+					AUTHOR_TEXT,
+					left,
+					height / 2 + element_get(AUTHOR_TEXT, PAD_TOP).to_int(),
+					width - left - element_get(AUTHOR_TEXT, PAD_RIGHT).to_int(),
+					element_get(AUTHOR_TEXT, HEIGHT).to_int()
+				));
+				break;
+				
+			case CONTENT:
+				int left = element_get(CONTENT_TEXT, PAD_LEFT).to_int(),
+				    top = element_get(CONTENT_TEXT, PAD_TOP).to_int();
+				
+				slide.add(create_text(
+					CONTENT_TEXT,
+					left,
+					top,
+					width - left - element_get(CONTENT_TEXT, PAD_RIGHT).to_int(),
+					height - top - element_get(HEADER_TEXT, PAD_BOTTOM).to_int()
+				));
+				break;
+				
+			case CONTENT_HEADER:
+				// create the slide's header
+				int left = element_get(HEADER_TEXT, PAD_LEFT).to_int(),
+				    top = element_get(HEADER_TEXT, PAD_TOP).to_int();
+				
+				slide.add(create_text(
+					HEADER_TEXT,
+					left,
+					top,
+					width - left - element_get(HEADER_TEXT, PAD_RIGHT).to_int(),
+					element_get(HEADER_TEXT, HEIGHT).to_int()
+				));
+				
+				// create the slide's content
+				left = element_get(CONTENT_TEXT, PAD_LEFT).to_int();
+				top += element_get(HEADER_TEXT, HEIGHT).to_int() + 
+				       element_get(CONTENT_TEXT, PAD_TOP).to_int();
+				slide.add(create_text(
+					CONTENT_TEXT,
+					left,
+					top,
+					width - left - element_get(CONTENT_TEXT, PAD_RIGHT).to_int(),
+					height - top - element_get(HEADER_TEXT, PAD_BOTTOM).to_int()
+				));
+				break;
+			
+			case CONTENT_DUAL:
+			case CONTENT_DUAL_HEADER:
+			case MEDIA:
+			case MEDIA_HEADER:
+				break;
+			default:
+				error(_("Invalid master slide title: %s"), master);
+				return null;
+		}
+		
+		return slide;
+	}
+	
+	/**
+	 * Creates a text element, given an element type and dimensions.
+	 */
+	private TextElement create_text(string type, int x, int y, int w, int h)
+	{
+		// error if an improper element type is used
+		if (!(type == TITLE_TEXT || type == AUTHOR_TEXT ||
+		      type == CONTENT_TEXT || type == HEADER_TEXT))
+		{
+			error(_("Not a valid text element type: %s"), type);
+		}
+		
+		// otherwise, construct the text element
+		var text = new TextElement();
+		
+		// set text properties
+		foreach (var prop in TEXT_PROPS)
+		{
+			text.set(prop, element_get(type, prop));
+		}
+		
+		// set the color property
+		Clutter.Color color = {0, 0, 0, 255};	
+		color.from_string(element_get(type, TEXT_COLOR));
+		text.color = color;
+		
+		// set size properties
+		text.x = x;
+		text.y = y;
+		text.width = w;
+		text.height = h;
+		
+		// TODO: some manner of default text
+		text.text = "Hello world!";
+		
+		return text;
+	}
+	
+	/**
+	 * Retrieves an element property.
+	 *
+	 * @param element The element name to search for.
+	 * @param prop The property name to search for.
+	 */
+	private string element_get(string element, string prop)
+	{
+		// try local specifics
+		var map = elements.get(element);
+		if (map != null)
+		{
+			var str = map.get(prop);
+			if (str != null) return str;
+		}
+		
+		// try local generics
+		var str = element_defaults.get(prop);
+		if (str != null) return str;
+		
+		// use default settings
+		if (defaults == this)
+		{
+			error(_("Could not find property %s on element type %s."),
+			      prop, element);
+		}
+		
+		return defaults.element_get(element, prop);
+	}
+	
+	/**
+	 * Retrieves an master property.
+	 *
+	 * @param master The master name to search for.
+	 * @param prop The property name to search for.
+	 */
+	private string master_get(string master, string prop)
+	{
+		// try local specifics
+		var str = masters.get(master).get(prop);
+		if (str != null) return str;
+		
+		// try local generics
+		str = master_defaults.get(prop);
+		if (str != null) return str;
+		
+		// use default settings
+		if (defaults == this)
+		{
+			error(_("Could not find property %s on master type %s."),
+			      master, prop);
+		}
+		
+		return defaults.master_get(master, prop);
+	}
+	
+	/**
+	 * Fills a Gee.Map with style property overrides in the form of more
+	 * Gee.Maps.
+	 *
+	 * @param obj The root object.
+	 * @param name The name of the JSON array to use.
+	 * @param map The map to fill with submaps.
+	 */
+	private void fill_map(Json.Object obj, string name,
+	                      Gee.Map<string, Gee.Map<string, string>> map)
+	{
+		if (!obj.has_member(name)) return;
+		var sub = obj.get_object_member(name);
+		if (sub == null) return;
+		
+		for (unowned List<string>* i = sub.get_members();
+		     i != null; i = i->next)
+		{
+			// get the current object (an array)
+			var curr_obj = sub.get_member(i->data).get_object();
+			
+			// create a map for the values
+			var submap = new Gee.HashMap<string, string>();
+		
+			// add each override to the map
+			fill_single_map(curr_obj, submap);
+			
+			// add the map to the map of overrides
+			map.set(i->data, submap);
+		}
+	}
+	
+	/**
+	 * Fill a Gee.Map with key/value pairs.
+	 *
+	 * @param obj The json object to use.
+	 * @param map The map to fill.
 	 */
-	public Theme() { }
+	private void fill_single_map(Json.Object obj, Gee.Map<string, string> map)
+	{
+		for (unowned List<string>* j = obj.get_members();
+		     j != null; j = j->next)
+		{
+			map.set(j->data, obj.get_member(j->data).get_string());
+		}
+	}
 }
 
diff --git a/src/ease-welcome-window.vala b/src/ease-welcome-window.vala
index eb65ee3..1763b7c 100644
--- a/src/ease-welcome-window.vala
+++ b/src/ease-welcome-window.vala
@@ -80,7 +80,7 @@ public class Ease.WelcomeWindow : Gtk.Window
 	
 	private int[] ZOOM_VALUES = {100, 150, 200, 250, 400};
 	
-	private const string PREVIEW_ID = "Standard";
+	private const string PREVIEW_ID = Theme.TITLE;
 	
 	public WelcomeWindow()
 	{
@@ -197,19 +197,18 @@ public class Ease.WelcomeWindow : Gtk.Window
 					string name = directory.read_name ();
 					while (name != null) {
 						var path = Path.build_filename (filename, name);
-						// FIXME : warning occurs here (g_param_spec)
-						themes.add (JSONParser.theme (path));
+						themes.add (new Theme(path));
 						name = directory.read_name ();
 					}
 				}
 			}
 		} catch (Error e) {
 			error_dialog("Error loading themes : %s", e.message);
-			}
+		}
 
 		// create the previews
 		foreach (var theme in themes) {
-			var master = theme.slide_by_title (PREVIEW_ID);
+			var master = theme.create_slide(PREVIEW_ID, 1024, 768);
 			if (master == null) continue;
 			
 			var act = new WelcomeActor (theme, previews, master);
diff --git a/themes/White/Theme.json b/themes/White/Theme.json
index 7d20ba4..8b13789 100644
--- a/themes/White/Theme.json
+++ b/themes/White/Theme.json
@@ -1,69 +1 @@
-{
-  "title" : "White",
-  "slides" : [
-    {
-      "transition" : "1",
-      "green" : "255",
-      "variant" : "0",
-      "automatically_advance" : "false",
-      "advance_delay" : "0",
-      "elements" : [
-        {
-          "font_variant" : "Normal",
-          "font_style" : "Normal",
-          "element_type" : "EaseTextElement",
-          "identifier" : "header",
-          "green" : "0",
-          "align" : "left",
-          "text" : "Lorem Ipsum Dolor",
-          "blue" : "0",
-          "font_weight" : "900",
-          "red" : "0",
-          "ease_name" : "header",
-          "font_name" : "Sans",
-          "font_size" : "36",
-          "left" : "30",
-          "right" : "30",
-          "top" : "30",
-          "bottom" : "510",
-          "bind_left" : "true",
-          "bind_right" : "true",
-          "bind_top" : "true",
-          "bind_bottom" : "false",
-          "expand_horizontally" : "true",
-          "expand_vertically" : "false"
-        },
-        
-        {
-          "font_variant" : "Normal",
-          "font_style" : "Normal",
-          "element_type" : "EaseTextElement",
-          "identifier" : "header",
-          "green" : "0",
-          "align" : "left",
-          "text" : "Sit amet, consectetur adipiscing elit. Maecenas sed odio eget purus laoreet volutpat. Etiam nulla orci, eleifend nec sodales in, tempor cursus urna. Aenean posuere aliquet malesuada. Integer varius placerat massa. Pellentesque enim urna, cursus et molestie et, iaculis vitae libero. Quisque vel metus sed magna lacinia luctus. Suspendisse vel lectus eget diam dapibus condimentum. Aliquam a dolor vel sem rutrum mattis sit amet vitae nisl. Etiam vel sem tortor. Vestibulum varius metus id orci vulputate viverra luctus magna commodo. Duis dignissim sollicitudin leo eget tristique. Maecenas adipiscing neque nec mauris mollis ut ultrices sem porta. Cras vitae massa lectus. Aenean orci lectus, pretium nec sodales eu, aliquam vitae neque. Mauris nibh lectus, porta et vestibulum a, vestibulum a nulla.",
-          "blue" : "0",
-          "font_weight" : "900",
-          "red" : "0",
-          "ease_name" : "header",
-          "font_name" : "Sans",
-          "font_size" : "16",
-          "left" : "30",
-          "right" : "30",
-          "top" : "100",
-          "bottom" : "30",
-          "bind_left" : "true",
-          "bind_right" : "true",
-          "bind_top" : "true",
-          "bind_bottom" : "true",
-          "expand_horizontally" : "true",
-          "expand_vertically" : "true"
-        }
-      ],
-      "blue" : "255",
-      "title" : "Standard",
-      "red" : "255",
-      "transition_time" : "0.5"
-    }
-  ]
-}
+



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