[ease] Implemented subclasses of Element.



commit 755cceb6ebc7f6173a27f8d15271df37678c0429
Author: Nate Stedman <natesm gmail com>
Date:   Thu Jun 10 05:40:15 2010 -0400

    Implemented subclasses of Element.
    
    - Element is now abstract
    - Video, Image, Text Element classes
    - Abstract MediaElement classes for video and image base
    - Themes and examples have been updated appropriately
    - Strict typing is nice

 Makefile.am                        |    5 +-
 examples/Transitions/Document.json |  108 ++++----
 src/Actor.vala                     |    2 +-
 src/Element.vala                   |  514 +++++++++++++-----------------------
 src/ImageActor.vala                |    4 +-
 src/ImageElement.vala              |   76 ++++++
 src/JSONParser.vala                |   36 ++-
 src/MasterElement.vala             |  246 -----------------
 src/MediaElement.vala              |   44 +++
 src/PDFExporter.vala               |    2 +-
 src/Slide.vala                     |    2 +-
 src/SlideActor.vala                |   14 +-
 src/TextActor.vala                 |    4 +-
 src/TextElement.vala               |  282 ++++++++++++++++++++
 src/VideoActor.vala                |    4 +-
 src/VideoElement.vala              |   66 +++++
 themes/Pink/Theme.json             |    4 +-
 themes/White/Theme.json            |    4 +-
 18 files changed, 742 insertions(+), 675 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d43f432..da47f16 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,13 +25,14 @@ ease_SOURCES = \
 	src/Handle.vala \
 	src/HTMLExporter.vala \
 	src/ImageActor.vala \
+	src/ImageElement.vala \
 	src/Inspector.vala\
 	src/InspectorPane.vala\
 	src/InspectorWindow.vala\
 	src/JSONParser.vala\
 	src/MainToolbar.vala \
 	src/Main.vala \
-	src/MasterElement.vala \
+	src/MediaElement.vala \
 	src/OpenDialog.vala \
 	src/PDFExporter.vala \
 	src/Player.vala \
@@ -44,12 +45,14 @@ ease_SOURCES = \
 	src/Slide.vala \
 	src/Temp.vala \
 	src/TextActor.vala \
+	src/TextElement.vala \
 	src/Theme.vala \
 	src/TransitionPane.vala \
 	src/Transitions.vala \
 	src/UndoActions.vala \
 	src/UndoController.vala \
 	src/VideoActor.vala \
+	src/VideoElement.vala \
 	src/WelcomeActor.vala \
 	src/WelcomeWindow.vala \
 	src/ZoomSlider.vala \
diff --git a/examples/Transitions/Document.json b/examples/Transitions/Document.json
index 63a6177..d7c17b0 100644
--- a/examples/Transitions/Document.json
+++ b/examples/Transitions/Document.json
@@ -15,7 +15,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -34,7 +34,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -42,7 +42,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -66,7 +66,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -85,7 +85,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -93,7 +93,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -117,7 +117,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -136,7 +136,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -144,7 +144,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -168,7 +168,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -187,7 +187,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -195,7 +195,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -219,7 +219,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -238,7 +238,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -246,7 +246,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -270,7 +270,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -289,7 +289,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -297,7 +297,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -321,7 +321,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -340,7 +340,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -348,7 +348,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -372,7 +372,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -391,7 +391,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -399,7 +399,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -423,7 +423,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -442,7 +442,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -450,7 +450,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -474,7 +474,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -493,7 +493,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -501,7 +501,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -525,7 +525,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -544,7 +544,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -552,7 +552,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -576,7 +576,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -595,7 +595,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -603,7 +603,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -627,7 +627,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -646,7 +646,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -654,7 +654,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -678,7 +678,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -697,7 +697,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -705,7 +705,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -729,7 +729,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -748,7 +748,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -756,7 +756,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -780,7 +780,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -799,7 +799,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -807,7 +807,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -831,7 +831,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -850,7 +850,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
@@ -858,7 +858,7 @@
           "blue" : "255",
           "font_weight" : "500",
           "red" : "255",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
@@ -882,7 +882,7 @@
           "height" : "200",
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "34",
           "align" : "left",
@@ -901,7 +901,7 @@
           "height" : "768",
           "font_style" : "Normal",
           "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.",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
@@ -909,7 +909,7 @@
           "blue" : "34",
           "font_weight" : "500",
           "red" : "34",
-          "ease_name" : "text",
+          "ease_name" : "EaseTextElement",
           "font_name" : "Sans",
           "font_size" : "16"
         }
diff --git a/src/Actor.vala b/src/Actor.vala
index e120d79..e8c9705 100644
--- a/src/Actor.vala
+++ b/src/Actor.vala
@@ -52,7 +52,7 @@ public abstract class Ease.Actor : Clutter.Group
 	 * @param e The { link Element} this Actor represents.
 	 * @param c The context of this Actor - sidebar, presentation, editor.
 	 */
-	public Actor(ref Element e, ActorContext c)
+	public Actor(Element e, ActorContext c)
 	{
 		element = e;
 		context = c;
diff --git a/src/Element.vala b/src/Element.vala
index 0d32a2c..0dba3d6 100644
--- a/src/Element.vala
+++ b/src/Element.vala
@@ -16,21 +16,40 @@
 */
 
 /**
- * An object on a { link Slide}
+ * An object on a { link Slide}.
  *
- * While there are several subclasses of { link Actor} for different types
- * of presentation objects, there is a single Element class. The Element
- * class uses an { link ElementMap} to store data. The "type" key
- * specifies the type of Element ("text", "image", "video", etc.)
- * 
- * For accessing data stored in the { link ElementMap}, Element provides
- * several convenience properties. Many of these are specific to a single
- * type of Element, such as the font_name property for text elements.
- * Accessing these properties in the wrong type of Element will cause
- * bad things to happen, including the heat death of the universe.
+ * Element is also used in { link Theme}s, which handle sizing differently.
+ *
+ * Ease uses a "base resolution" of 1024 by 768, a common projector resolution.
+ * Unlike { link Element}, which expresses positions in x, y, width, and height,
+ * MasterElement expressed them in left, right, top, and bottom. As the total
+ * size of the presentation is known, the actual sizes can be easily calculated.
+ * Then, the Element's size can be increased or decreased by using the
+ * four directional bind_ properties and the expand_.
+ *
+ * For example, the "header" font for a theme will often be selected so that a
+ * single line fits perfectly in the space allocated for the header. Therefore,
+ * increasing the height at a greater resolution is a waste of space, and
+ * decreasing the size at a lower resolution will cause the text not to fit in
+ * the box. Therefore, the header should be bound to the top, but not to the
+ * bottom. expand_vertically should be false.
+ *
+ * In contrast, the content box below the header is designed for an arbitrary
+ * amount of lines. Therefore, this box should scale vertically, shrinking as
+ * the presentation gets smaller, and enlarging as the presentation gets larger.
+ * To do this, both bind_bottom and bind_top should be true, as well as
+ * expand_vertically.
+ *
+ * While the bind_left and bind_right properties exist, it's not clear whether
+ * or not they will actually be useful at any point. They perform in the same
+ * manner as the other two bind_ properties, but for horizontal scaling. The
+ * same applies to the expand_horizontally property.
  */
-public class Ease.Element : GLib.Object
+public abstract class Ease.Element : GLib.Object
 {
+	private const float THEME_WIDTH = 800;
+	private const float THEME_HEIGHT = 600;
+
 	/**
 	 * The { link Slide} that this Element is a part of.
 	 */
@@ -41,7 +60,7 @@ public class Ease.Element : GLib.Object
 	 * directly though get() and set(), or though the typed convenience
 	 * properties that Element provides.
 	 */
-	public ElementMap data;
+	protected ElementMap data;
 	
 	/**
 	 * Create a new element, with an empty { link ElementMap}.
@@ -54,19 +73,12 @@ public class Ease.Element : GLib.Object
 	/**
 	 * Creates a completely empty Element, without an { link ElementMap}.
 	 */
-	private Element.empty() { }
+	public Element.empty() {}	
 	
 	/**
 	 * Creates and returns a copy of this Element.
 	 */
-	public Element copy()
-	{
-		var element = new Element.empty();
-		element.parent = parent;
-		element.data = data.copy();
-		
-		return element;
-	}
+	public abstract Element copy();
 	
 	/**
 	 * Create a new element.
@@ -103,6 +115,16 @@ public class Ease.Element : GLib.Object
 	}
 	
 	/**
+	 * Output this Element as JSON.
+	 * 
+	 * Returns a JSON object with the element's data.
+	 */
+	public Json.Node to_json()
+	{
+		return data.to_json();
+	}
+	
+	/**
 	 * Creates HTML markup for this Element.
 	 * 
 	 * The <div> tag for this Element is appended to the "HTML" parameter.
@@ -112,165 +134,94 @@ public class Ease.Element : GLib.Object
 	 * @param exporter The { link HTMLExporter}, for the path and progress.
 	 * @param amount The amount progress should increase by when done.
 	 */
-	public void to_html(ref string html,
-	                    HTMLExporter exporter,
-	                    double amount)
+	public virtual void to_html(ref string html,
+	                            HTMLExporter exporter,
+	                            double amount)
 	{
-		switch (data.get("element_type"))
-		{
-			case "image":
-				// open the img tag
-				html += "<img class=\"image element\" ";
-				
-				// set the image's style
-				html += "style=\"";
-				html += "left:" + data.get("x") + "px;";
-				html += " top:" + data.get("y") + "px;";
-				html += " width:" + data.get("width") + "px;";
-				html += " height:" + data.get("height") + "px;";
-				html += " position: absolute;\" ";
-				
-				// add the image
-				html += "src=\"" + exporter.path + " " +
-				        data.get("filename") + "\" alt=\"Image\" />";
-				
-				// copy the image file
-				exporter.copy_file(data.get("filename"),
-				                   parent.parent.path);
-				
-				break;
-				
-			case "text":
-				// open the tag
-				html += "<div class=\"text element\" ";
-				
-				// set the size and position of the element
-				html += "style=\"";
-				html += "left:" + data.get("x") + "px;";
-				html += " top:" + data.get("y") + "px;";
-				html += " width:" + data.get("width") + "px;";
-				html += " height:" + data.get("height") + "px;";
-				html += " position: absolute;";
-				
-				// set the text-specific properties of the element
-				html += " color:" + 
-				        @"rgb($(color.red),$(color.green),$(color.blue));";
-				        
-				html += " font-family:'" + data.get("font_name") +
-				        "', sans-serif;";
-				        
-				html += " font-size:" + data.get("font_size") + "pt;";
-				
-				html += " font-weight:" + data.get("font_weight") + ";";
-				html += " font-style:" + data.get("font_style").down() +
-				        ";";
-				        
-				html += " text-align:" + data.get("align") + ";\"";
-				
-				// write the actual content
-				html += ">" + data.get("text").replace("\n", "<br />") +
-				        "</div>";
-				
-				break;
-				
-			case "video":
-				// open the tag
-				html += "<video class=\"video element\" ";
-				
-				// set the video's style
-				html += "style=\"";
-				html += "left:" + data.get("x") + "px;";
-				html += " top:" + data.get("y") + "px;";
-				html += " position: absolute;\" ";
-				
-				// set the video's size
-				html += " width=\"" + data.get("width") + "\" ";
-				html += " height=\"" + data.get("height") + "\" ";
-				
-				// set the video's source and controls
-				html += "src=\"" + exporter.path + " " +
-				        data.get("filename") + "\" " +
-				        "controls=\"yes\">" +
-				        _("Your browser does not support the video tag") + 
-				        "</video>";
-				        
-				// copy the video file
-				exporter.copy_file(data.get("filename"),
-				                   parent.parent.path);
-				
-				break;
-		}
+		// write the markup
+		write_html(ref html, exporter);
 		
 		// advance the progress bar
 		exporter.add_progress(amount);
 	}
 	
-	/**
-	 * Renders this Element with Cairo.
-	 */
-	public void pdf_render(Cairo.Context context) throws Error
-	{
-		switch (data.get("element_type"))
-		{
-			case "image":
-				pdf_render_image(context);
-				break;
-			case "text":
-				pdf_render_text(context);
-				break;
-		}
-	}
+	protected abstract void write_html(ref string html, HTMLExporter exporter);
+	
+	public abstract void cairo_render(Cairo.Context context) throws Error;
+	
+	public abstract Actor actor(ActorContext c);
 	
 	/**
-	 * Renders an image Element with Cairo.
+	 * Creates a new Element from this Element at the specified size.
+	 *
+	 * @param w The width of the { link Document} the new Element will be a
+	 * part of.
+	 * @param w The height of the { link Document} the new Element will be a
+	 * part of.
 	 */
-	private void pdf_render_image(Cairo.Context context) throws Error
+	public Element sized_element(float w, float h)
 	{
-		var filename = Path.build_path("/",
-		                               parent.parent.path,
-		                               data.get("filename"));
+		// copy this element
+		var element = copy();
 		
-		// load the image
-		var pixbuf = new Gdk.Pixbuf.from_file_at_scale(filename,
-		                                               (int)width,
-		                                               (int)height,
-		                                               false);
+		// find the differences in each direction for the new resolution
+		var x_diff = (w - THEME_WIDTH) / 2;
+		var y_diff = (h - THEME_HEIGHT) / 2;
 		
-		Gdk.cairo_set_source_pixbuf(context, pixbuf, x, y);
+		// set the base size (at 1024x768)
+		element.width = THEME_WIDTH - left - right;
+		element.height = THEME_HEIGHT - top - bottom;
 		
-		context.rectangle(x, y, width, height);
-		context.fill();
-	}
-	
-	/**
-	 * Renders a text Element with Cairo.
-	 */
-	private void pdf_render_text(Cairo.Context context) throws Error
-	{	
-		// create the layout
-		var layout = Pango.cairo_create_layout(context);
-		layout.set_text(data.get("text"), (int)data.get("text").length);
-		layout.set_width((int)(width * Pango.SCALE));
-		layout.set_height((int)(height * Pango.SCALE));
-		layout.set_font_description(font_description);
+		// handle binding to the left
+		if (bind_left)
+		{
+			element.x = left;
+			
+			if (expand_horizontally)
+			{
+				element.width += x_diff;
+			}
+		}
+		else
+		{
+			element.x = left + x_diff; 
+		}
 		
-		// render
-		context.save();
+		// handle binding to the top
+		if (bind_top)
+		{
+			element.y = top;
+			
+			if (expand_vertically)
+			{
+				element.height += y_diff;
+			}
+		}
+		else
+		{
+			element.y = top + y_diff;
+		}
 		
-		context.set_source_rgb(color.red / 255f,
-		                       color.green / 255f,
-		                       color.blue / 255f);
+		// handle binding to the right
+		if (bind_right)
+		{	
+			if (expand_horizontally)
+			{
+				element.width += x_diff;
+			}
+		}
 		
-		Pango.cairo_update_layout(context, layout);
-		context.move_to((int)x, (int)y);
+		// handle binding to the bottom
+		if (bind_bottom)
+		{	
+			if (expand_vertically)
+			{
+				element.height += y_diff;
+			}
+		}
 		
-		Pango.cairo_show_layout(context, layout);
-		context.restore();
+		return element;
 	}
-
-	// convenience properties
-
-	// base element
 	
 	/**
 	 * A unique identifier for this Element.
@@ -310,7 +261,7 @@ public class Ease.Element : GLib.Object
 		}
 		set
 		{
-			data.set("x", @"$value");
+			data.set("x", value.to_string());
 		}
 	}
 	
@@ -325,7 +276,7 @@ public class Ease.Element : GLib.Object
 		}
 		set
 		{
-			data.set("y", @"$value");
+			data.set("y", value.to_string());
 		}
 	}
 	
@@ -340,7 +291,7 @@ public class Ease.Element : GLib.Object
 		}
 		set
 		{
-			data.set("width", @"$value");
+			data.set("width", value.to_string());
 		}
 	}
 	
@@ -355,225 +306,122 @@ public class Ease.Element : GLib.Object
 		}
 		set
 		{
-			data.set("height", @"$value");
+			data.set("height", value.to_string());
 		}
 	}
-
-	// text elements
 	
 	/**
-	 * The text value of this Element. Only available for "text" Elements.
-	 */
-	public string text
-	{
-		owned get { return data.get("text"); }
-		set	{ data.set("text", value); }
-	}
-	
-	/**
-	 * The color of the text. Only available for "text" Elements.
+	 * If the Element should maintain "top" when resized.
+	 *
+	 * To scale to different resolutions, MasterElement tracks the distance of
+	 * Elements from each edge, and maintains them as these edges expand if the
+	 * appropriate bind_ and expand_ properties are true.
 	 */
-	public Clutter.Color color
+	public bool bind_top
 	{
-		get
-		{
-			return { (uchar)data.get("red").to_int(),
-			         (uchar)data.get("green").to_int(),
-			         (uchar)data.get("blue").to_int(),
-			         255};
-		}		
-		set
-		{
-			data.set("red", ((int)value.red).to_string());
-			data.set("green", ((int)value.green).to_string());
-			data.set("blue", ((int)value.blue).to_string());
-		}
+		get { return data.get("bind_top").to_bool(); }
+		set { data.set("bind_top", value.to_string()); }
 	}
 	
 	/**
-	 * The name of the text's font family. Only available for "text" Elements.
+	 * If the Element should maintain "bottom" when resized.
+	 *
+	 * To scale to different resolutions, MasterElement tracks the distance of
+	 * Elements from each edge, and maintains them as these edges expand if the
+	 * appropriate bind_ and expand_ properties are true.
 	 */
-	public string font_name
+	public bool bind_bottom
 	{
-		set
-		{
-			data.set("font_name", value);
-		}
+		get { return data.get("bind_bottom").to_bool(); }
+		set { data.set("bind_bottom", value.to_string()); }
 	}
 	
 	/**
-	 * The PangoStyle for this Element. Only available for "text" Elements.
+	 * If the Element should maintain "left" when resized.
+	 *
+	 * To scale to different resolutions, MasterElement tracks the distance of
+	 * Elements from each edge, and maintains them as these edges expand if the
+	 * appropriate bind_ and expand_ properties are true.
 	 */
-	public Pango.Style font_style
+	public bool bind_left
 	{
-		get
-		{
-			switch (data.get("font_style"))
-			{
-				case "Oblique":
-					return Pango.Style.OBLIQUE;
-				case "Italic":
-					return Pango.Style.ITALIC;
-				default:
-					return Pango.Style.NORMAL;
-			}
-		}
-		set
-		{
-			switch (value)
-			{
-				case Pango.Style.OBLIQUE:
-					data.set("font_style", "Oblique");
-					break;
-				case Pango.Style.ITALIC:
-					data.set("font_style", "Italic");
-					break;
-				case Pango.Style.NORMAL:
-					data.set("font_style", "Normal");
-					break;
-			}
-		}
+		get { return data.get("bind_left").to_bool(); }
+		set { data.set("bind_left", value.to_string()); }
 	}
 	
 	/**
-	 * The PangoVariant for this Element. Only available for "text" Elements.
+	 * If the Element should maintain "right" when resized.
+	 *
+	 * To scale to different resolutions, MasterElement tracks the distance of
+	 * Elements from each edge, and maintains them as these edges expand if the
+	 * appropriate bind_ and expand_ properties are true.
 	 */
-	public Pango.Variant font_variant
+	public bool bind_right
 	{
-		get
-		{
-			return data.get("font_variant") == "Normal"
-			     ? Pango.Variant.NORMAL
-			     : Pango.Variant.SMALL_CAPS;
-		}
-		set
-		{
-			data.set("font_name",
-			             value == Pango.Variant.NORMAL ?
-			                      "Normal" : "Small Caps");
-		}
+		get { return data.get("bind_right").to_bool(); }
+		set { data.set("bind_right", value.to_string()); }
 	}
 	
 	/**
-	 * The font's weight. Only available for "text" Elements.
+	 * If the Element should expand horizontally when resized.
+	 *
+	 * To scale to different resolutions, MasterElement tracks the distance of
+	 * Elements from each edge, and maintains them as these edges expand if the
+	 * appropriate bind_ and expand_ properties are true.
 	 */
-	public Pango.Weight font_weight
+	public bool expand_horizontally
 	{
-		get
-		{
-			var str = "font_name";
-			return (Pango.Weight)(data.get(str).to_int());
-		}
-		set
-		{
-			data.set("font_weight", ((int)value).to_string());
-		}
+		get { return data.get("expand_horizontally").to_bool(); }
+		set { data.set("expand_horizontally", value.to_string()); }
 	}
 	
 	/**
-	 * A full PangoFontDescription for this Element.
+	 * If the Element should expand vertically when resized.
 	 *
-	 * This property creates a new FontDescription when retrieved, and
-	 * sets all appropriate properties (font_weight, etc.) when set. Only
-	 * available for "text" Elements.
+	 * To scale to different resolutions, MasterElement tracks the distance of
+	 * Elements from each edge, and maintains them as these edges expand if the
+	 * appropriate bind_ and expand_ properties are true.
 	 */
-	public Pango.FontDescription font_description
+	public bool expand_vertically
 	{
-		owned get
-		{
-			var desc = new Pango.FontDescription();
-			desc.set_family(data.get("font_name"));
-			desc.set_style(font_style);
-			desc.set_weight(font_weight);
-			desc.set_variant(font_variant);
-			desc.set_size(font_size * Pango.SCALE);
-			
-			return desc;
-		}
-		set
-		{
-			data.set("font_name", value.get_family());
-			font_style = value.get_style();
-			font_weight = value.get_weight();
-			font_variant = value.get_variant();
-			font_size = value.get_size() / Pango.SCALE;
-		}
+		get { return data.get("expand_vertically").to_bool(); }
+		set { data.set("expand_vertically", value.to_string()); }
 	}
 	
 	/**
-	 * The alignment of the text. Only available for "text" Elements.
+	 * The Element's distance from the top of the screen.
 	 */
-	public Pango.Alignment text_align
+	public float top
 	{
-		get
-		{
-			switch (data.get("align"))
-			{
-				case "right":
-					return Pango.Alignment.RIGHT;
-				case "center":
-					return Pango.Alignment.CENTER;
-				default:
-					return Pango.Alignment.LEFT;
-			}
-		}
-		set
-		{
-			switch (value)
-			{
-				case Pango.Alignment.RIGHT:
-					data.set("font_style", "right");
-					break;
-				case Pango.Alignment.CENTER:
-					data.set("font_style", "center");
-					break;
-				case Pango.Alignment.LEFT:
-					data.set("font_style", "left");
-					break;
-			}
-		}
+		get { return (float)data.get("top").to_double(); }
+		set { data.set("top", value.to_string()); }
 	}
 	
 	/**
-	 * The size of the font. Only available for "text" Elements.
-	 *
-	 * This value should be multiplied by Pango.SCALE for rendering, otherwise
-	 * the text will be far too small to be visible.
+	 * The Element's distance from the bottom of the screen.
 	 */
-	public int font_size
+	public float bottom
 	{
-		get
-		{
-			return data.get("font_size").to_int();
-		}
-		set
-		{
-			data.set("font_size", @"$value");
-		}
+		get { return (float)data.get("bottom").to_double(); }
+		set { data.set("bottom", value.to_string()); }
 	}
-
-	// image and video elements
 	
 	/**
-	 * The path to a media file. Applies to "image" and "video" Elements.
+	 * The Element's distance from the left edge of the screen.
 	 */
-	public string filename
+	public float left
 	{
-		owned get { return data.get("filename"); }
-		set	{ data.set("filename", value); }
+		get { return (float)data.get("left").to_double(); }
+		set { data.set("left", value.to_string()); }
 	}
 	
 	/**
-	 * The full path to a media file. Applies to "image" and "video"
-	 * Elements. Cannot be set.
+	 * The Element's distance from the right edge of the screen.
 	 */
-	public string full_filename
+	public float right
 	{
-		owned get
-		{
-			var str = Path.build_filename(parent.parent.path, filename);
-			return str;
-		}
+		get { return (float)data.get("right").to_double(); }
+		set { data.set("right", value.to_string()); }
 	}
 }
 
diff --git a/src/ImageActor.vala b/src/ImageActor.vala
index 5e146c1..71708b8 100644
--- a/src/ImageActor.vala
+++ b/src/ImageActor.vala
@@ -34,9 +34,9 @@ public class Ease.ImageActor : Actor
 	 * @param e The represented element.
 	 * @param c The context of this Actor (Presentation, Sidebar, Editor)
 	 */
-	public ImageActor(ref Element e, ActorContext c)
+	public ImageActor(ImageElement e, ActorContext c)
 	{
-		base(ref e, c);
+		base(e, c);
 		
 		try
 		{
diff --git a/src/ImageElement.vala b/src/ImageElement.vala
new file mode 100644
index 0000000..abd5b86
--- /dev/null
+++ b/src/ImageElement.vala
@@ -0,0 +1,76 @@
+/*  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/>.
+*/
+
+public class Ease.ImageElement : MediaElement
+{	
+	public override Element copy()
+	{
+		var element = new ImageElement.empty();
+		element.parent = parent;
+		element.data = data.copy();
+		
+		return element;
+	}
+	
+	public override Actor actor(ActorContext c)
+	{
+		return new ImageActor(this, c);
+	}
+	
+	public override void write_html(ref string html, HTMLExporter exporter)
+	{
+		// open the img tag
+		html += "<img class=\"image element\" ";
+		
+		// set the image's style
+		html += "style=\"";
+		html += "left:" + data.get("x") + "px;";
+		html += " top:" + data.get("y") + "px;";
+		html += " width:" + data.get("width") + "px;";
+		html += " height:" + data.get("height") + "px;";
+		html += " position: absolute;\" ";
+		
+		// add the image
+		html += "src=\"" + exporter.path + " " +
+		        data.get("filename") + "\" alt=\"Image\" />";
+		
+		// copy the image file
+		exporter.copy_file(data.get("filename"),
+		                   parent.parent.path);
+	}
+
+	/**
+	 * Renders an image Element with Cairo.
+	 */
+	public override void cairo_render(Cairo.Context context) throws Error
+	{
+		var filename = Path.build_path("/",
+		                               parent.parent.path,
+		                               data.get("filename"));
+		
+		// load the image
+		var pixbuf = new Gdk.Pixbuf.from_file_at_scale(filename,
+		                                               (int)width,
+		                                               (int)height,
+		                                               false);
+		
+		Gdk.cairo_set_source_pixbuf(context, pixbuf, x, y);
+		
+		context.rectangle(x, y, width, height);
+		context.fill();
+	}
+}
diff --git a/src/JSONParser.vala b/src/JSONParser.vala
index f1044f4..aeb36c7 100644
--- a/src/JSONParser.vala
+++ b/src/JSONParser.vala
@@ -19,7 +19,10 @@
  * Parses JSON files to load Ease { link Document}s
  */
 public static class Ease.JSONParser
-{	
+{
+	private const Type VIDEO_TYPE = typeof(VideoElement);
+	private const Type IMAGE_TYPE = typeof(ImageElement);
+
 	/**
 	 * Parses a document JSON file, creating a { link Document}.
 	 *
@@ -51,7 +54,7 @@ public static class Ease.JSONParser
 		for (var i = 0; i < slides.get_length(); i++)
 		{
 			var node = slides.get_object_element(i);
-			document.add_slide(document.length, parse_slide(node, false));
+			document.add_slide(document.length, parse_slide(node));
 		}
 		
 		return document;
@@ -85,13 +88,13 @@ public static class Ease.JSONParser
 		for (var i = 0; i < slides.get_length(); i++)
 		{
 			var node = slides.get_object_element(i);
-			theme.add_slide(theme.length, parse_slide(node, true));
+			theme.add_slide(theme.length, parse_slide(node));
 		}
 		
 		return theme;
 	}
 	
-	private static Slide parse_slide(Json.Object obj, bool theme)
+	private static Slide parse_slide(Json.Object obj)
 	{
 		var slide = new Slide();
 		
@@ -138,30 +141,33 @@ public static class Ease.JSONParser
 		for (var i = 0; i < elements.get_length(); i++)
 		{
 			var node = elements.get_object_element(i);
-			slide.add_element(slide.count, parse_element(node, theme));
+			slide.add_element(slide.count, parse_element(node));
 		}
 		
 		return slide;
 	}
 	
-	private static Element parse_element(Json.Object obj, bool theme)
+	private static Element parse_element(Json.Object obj)
 	{
 		Element element;
-		if (theme)
-		{
-			element = new MasterElement();
-		}
+		
+		// find the proper type
+		var type = Type.from_name(obj.get_string_member("element_type"));
+		
+		// create the element
+		if (type == VIDEO_TYPE)
+			element = new VideoElement();
+		else if (type == IMAGE_TYPE)
+			element = new ImageElement();
 		else
-		{
-			element = new Element();
-		}
+			element = new TextElement();
 		
 		// set the Element's properties
 		for (unowned List<string>* itr = obj.get_members();
 		     itr != null; itr = itr->next)
 		{
 			string name = itr->data;
-			element.data.set(name, obj.get_member(name).get_string());
+			element.set(name, obj.get_member(name).get_string());
 		}
 		
 		return element;
@@ -239,7 +245,7 @@ public static class Ease.JSONParser
 		var elements = new Json.Array();
 		foreach (var e in slide.elements)
 		{
-			elements.add_element(e.data.to_json());
+			elements.add_element(e.to_json());
 		}
 
 		obj.set_array_member("elements", elements);
diff --git a/src/MediaElement.vala b/src/MediaElement.vala
new file mode 100644
index 0000000..67fdf42
--- /dev/null
+++ b/src/MediaElement.vala
@@ -0,0 +1,44 @@
+/*  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/>.
+*/
+
+/**
+ * An abstract subclass of { link Element} with a path to a media file.
+ */
+public abstract class Ease.MediaElement : Element
+{
+	/**
+	 * The path to a media file. Applies to "image" and "video" Elements.
+	 */
+	public string filename
+	{
+		owned get { return data.get("filename"); }
+		set	{ data.set("filename", value); }
+	}
+	
+	/**
+	 * The full path to a media file. Applies to "image" and "video"
+	 * Elements. Cannot be set.
+	 */
+	public string full_filename
+	{
+		owned get
+		{
+			var str = Path.build_filename(parent.parent.path, filename);
+			return str;
+		}
+	}
+}
diff --git a/src/PDFExporter.vala b/src/PDFExporter.vala
index 5773ddc..4ffc97d 100644
--- a/src/PDFExporter.vala
+++ b/src/PDFExporter.vala
@@ -108,7 +108,7 @@ public static class Ease.PDFExporter : Object
 		
 		foreach (var e in s.elements)
 		{
-			e.pdf_render(context);
+			e.cairo_render(context);
 		}
 	}
 }
diff --git a/src/Slide.vala b/src/Slide.vala
index 6c58422..33f3e97 100644
--- a/src/Slide.vala
+++ b/src/Slide.vala
@@ -198,7 +198,7 @@ public class Ease.Slide : GLib.Object
 		// add all of the master Slide's elements
 		foreach (var e in master.elements)
 		{
-			elements.add(((MasterElement)e).sized_element(width, height));
+			elements.add(e.sized_element(width, height));
 		}
 	}
 	
diff --git a/src/SlideActor.vala b/src/SlideActor.vala
index 95c6100..2620bca 100644
--- a/src/SlideActor.vala
+++ b/src/SlideActor.vala
@@ -167,19 +167,7 @@ public class Ease.SlideActor : Clutter.Group
 
 		foreach (var e in slide.elements)
 		{
-			// load the proper type of actor
-			switch (e.get("element_type"))
-			{
-				case "image":
-					contents.add_actor(new ImageActor(ref e, context));
-					break;
-				case "text":
-					contents.add_actor(new TextActor(ref e, context));
-					break;
-				case "video":
-					contents.add_actor(new VideoActor(ref e, context));
-					break;
-			}
+			contents.add_actor(e.actor(context));
 		}
 
 		add_actor(contents);
diff --git a/src/TextActor.vala b/src/TextActor.vala
index 399311e..72ceb2a 100644
--- a/src/TextActor.vala
+++ b/src/TextActor.vala
@@ -30,9 +30,9 @@ public class Ease.TextActor : Actor
 	 * @param e The represented element.
 	 * @param c The context of this Actor (Presentation, Sidebar, Editor)
 	 */
-	public TextActor(ref Element e, ActorContext c)
+	public TextActor(TextElement e, ActorContext c)
 	{
-		base(ref e, c);
+		base(e, c);
 		
 		var text = new Clutter.Text();
 
diff --git a/src/TextElement.vala b/src/TextElement.vala
new file mode 100644
index 0000000..851d451
--- /dev/null
+++ b/src/TextElement.vala
@@ -0,0 +1,282 @@
+/*  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/>.
+*/
+
+public class Ease.TextElement : Element
+{		
+	public override Element copy()
+	{
+		var element = new TextElement.empty();
+		element.parent = parent;
+		element.data = data.copy();
+		
+		return element;
+	}
+	
+	public override Actor actor(ActorContext c)
+	{
+		return new TextActor(this, c);
+	}
+
+	protected override void write_html(ref string html, HTMLExporter exporter)
+	{
+		// open the tag
+		html += "<div class=\"text element\" ";
+		
+		// set the size and position of the element
+		html += "style=\"";
+		html += "left:" + data.get("x") + "px;";
+		html += " top:" + data.get("y") + "px;";
+		html += " width:" + data.get("width") + "px;";
+		html += " height:" + data.get("height") + "px;";
+		html += " position: absolute;";
+		
+		// set the text-specific properties of the element
+		html += " color:" + 
+		        @"rgb($(color.red),$(color.green),$(color.blue));";
+		        
+		html += " font-family:'" + data.get("font_name") +
+		        "', sans-serif;";
+		        
+		html += " font-size:" + data.get("font_size") + "pt;";
+		
+		html += " font-weight:" + data.get("font_weight") + ";";
+		html += " font-style:" + data.get("font_style").down() +
+		        ";";
+		        
+		html += " text-align:" + data.get("align") + ";\"";
+		
+		// write the actual content
+		html += ">" + data.get("text").replace("\n", "<br />") +
+		        "</div>";
+	}
+
+	/**
+	 * Renders a text Element with Cairo.
+	 */
+	public override void cairo_render(Cairo.Context context) throws Error
+	{	
+		// create the layout
+		var layout = Pango.cairo_create_layout(context);
+		layout.set_text(data.get("text"), (int)data.get("text").length);
+		layout.set_width((int)(width * Pango.SCALE));
+		layout.set_height((int)(height * Pango.SCALE));
+		layout.set_font_description(font_description);
+		
+		// render
+		context.save();
+		
+		context.set_source_rgb(color.red / 255f,
+		                       color.green / 255f,
+		                       color.blue / 255f);
+		
+		Pango.cairo_update_layout(context, layout);
+		context.move_to((int)x, (int)y);
+		
+		Pango.cairo_show_layout(context, layout);
+		context.restore();
+	}
+	
+	/**
+	 * The text value of this Element.
+	 */
+	public string text
+	{
+		owned get { return data.get("text"); }
+		set	{ data.set("text", value); }
+	}
+	
+	/**
+	 * The color of the text.
+	 */
+	public Clutter.Color color
+	{
+		get
+		{
+			return { (uchar)data.get("red").to_int(),
+			         (uchar)data.get("green").to_int(),
+			         (uchar)data.get("blue").to_int(),
+			         255};
+		}		
+		set
+		{
+			data.set("red", ((int)value.red).to_string());
+			data.set("green", ((int)value.green).to_string());
+			data.set("blue", ((int)value.blue).to_string());
+		}
+	}
+	
+	/**
+	 * The name of the text's font family.
+	 */
+	public string font_name
+	{
+		set
+		{
+			data.set("font_name", value);
+		}
+	}
+	
+	/**
+	 * The PangoStyle for this Element.
+	 */
+	public Pango.Style font_style
+	{
+		get
+		{
+			switch (data.get("font_style"))
+			{
+				case "Oblique":
+					return Pango.Style.OBLIQUE;
+				case "Italic":
+					return Pango.Style.ITALIC;
+				default:
+					return Pango.Style.NORMAL;
+			}
+		}
+		set
+		{
+			switch (value)
+			{
+				case Pango.Style.OBLIQUE:
+					data.set("font_style", "Oblique");
+					break;
+				case Pango.Style.ITALIC:
+					data.set("font_style", "Italic");
+					break;
+				case Pango.Style.NORMAL:
+					data.set("font_style", "Normal");
+					break;
+			}
+		}
+	}
+	
+	/**
+	 * The PangoVariant for this Element.
+	 */
+	public Pango.Variant font_variant
+	{
+		get
+		{
+			return data.get("font_variant") == "Normal"
+			     ? Pango.Variant.NORMAL
+			     : Pango.Variant.SMALL_CAPS;
+		}
+		set
+		{
+			data.set("font_name",
+			             value == Pango.Variant.NORMAL ?
+			                      "Normal" : "Small Caps");
+		}
+	}
+	
+	/**
+	 * The font's weight.
+	 */
+	public Pango.Weight font_weight
+	{
+		get
+		{
+			var str = "font_name";
+			return (Pango.Weight)(data.get(str).to_int());
+		}
+		set
+		{
+			data.set("font_weight", ((int)value).to_string());
+		}
+	}
+	
+	/**
+	 * A full PangoFontDescription for this Element.
+	 *
+	 * This property creates a new FontDescription when retrieved, and
+	 * sets all appropriate properties (font_weight, etc.) when set.
+	 */
+	public Pango.FontDescription font_description
+	{
+		owned get
+		{
+			var desc = new Pango.FontDescription();
+			desc.set_family(data.get("font_name"));
+			desc.set_style(font_style);
+			desc.set_weight(font_weight);
+			desc.set_variant(font_variant);
+			desc.set_size(font_size * Pango.SCALE);
+			
+			return desc;
+		}
+		set
+		{
+			data.set("font_name", value.get_family());
+			font_style = value.get_style();
+			font_weight = value.get_weight();
+			font_variant = value.get_variant();
+			font_size = value.get_size() / Pango.SCALE;
+		}
+	}
+	
+	/**
+	 * The alignment of the text.
+	 */
+	public Pango.Alignment text_align
+	{
+		get
+		{
+			switch (data.get("align"))
+			{
+				case "right":
+					return Pango.Alignment.RIGHT;
+				case "center":
+					return Pango.Alignment.CENTER;
+				default:
+					return Pango.Alignment.LEFT;
+			}
+		}
+		set
+		{
+			switch (value)
+			{
+				case Pango.Alignment.RIGHT:
+					data.set("font_style", "right");
+					break;
+				case Pango.Alignment.CENTER:
+					data.set("font_style", "center");
+					break;
+				case Pango.Alignment.LEFT:
+					data.set("font_style", "left");
+					break;
+			}
+		}
+	}
+	
+	/**
+	 * The size of the font.
+	 *
+	 * This value should be multiplied by Pango.SCALE for rendering, otherwise
+	 * the text will be far too small to be visible.
+	 */
+	public int font_size
+	{
+		get
+		{
+			return data.get("font_size").to_int();
+		}
+		set
+		{
+			data.set("font_size", @"$value");
+		}
+	}
+}
diff --git a/src/VideoActor.vala b/src/VideoActor.vala
index 57b55c6..94ca2c0 100644
--- a/src/VideoActor.vala
+++ b/src/VideoActor.vala
@@ -41,9 +41,9 @@ public class Ease.VideoActor : Actor, Clutter.Media
 	 * @param e The represented element.
 	 * @param c The context of this Actor (Presentation, Sidebar, Editor)
 	 */
-	public VideoActor(ref Element e, ActorContext c)
+	public VideoActor(VideoElement e, ActorContext c)
 	{
-		base(ref e, c);
+		base(e, c);
 
 		video = new ClutterGst.VideoTexture();
 		video.set_filename(Path.build_filename(e.parent.parent.path,
diff --git a/src/VideoElement.vala b/src/VideoElement.vala
new file mode 100644
index 0000000..8862eef
--- /dev/null
+++ b/src/VideoElement.vala
@@ -0,0 +1,66 @@
+/*  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/>.
+*/
+
+public class Ease.VideoElement : MediaElement
+{	
+	public override Element copy()
+	{
+		var element = new VideoElement.empty();
+		element.parent = parent;
+		element.data = data.copy();
+		
+		return element;
+	}
+	
+	public override Actor actor(ActorContext c)
+	{
+		return new VideoActor(this, c);
+	}
+	
+	public override void write_html(ref string html, HTMLExporter exporter)
+	{
+		// open the tag
+		html += "<video class=\"video element\" ";
+		
+		// set the video's style
+		html += "style=\"";
+		html += "left:" + data.get("x") + "px;";
+		html += " top:" + data.get("y") + "px;";
+		html += " position: absolute;\" ";
+		
+		// set the video's size
+		html += " width=\"" + data.get("width") + "\" ";
+		html += " height=\"" + data.get("height") + "\" ";
+		
+		// set the video's source and controls
+		html += "src=\"" + exporter.path + " " +
+		        data.get("filename") + "\" " +
+		        "controls=\"yes\">" +
+		        _("Your browser does not support the video tag") + 
+		        "</video>";
+		        
+		// copy the video file
+		exporter.copy_file(data.get("filename"),
+		                   parent.parent.path);
+	}
+
+	public override void cairo_render(Cairo.Context context) throws Error
+	{
+		stdout.printf("Video rendering not supported yet...\n");
+	}
+}
+
diff --git a/themes/Pink/Theme.json b/themes/Pink/Theme.json
index a34c60c..af1fb3d 100644
--- a/themes/Pink/Theme.json
+++ b/themes/Pink/Theme.json
@@ -10,7 +10,7 @@
         {
           "font_variant" : "Normal",
           "font_style" : "Normal",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
@@ -36,7 +36,7 @@
         {
           "font_variant" : "Normal",
           "font_style" : "Normal",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "255",
           "align" : "left",
diff --git a/themes/White/Theme.json b/themes/White/Theme.json
index 61ac5dd..7d20ba4 100644
--- a/themes/White/Theme.json
+++ b/themes/White/Theme.json
@@ -11,7 +11,7 @@
         {
           "font_variant" : "Normal",
           "font_style" : "Normal",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "0",
           "align" : "left",
@@ -37,7 +37,7 @@
         {
           "font_variant" : "Normal",
           "font_style" : "Normal",
-          "element_type" : "text",
+          "element_type" : "EaseTextElement",
           "identifier" : "header",
           "green" : "0",
           "align" : "left",



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