[ease/inspector] Added the beginnings of support for themes.



commit 87a51d1dc1001d025ef17a7dbbc26fc7b850e43a
Author: Nate Stedman <natesm gmail com>
Date:   Thu Jun 3 05:01:19 2010 -0400

    Added the beginnings of support for themes.
    
    Removed Example.ease (it's fairly obsolete at this point)

 Examples/Example.ease/Document.json                |  117 --------------------
 .../Example.ease/Media/Themes/Pink/Background.svg  |   11 --
 Examples/Example.ease/Media/psyduck.jpg            |  Bin 79094 -> 0 bytes
 Examples/Transitions.ease/Document.json            |   56 +++++++++-
 src/EditorWindow.vala                              |   48 +++++++-
 src/Element.vala                                   |   54 +++++++++-
 src/ElementMap.vala                                |   26 ++++-
 src/JSONParser.vala                                |   47 +++++++-
 src/Main.vala                                      |    2 +-
 src/PDFExporter.vala                               |    2 +-
 src/Slide.vala                                     |   62 ++++++++++
 src/SlideActor.vala                                |   22 +++-
 src/SlideButton.vala                               |   95 ++++++++--------
 src/SlideButtonPanel.vala                          |   55 +++++++++-
 src/SlideSet.vala                                  |   47 ++++++++-
 src/WelcomeActor.vala                              |   84 ++++++++++++--
 src/WelcomeWindow.vala                             |  106 ++++++++++++++----
 themes/White.easetheme/Theme.json                  |   56 ++++++++++
 18 files changed, 652 insertions(+), 238 deletions(-)
---
diff --git a/Examples/Transitions.ease/Document.json b/Examples/Transitions.ease/Document.json
index b1f5529..63a6177 100644
--- a/Examples/Transitions.ease/Document.json
+++ b/Examples/Transitions.ease/Document.json
@@ -16,6 +16,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -34,6 +35,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -46,6 +48,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -64,6 +67,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -82,6 +86,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -94,6 +99,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -112,6 +118,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -130,6 +137,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -142,6 +150,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -160,6 +169,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -178,6 +188,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -190,6 +201,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -208,6 +220,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -226,6 +239,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -238,6 +252,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -256,6 +271,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -274,6 +290,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -286,6 +303,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -304,6 +322,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -322,6 +341,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -334,6 +354,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -352,6 +373,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -370,6 +392,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -382,6 +405,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -400,6 +424,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -418,6 +443,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -430,6 +456,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -448,6 +475,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -466,6 +494,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -478,6 +507,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -496,6 +526,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -514,6 +545,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -526,6 +558,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -544,6 +577,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -562,6 +596,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -574,6 +609,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -592,6 +628,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -610,6 +647,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -622,6 +660,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -640,6 +679,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -658,6 +698,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -670,6 +711,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -688,6 +730,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -706,6 +749,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -718,6 +762,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -736,6 +781,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -754,6 +800,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -766,6 +813,7 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     },
@@ -784,6 +832,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -802,6 +851,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "255",
           "align" : "left",
           "width" : "964",
@@ -814,6 +864,7 @@
         }
       ],
       "blue" : "34",
+      "title" : "Standard",
       "red" : "34",
       "transition_time" : "1"
     },
@@ -832,6 +883,7 @@
           "font_style" : "Normal",
           "text" : "Lorem Ipsum Dolor",
           "element_type" : "text",
+          "identifier" : "header",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -850,6 +902,7 @@
           "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",
+          "identifier" : "main-text",
           "green" : "34",
           "align" : "left",
           "width" : "964",
@@ -862,9 +915,10 @@
         }
       ],
       "blue" : "255",
+      "title" : "Standard",
       "red" : "255",
       "transition_time" : "1"
     }
   ],
   "width" : "1024"
-}
\ No newline at end of file
+}
diff --git a/src/EditorWindow.vala b/src/EditorWindow.vala
index 2ab1fce..32b5648 100644
--- a/src/EditorWindow.vala
+++ b/src/EditorWindow.vala
@@ -29,7 +29,7 @@ public class Ease.EditorWindow : Gtk.Window
 	public EditorEmbed embed;
 	public MainToolbar main_toolbar;
 	public Gtk.HBox slides;
-	public SlidePane pane_slide;
+	public SlideButtonPanel slide_button_panel;
 	
 	// zoom
 	public ZoomSlider zoom_slider;
@@ -68,7 +68,7 @@ public class Ease.EditorWindow : Gtk.Window
 		document = doc;
 		
 		// slide display
-		var slides_win = new SlideButtonPanel(document, this);
+		slide_button_panel = new SlideButtonPanel(document, this);
 		
 		// undo controller
 		undo = new UndoController();
@@ -78,10 +78,9 @@ public class Ease.EditorWindow : Gtk.Window
 		
 		// assemble middle contents			
 		var hbox = new Gtk.HBox(false, 0);
-		var hpaned = new Gtk.HPaned();
-		hpaned.pack1(slides_win, false, false);
-		hpaned.pack2(embed, true, true);
-		hbox.pack_start(hpaned, true, true, 0);
+		hbox.pack_start(slide_button_panel, false, false, 0);
+		hbox.pack_start(new Gtk.VSeparator(), false, false, 0);
+		hbox.pack_start(embed, true, true, 0);
 		
 		// assemble window contents
 		var vbox = new Gtk.VBox(false, 0);
@@ -102,6 +101,20 @@ public class Ease.EditorWindow : Gtk.Window
 		
 		// toolbar
 		
+		// create new slides
+		main_toolbar.new_slide.clicked.connect(() => {
+			var master = document.theme.slide_by_title(slide.title);
+			
+			var slide = new Slide.from_master(master, document,
+			                                  document.width,
+			                                  document.height);
+			
+			var index = document.index_of(slide) + 1;
+			
+			document.add_slide(index, slide);
+			slide_button_panel.add_slide(index, slide);
+		});
+		
 		// show and hide inspector
 		main_toolbar.inspector.clicked.connect(() => {
 			InspectorWindow.toggle();
@@ -128,6 +141,29 @@ public class Ease.EditorWindow : Gtk.Window
 		
 		// save file
 		main_toolbar.save.clicked.connect(() => {
+			if (document.path == null)
+			{
+				var dialog =
+					new Gtk.FileChooserDialog(_("Save Document"),
+		        	                          null,
+		        	                          Gtk.FileChooserAction.SELECT_FOLDER,
+		        	                          "gtk-cancel",
+		        	                          Gtk.ResponseType.CANCEL,
+		        	                          "gtk-open",
+		        	                          Gtk.ResponseType.ACCEPT, null);
+
+				if (dialog.run() == Gtk.ResponseType.ACCEPT)
+				{
+					document.path = dialog.get_filename();
+				}
+				else
+				{
+					dialog.destroy();
+					return;
+				}
+				dialog.destroy();
+			}
+		
 			try { JSONParser.document_write(document); }
 			catch (GLib.Error e)
 			{
diff --git a/src/Element.vala b/src/Element.vala
index fed40a1..0aa86b3 100644
--- a/src/Element.vala
+++ b/src/Element.vala
@@ -41,12 +41,29 @@ public class Ease.Element : GLib.Object
 	 * directly though get() and set(), or though the typed convenience
 	 * properties that Element provides.
 	 */
-	public ElementMap data = new ElementMap();
+	public ElementMap data;
 	
 	/**
 	 * Create a new element.
 	 */
-	public Element() {}
+	public Element()
+	{
+		data = new ElementMap();
+	}
+	
+	private Element.empty() { }
+	
+	/**
+	 * Return a copy of this Element
+	 */
+	public Element copy()
+	{
+		var element = new Element.empty();
+		element.parent = parent;
+		element.data = data.copy();
+		
+		return element;
+	}
 	
 	/**
 	 * Create a new element.
@@ -59,6 +76,30 @@ public class Ease.Element : GLib.Object
 	}
 	
 	/**
+	 * Get a value, given a key.
+	 *
+	 * @param key The key to get a value for.
+	 */
+	public new string get(string key)
+	{
+		return data.get(key);
+	}
+	
+	/**
+	 * Set a value.
+	 * 
+	 * Element uses a key/value system to make exporting JSON and adding
+	 * new types of Elements easy. 
+	 *
+	 * @param key The map key.
+	 * @param val A string to be stored as the key's value.
+	 */
+	public new void set(string key, string val)
+	{
+		data.set(key, val);
+	}
+	
+	/**
 	 * Creates HTML markup for this Element.
 	 * 
 	 * The <div> tag for this Element is appended to the "HTML" parameter.
@@ -236,6 +277,15 @@ public class Ease.Element : GLib.Object
 	}
 	
 	/**
+	 * The Element's identifier on its master { link Slide}
+	 */
+	public string identifier
+	{
+		owned get { return data.get("identifier"); }
+		set	{ data.set("identifier", value); }
+	}
+	
+	/**
 	 * The X position of this Element.
 	 */
 	public float x
diff --git a/src/ElementMap.vala b/src/ElementMap.vala
index 974316f..d8a097c 100644
--- a/src/ElementMap.vala
+++ b/src/ElementMap.vala
@@ -25,7 +25,7 @@
  * The vast majority of XML conversion of { link Element}s is done by
  * ElementMap.
  */
-public class Ease.ElementMap
+public class Ease.ElementMap : Object
 {
 	private Gee.Map<string, ElementMapValue> map;
 	
@@ -43,6 +43,18 @@ public class Ease.ElementMap
 		map = new Gee.HashMap<string, ElementMapValue>();
 	}
 	
+	public ElementMap copy()
+	{
+		var ret = new ElementMap();
+		
+		foreach (var key in map.keys)
+		{
+			ret.set("%s".printf(key), "%s".printf(get(key)));
+		}
+		
+		return ret;
+	}
+	
 	/**
 	 * Output this ElementData as JSON.
 	 * 
@@ -71,7 +83,7 @@ public class Ease.ElementMap
 	 * @param key The map key.
 	 * @param val A string to be stored as the key's value.
 	 */
-	public void set(string key, string val)
+	public new void set(string key, string val)
 	{
 		if (map.has_key(key))
 		{
@@ -90,9 +102,17 @@ public class Ease.ElementMap
 	 *
 	 * @param key The key to get a value for.
 	 */
-	public string get(string key)
+	public new string get(string key)
 	{
 		return map.get(key).str_val;
 	}
+	
+	public void print()
+	{
+		foreach (var key in map.keys)
+		{
+			stdout.printf("%s: %s\n", key, get(key));
+		}
+	}
 }
 
diff --git a/src/JSONParser.vala b/src/JSONParser.vala
index a197da1..14af130 100644
--- a/src/JSONParser.vala
+++ b/src/JSONParser.vala
@@ -21,7 +21,7 @@
 public static class Ease.JSONParser
 {
 	/**
-	 * Parses a JSON file, creating a { link Document}.
+	 * Parses a document JSON file, creating a { link Document}.
 	 *
 	 * @param filename The filename of the { link Document}
 	 */
@@ -33,7 +33,7 @@ public static class Ease.JSONParser
 		var parser = new Json.Parser();
 		
 		// attempt to load the file
-		parser.load_from_file(filename + "/Document.json");
+		parser.load_from_file(Path.build_path("/", filename, "Document.json"));
 		
 		// grab the root object
 		var root = parser.get_root().get_object();
@@ -48,13 +48,45 @@ 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, document_parse_slide(node));
+			document.add_slide(document.length, parse_slide(node));
 		}
 		
 		return document;
 	}
 	
-	private static Slide document_parse_slide(Json.Object obj)
+	/**
+	 * Parses a theme JSON file, creating a { link Theme}.
+	 *
+	 * @param filename The path to the { link Theme}
+	 */
+	public static Theme theme(string filename) throws GLib.Error
+	{
+		var theme = new Theme();
+	
+		var parser = new Json.Parser();
+		
+		// attempt to load the file
+		parser.load_from_file(Path.build_path("/", filename, "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();
 		
@@ -74,6 +106,8 @@ public static class Ease.JSONParser
 		slide.advance_delay =
 			obj.get_string_member("advance_delay").to_double();
 		
+		slide.title = obj.get_string_member("title");
+		
 		// read the slide's background properties
 		if (obj.has_member("background_image"))
 		{
@@ -99,13 +133,13 @@ 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, document_parse_element(node));
+			slide.add_element(slide.count, parse_element(node));
 		}
 		
 		return slide;
 	}
 	
-	private static Element document_parse_element(Json.Object obj)
+	private static Element parse_element(Json.Object obj)
 	{
 		var element = new Element();
 		
@@ -168,6 +202,7 @@ public static class Ease.JSONParser
 		                      slide.automatically_advance.to_string());
 		obj.set_string_member("advance_delay",
 		                      slide.advance_delay.to_string());
+		obj.set_string_member("title", slide.title);
 		
 		// write the slide's background properties
 		if (slide.background_image != null)
diff --git a/src/Main.vala b/src/Main.vala
index 0093c37..f698cf1 100644
--- a/src/Main.vala
+++ b/src/Main.vala
@@ -205,7 +205,7 @@ public static class Ease.Main : GLib.Object
 	 *
 	 * @param win The { link EditorWindow}.
 	 */
-	private static void add_window(EditorWindow win)
+	public static void add_window(EditorWindow win)
 	{
 		windows.add(win);
 	}
diff --git a/src/PDFExporter.vala b/src/PDFExporter.vala
index a3c0b24..a2ca2d7 100644
--- a/src/PDFExporter.vala
+++ b/src/PDFExporter.vala
@@ -73,7 +73,7 @@ public static class Ease.PDFExporter : Object
 		}
 	}
 	
-	private static void write_slide(Slide s, Cairo.Context context) throws Error
+	public static void write_slide(Slide s, Cairo.Context context) throws Error
 	{
 		// write the background color if there is no image
 		if (s.background_image == null)
diff --git a/src/Slide.vala b/src/Slide.vala
index ae72741..0cb76a7 100644
--- a/src/Slide.vala
+++ b/src/Slide.vala
@@ -82,6 +82,11 @@ public class Ease.Slide
 	}
 	
 	/**
+	 * The title of this Slide's master (unless the Slide is a master itself)
+	 */
+	public string title { get; set; }
+	
+	/**
 	 * The { link Document} that this Slide is part of
 	 */
 	public Document parent { get; set; }
@@ -144,6 +149,63 @@ public class Ease.Slide
 		parent = owner;
 	}
 	
+	/**
+	 * Create a Slide from a master Slide.
+	 *
+	 * Used for creating new Slides in a { link Document} linked to a
+	 * { link Theme}.
+	 *
+	 * @param master The master slide.
+	 * @param document The { link Document} this slide is being inserted into.
+	 * @param width The width, in pixels, of the Slide.
+	 * @param height The height, in pixels, of the Slide.
+	 */
+	public Slide.from_master(Slide master, Document? document,
+	                         int width, int height)
+	{
+		// set basic properties
+		transition = master.transition;
+		transition_time = master.transition_time;
+		variant = master.variant;
+		automatically_advance = master.automatically_advance;
+		advance_delay = master.advance_delay;
+		parent = document;
+		
+		// set the background
+		if (master.background_image != null)
+		{
+			background_image = master.background_image.dup();
+		}
+		else
+		{
+			background_color = master.background_color;
+		}
+		
+		if (master.title != null)
+		{
+			title = master.title.dup();
+		}
+		
+		// add all of the master Slide's elements
+		foreach (var e in master.elements)
+		{
+			// copy the Element
+			var element = e.copy();
+			element.parent = this;
+			
+			// resize the Element to fit the Document
+			element.x = (int)(element.data.get("x").to_double() * width);
+			element.y = (int)(element.data.get("y").to_double() * height);
+			element.width = (int)(element.data.get("width").to_double() *
+			                      width);
+			element.height = (int)(element.data.get("height").to_double() *
+			                       height);
+			
+			// add the Element to the new Slide
+			elements.add(element);
+		}
+	}
+	
 	public void add_element(int index, Element e)
 	{
 		e.parent = this;
diff --git a/src/SlideActor.vala b/src/SlideActor.vala
index aaf1e2c..d85e2a8 100644
--- a/src/SlideActor.vala
+++ b/src/SlideActor.vala
@@ -38,6 +38,10 @@ public class Ease.SlideActor : Clutter.Group
 
 	// the context of the actor (presentation, etc.)
 	public ActorContext context;
+	
+	// dimensions
+	private float width_px;
+	private float height_px;
 
 	// timelines
 	public Clutter.Timeline animation_time { get; set; }
@@ -65,13 +69,21 @@ public class Ease.SlideActor : Clutter.Group
 	public SlideActor.from_slide(Document document, Slide s, bool clip,
 	                             ActorContext ctx)
 	{
+		with_dimensions(document.width, document.height, s, clip, ctx);
+	}
+	
+	public SlideActor.with_dimensions(float w, float h, Slide s, bool clip,
+	                                  ActorContext ctx)
+	{
 		slide = s;
 		context = ctx;
+		width_px = w;
+		height_px = h;
 
 		// clip the actor's bounds
 		if (clip)
 		{
-			set_clip(0, 0, document.width, document.height);
+			set_clip(0, 0, w, h);
 		}
 
 		// set the background
@@ -117,8 +129,8 @@ public class Ease.SlideActor : Clutter.Group
 		contents = new Clutter.Group();
 		
 		// set the background size
-		background.width = document.width;
-		background.height = document.height;
+		background.width = width_px;
+		background.height = height_px;
 	}
 	
 	/**
@@ -187,8 +199,8 @@ public class Ease.SlideActor : Clutter.Group
 			background = new Clutter.Rectangle();
 			((Clutter.Rectangle)background).color = slide.background_color;
 		}
-		background.width = slide.parent.width;
-		background.height = slide.parent.height;
+		background.width = width_px;
+		background.height = height_px;
 
 		add_actor(background);
 		lower_child(background, null);
diff --git a/src/SlideButton.vala b/src/SlideButton.vala
index 6caf48a..fb77819 100644
--- a/src/SlideButton.vala
+++ b/src/SlideButton.vala
@@ -26,16 +26,11 @@ public class Ease.SlideButton : Gtk.Button
 	public int slide_id { get; set; }
 	public Slide slide { get; set; }
 
+	private Gtk.AspectFrame aspect;
 	private Gtk.Alignment align;
 
-	// the clutter view
-	private GtkClutter.Embed slide_image;
-
-	// the clutter actor
-	private SlideActor actor;
-
-	// the frame to maintain the aspect ratio
-	private Gtk.AspectFrame aspect;
+	// the Slide preview
+	private Gtk.DrawingArea drawing;
 
 	// the editor window this button is in
 	private EditorWindow owner;
@@ -43,7 +38,7 @@ public class Ease.SlideButton : Gtk.Button
 	// the panel the button is in
 	private SlideButtonPanel panel;
 
-	bool dont_loop = false;
+	bool dont_loop;
 	
 	/**
 	 * Creates a new SlideButton.
@@ -65,55 +60,27 @@ public class Ease.SlideButton : Gtk.Button
 		owner = win;
 		panel = pan;
 
-		// make the embed
-		slide_image = new GtkClutter.Embed();
-		((Clutter.Stage)(slide_image.get_stage())).color = {0, 0, 0, 255};
-		((Clutter.Stage)(slide_image.get_stage())).use_fog = false;
-
-		// make the slide actor
-		actor = new SlideActor.from_slide(s.parent, s, true, ActorContext.SIDEBAR);
-		actor.width = s.parent.width;
-		actor.height = s.parent.height;
-		((Clutter.Stage)(slide_image.get_stage())).add_actor(actor);
-
+		// make the slide thumbnail
+		drawing = new Gtk.DrawingArea();
+		
 		// make the aspect frame
-		aspect = new Gtk.AspectFrame("Slide", 0, 0,
-		                             (float)slide.parent.width /
-		                                    slide.parent.height,
-		                             false);
-		aspect.set_size_request(75, 50);
+		aspect = new Gtk.AspectFrame("", 0.5f, 0.5f, s.parent.aspect, false);
 		aspect.label = null;
-		aspect.add(slide_image);
+		aspect.add(drawing);
 
 		// place things together
-		align = new Gtk.Alignment(0.5f, 0.5f, 0, 0);
+		align = new Gtk.Alignment(0.5f, 0.5f, 1, 1);
 		align.set_padding(0, 0, 0, 0);
-		align.add(aspect);
 
 		// set the style of the button
-		aspect.shadow_type = Gtk.ShadowType.IN;
 		relief = Gtk.ReliefStyle.NONE;
 		focus_on_click = false;
 		show_all();
-		add(align);
-
-		// resize the slide actor appropriately
-		slide_image.size_allocate.connect((rect) => {
-			actor.set_scale_full(rect.width / actor.width, rect.height / actor.height, 0, 0);
-		});
-
-		align.size_allocate.connect((rect) => {
-			if (dont_loop)
-			{
-				dont_loop = false;
-				return;
-			}
-			aspect.set_size_request(rect.width, (int)(rect.width * (float)slide.parent.height / slide.parent.width));
-			dont_loop = true;
-		});
+		add(aspect);
 
 		clicked.connect(() => {
-			for (unowned GLib.List<Gtk.Widget>* itr = panel.slides_box.get_children();
+			for (unowned GLib.List<Gtk.Widget>* itr =
+			     panel.slides_box.get_children();
 			     itr != null; itr = itr->next)
 			{
 				((SlideButton*)(itr->data))->set_relief(Gtk.ReliefStyle.NONE);
@@ -122,6 +89,42 @@ public class Ease.SlideButton : Gtk.Button
 			relief = Gtk.ReliefStyle.NORMAL;
 			owner.load_slide(slide_id);
 		});
+		
+		aspect.size_allocate.connect((rect) => {
+			if (dont_loop)
+			{
+				dont_loop = false;
+				return;
+			}
+			aspect.set_size_request(0, (int)(rect.width / slide.parent.aspect));
+			dont_loop = true;
+		});
+		
+		drawing.expose_event.connect((area, event) => {
+			draw();
+			return false;
+		});
+	}
+	
+	/**
+	 * Draws the SlideButton's preview.
+	 */
+	public void draw()
+	{
+		// get a context for the drawing area
+		var context = Gdk.cairo_create(drawing.get_window());
+		
+		// get the size of the drawing area
+		var allocation = Gtk.Allocation();
+		drawing.get_allocation(allocation);
+		
+		context.save();
+		context.scale(((float)allocation.width) / slide.parent.width,
+		              ((float)allocation.height) / slide.parent.height);
+		
+		// write the slide
+		PDFExporter.write_slide(slide, context);
+		context.restore();
 	}
 }
 
diff --git a/src/SlideButtonPanel.vala b/src/SlideButtonPanel.vala
index d81b06a..396b943 100644
--- a/src/SlideButtonPanel.vala
+++ b/src/SlideButtonPanel.vala
@@ -25,7 +25,8 @@ public class Ease.SlideButtonPanel : Gtk.ScrolledWindow
 {
 	private Document document;
 	private EditorWindow owner;
-	public Gtk.VBox slides_box;
+	public Gtk.VButtonBox slides_box;
+	private Gtk.Alignment align;
 	
 	/**
 	 * Creates a SlideButtonPanel
@@ -42,21 +43,67 @@ public class Ease.SlideButtonPanel : Gtk.ScrolledWindow
 		owner = win;
 
 		// set the scrollbar policy
-		vscrollbar_policy = Gtk.PolicyType.ALWAYS;
+		vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
 		hscrollbar_policy = Gtk.PolicyType.NEVER;
 		
-		slides_box = new Gtk.VBox(true, 1);
+		slides_box = new Gtk.VButtonBox();
 		for (int i = 0; i < document.slides.size; i++)
 		{
 			var button = new SlideButton(i, document.slides.get(i), owner, this);
 			slides_box.pack_start(button, false, false, 0);
 		}
-		var align = new Gtk.Alignment(0, 0, 1, 0);
+		align = new Gtk.Alignment(0, 0, 1, 0);
 		align.add(slides_box);
 		var viewport = new Gtk.Viewport(null, null);
 		viewport.set_shadow_type(Gtk.ShadowType.NONE);
 		viewport.add(align);
 		add(viewport);
+		
+//		slides_box.size_allocate.connect((box, rect) => {
+//			slides_box.child_min_height = (int)(rect.width / document.aspect);
+//			slides_box.child_min_width = rect.width;
+//		});
+//		
+//		size_allocate.connect((self, rect) => {
+//			var allocation = Gtk.Allocation();
+//			slides_box.get_allocation(allocation);
+//			
+//			stdout.printf("\nasdf %i %i\n", rect.width, allocation.width);
+//			
+//			if (allocation.width > rect.width)
+//			{
+//				stdout.printf("\nsmaller");
+//				slides_box.child_min_width = 0;
+//			}
+//		});
+	}
+	
+	/**
+	 * Adds a new { link Slide} to the SlideButtonPanel.
+	 *
+	 * @param index The index of the new { link Slide} in the { link Document}.
+	 * @param slide The { link Slide} to add.
+	 */
+	public void add_slide(int index, Slide slide)
+	{
+		// create a new button
+		var button = new SlideButton(index, slide, owner, this);
+		
+		// add the new button to the box
+		slides_box.pack_start(button, false, false, 0);
+		
+		// put the button in the proper position
+		slides_box.reorder_child(button, index);
+		
+		int i = 0;
+		for (unowned GLib.List<Gtk.Widget>* itr = slides_box.get_children();
+		     itr != null; itr = itr->next)
+		{
+			((SlideButton*)(itr->data))->slide_id = i;
+			i++;
+		}
+		
+		button.show_all();
 	}
 }
 
diff --git a/src/SlideSet.vala b/src/SlideSet.vala
index 0fceeaf..7840979 100644
--- a/src/SlideSet.vala
+++ b/src/SlideSet.vala
@@ -31,7 +31,7 @@ public abstract class Ease.SlideSet : Object
 	public int length { get { return slides.size; } }
 	
 	/**
-	 * Inserts a new { link Slide} into the SlideSet
+	 * Inserts a new { link Slide} into the SlideSet.
 	 *
 	 * @param s The { link Slide} to insert.
 	 * @param index The position of the new { link Slide} in the SlideSet.
@@ -40,4 +40,49 @@ public abstract class Ease.SlideSet : Object
 	{
 		slides.insert(index, s);
 	}
+	
+	/**
+	 * Adds a new { link Slide to the end of the SlideSet.
+	 *
+	 * @param s The { link Slide} to append.
+	 */
+	public void append_slide(Slide s)
+	{
+		slides.insert(length, s);
+	}
+	
+	/**
+	 * Finds the index of the given slide, or returns -1 if it is not found.
+	 *
+	 * @param s The { link Slide} to find the index of.
+	 */
+	public int index_of(Slide s)
+	{
+		for (int i = 0; i < slides.size; i++)
+		{
+			if (slides.get(i) == s)
+			{
+				return i;
+			}
+		}
+		return -1;
+	}
+	
+	/**
+	 * Finds a { link Slide} by its "title" property.
+	 *
+	 * @param id The title to search for.
+	 */
+	public Slide? slide_by_title(string title)
+	{
+		foreach (Slide s in slides)
+		{
+			if (s.title == title)
+			{
+				return s;
+			}
+		}
+		return null;
+	}
 }
+
diff --git a/src/WelcomeActor.vala b/src/WelcomeActor.vala
index df27dad..227dbe9 100644
--- a/src/WelcomeActor.vala
+++ b/src/WelcomeActor.vala
@@ -21,36 +21,94 @@
  * Each WelcomeActor is a preview of a { link Theme}. The user can
  * click on these to create a new { link Document} with that { link Theme}.
  */
-public class Ease.WelcomeActor : Clutter.Rectangle
+public class Ease.WelcomeActor : Clutter.Group
 {
 	private Gee.ArrayList<WelcomeActor> others;
 	private bool is_selected = false;
+	private Slide master;
+	private Slide slide;
+	private SlideActor slide_actor;
+	private float slide_height;
+	private Clutter.Rectangle rect;
+	public Theme theme;
+	
+	// display the name of the theme
+	private const string FONT_NAME = "Sans 8";
+	private const float TEXT_OFFSET = 5;
+	private const float TEXT_HEIGHT = 12;
+	private Clutter.Text text;
 	
 	// constants
 	private const int FADE_TIME = 200;
 	private const int FADE_INIT_TIME = 1000;
 	private const int FADE_EASE = Clutter.AnimationMode.EASE_IN_OUT_SINE;
-	private const int FADE_VALUE = 100;
+	private const int FADE_VALUE = 150;
+	private const float WIDTH = 1024;
 	
 	public signal void selected();
 	
-	public WelcomeActor(int w, Gee.ArrayList<WelcomeActor> o)
+	public WelcomeActor(Theme t, Gee.ArrayList<WelcomeActor> o, Slide m)
 	{
-		width = w;
 		others = o;
-		height = w * 3 / 4; // 4:3
-	
-		// TODO: make this an actual preview
-		color = {200, 200, 200, 255};
+		slide_height = WIDTH * 3 / 4; // 4:3
+		master = m;
+		theme = t;
+		
+		rect = new Clutter.Rectangle();
+		rect.color = {0, 0, 0, 255};
+		add_actor(rect);
+		
+		text = new Clutter.Text();
+		text.color = {255, 255, 255, 255};
+		text.height = TEXT_HEIGHT;
+		text.text = theme.title;
+		text.font_name = FONT_NAME;
+		text.line_alignment = Pango.Alignment.RIGHT;
+		add_actor(text);
 		
-		border_color = {255, 255, 255, 255};
-		border_width = 2;
 		reactive = true;
 		
 		opacity = 0;
 		animate(FADE_EASE, FADE_INIT_TIME, "opacity", 255);
 	}
 	
+	public void set_slide_size(int w, int h)
+	{
+		if (slide_actor != null)
+		{
+			remove_actor(slide_actor);
+		}
+		
+		slide = new Slide.from_master(master, null,
+		                              (int)WIDTH, (int)slide_height);
+		slide_actor = new SlideActor.with_dimensions(w, h, slide, true,
+		                                             ActorContext.PRESENTATION);
+		
+		slide_actor.scale_x = w / WIDTH;
+		slide_actor.scale_y = h / slide_height;
+		slide_actor.height = rect.height;
+		slide_actor.width = rect.width;
+		
+		add_actor(slide_actor);
+	}
+	
+	public void set_dims(float w, float h)
+	{
+		rect.width = w;
+		rect.height = h;
+		
+		text.x = rect.width / 2 - text.width / 2;
+		text.y = h + TEXT_OFFSET;
+		
+		if (slide_actor != null)
+		{
+			slide_actor.scale_x = w / WIDTH;
+			slide_actor.scale_y = h / slide_height;
+			slide_actor.height = rect.height;
+			slide_actor.width = rect.width;
+		}
+	}
+	
 	public void clicked()
 	{
 		if (!is_selected)
@@ -71,13 +129,15 @@ public class Ease.WelcomeActor : Clutter.Rectangle
 	private void fade()
 	{
 		is_selected = false;
-		animate(FADE_EASE, FADE_TIME, "opacity", FADE_VALUE);
+		slide_actor.animate(FADE_EASE, FADE_TIME, "opacity", FADE_VALUE);
+		text.animate(FADE_EASE, FADE_TIME, "opacity", FADE_VALUE);
 	}
 	
 	private void unfade()
 	{
 		is_selected = true;
-		animate(FADE_EASE, FADE_TIME, "opacity", 255);
+		slide_actor.animate(FADE_EASE, FADE_TIME, "opacity", 255);
+		text.animate(FADE_EASE, FADE_TIME, "opacity", 255);
 	}
 }
 
diff --git a/src/WelcomeWindow.vala b/src/WelcomeWindow.vala
index bed54a8..27c589d 100644
--- a/src/WelcomeWindow.vala
+++ b/src/WelcomeWindow.vala
@@ -31,6 +31,10 @@ public class Ease.WelcomeWindow : Gtk.Window
 	private Gtk.ComboBox resolution;
 	private Gtk.SpinButton x_res;
 	private Gtk.SpinButton y_res;
+	
+	// themes
+	private Gee.ArrayList<Theme> themes = new Gee.ArrayList<Theme>();
+	private Theme selected_theme;
 
 	// clutter view
 	private ScrollableEmbed embed;
@@ -39,7 +43,7 @@ public class Ease.WelcomeWindow : Gtk.Window
 	private Clutter.Group preview_container;
 	private Clutter.Rectangle preview_background;
 	private Gee.ArrayList<WelcomeActor> previews = new Gee.ArrayList<WelcomeActor>();
-	private int preview_width = 100;
+	private int preview_width;
 	private float preview_aspect;
 	
 	// preview reshuffle animation
@@ -53,26 +57,35 @@ public class Ease.WelcomeWindow : Gtk.Window
 	private ZoomSlider zoom_slider;
 	
 	// constants
-	private const int[] RESOLUTIONS_X = {800,
+	private const int[] RESOLUTIONS_X = {640,
+	                                     800,
 	                                     1024,
 	                                     1280,
 	                                     1280,
 	                                     1920};
-	private const int[] RESOLUTIONS_Y = {600,
+	private const int[] RESOLUTIONS_Y = {480,
+	                                     600,
 	                                     768,
 	                                     1024,
 	                                     720,
 	                                     1080};
-	private const int RESOLUTION_COUNT = 5;
+	
 	private const int PREVIEW_PADDING = 20;
+	private const int PREVIEW_VERT_PADDING = 35;
+	
+	private const int DEFAULT_ACTIVE = 2;
 	
 	private const int ANIM_TIME = 300;
 	private const int ANIM_EASE = Clutter.AnimationMode.EASE_IN_OUT_SINE;
 	
 	private int[] ZOOM_VALUES = {100, 150, 200, 250, 400};
 	
+	private const string PREVIEW_ID = "Standard";
+	
 	public WelcomeWindow()
 	{
+		assert(RESOLUTIONS_X.length == RESOLUTIONS_Y.length);
+	
 		title = _("New Presentation");
 		set_default_size(640, 480);
 		
@@ -80,35 +93,37 @@ public class Ease.WelcomeWindow : Gtk.Window
 		var hbox = new Gtk.HBox(false, 5);
 		resolution = new Gtk.ComboBox.text();
 		resolution.append_text(_("Custom"));
-		for (var i = 0; i < RESOLUTION_COUNT; i++)
+		for (var i = 0; i < RESOLUTIONS_X.length; i++)
 		{
-			resolution.append_text(_("%i by %i").printf(RESOLUTIONS_X[i], RESOLUTIONS_Y[i]));
+			resolution.append_text(_("%i by %i").printf(RESOLUTIONS_X[i],
+			                                            RESOLUTIONS_Y[i]));
 		}
-		resolution.set_active(2);
 		
 		var align = new Gtk.Alignment(0, 0.5f, 0, 0);
 		align.add(resolution);
 		hbox.pack_start(align, false, false, 0);
 		
+		var resolution_count = RESOLUTIONS_X.length;
 		x_res = new Gtk.SpinButton.with_range(RESOLUTIONS_X[0],
-											  RESOLUTIONS_Y[RESOLUTION_COUNT-1],
+											  RESOLUTIONS_X[resolution_count-1],
 											  1);
-		x_res.set_value(1024);
+											  
 		align = new Gtk.Alignment(0, 0.5f, 0, 0);
 		align.add(x_res);
 		hbox.pack_start(align, false, false, 0);
 		
 		y_res = new Gtk.SpinButton.with_range(RESOLUTIONS_Y[0],
-											  RESOLUTIONS_Y[RESOLUTION_COUNT-1],
+											  RESOLUTIONS_Y[resolution_count-1],
 											  1);
-		y_res.set_value(768);
+		
 		align = new Gtk.Alignment(0, 0.5f, 0, 0);
 		align.add(y_res);
 		hbox.pack_start(align, false, false, 0);
 		
 		new_button = new Gtk.Button.with_label(_("New Presentation"));
 		new_button.sensitive = false;
-		new_button.image = new Gtk.Image.from_stock("gtk-new", Gtk.IconSize.BUTTON);
+		new_button.image = new Gtk.Image.from_stock("gtk-new",
+		                                            Gtk.IconSize.BUTTON);
 		align = new Gtk.Alignment(0, 0.5f, 0, 0);
 		align.add(new_button);
 		hbox.pack_start(align, false, false, 0);
@@ -124,6 +139,7 @@ public class Ease.WelcomeWindow : Gtk.Window
 		
 		// create the upper UI - the embed
 		embed = new ScrollableEmbed(false);
+		embed.get_stage().use_fog = false;
 
 		// create the preview container
 		preview_container = new Clutter.Group();
@@ -133,10 +149,17 @@ public class Ease.WelcomeWindow : Gtk.Window
 		preview_background.color = {0, 0, 0, 255};
 		preview_container.add_actor(preview_background);
 		
+		// load themes
+		for (int i = 0; i < 20; i++)
+			themes.add(JSONParser.theme("themes/White.easetheme"));
+		
 		// create the previews
-		for (var i = 0; i < 10; i++)
+		foreach (var theme in themes)
 		{
-			var act = new WelcomeActor(preview_width, previews);
+			var master = theme.slide_by_title(PREVIEW_ID);
+			if (master == null) continue;
+			
+			var act = new WelcomeActor(theme, previews, master);
 			previews.add(act);
 			preview_container.add_actor(act);
 			
@@ -158,18 +181,49 @@ public class Ease.WelcomeWindow : Gtk.Window
 		
 		add(vbox);
 		show_all();
-		reflow_previews();
 		
 		// ui signals
+		new_button.clicked.connect(() => {
+			// create a new Document
+			var document = new Document();
+			document.width = (int)x_res.get_value();
+			document.height = (int)y_res.get_value();
+			document.theme = selected_theme;
+			
+			// get the master
+			var master = selected_theme.slide_by_title(PREVIEW_ID);
+			
+			// add the first slide
+			document.append_slide(new Slide.from_master(master, document,
+			                                            document.width,
+			                                            document.height));
+			
+			// create an EditorWindow for the new Document
+			var editor = new EditorWindow(document);
+			
+			Main.add_window(editor);
+			Main.remove_welcome();
+		});
+		
 		// changing resolution values
 		x_res.value_changed.connect(() => {
 			set_resolution_box((int)(x_res.get_value()),
 			                   (int)(y_res.get_value()));
+			foreach (var p in previews)
+			{
+				p.set_slide_size((int)x_res.get_value(),
+				                 (int)y_res.get_value());
+			}
 		});
 		
 		y_res.value_changed.connect(() => {
 			set_resolution_box((int)(x_res.get_value()),
 			                   (int)(y_res.get_value()));
+			foreach (var p in previews)
+			{
+				p.set_slide_size((int)x_res.get_value(),
+				                 (int)y_res.get_value());
+			}
 		});
 		
 		resolution.changed.connect(() => {
@@ -192,6 +246,7 @@ public class Ease.WelcomeWindow : Gtk.Window
 		{
 			a.button_press_event.connect((act, event) => {
 				((WelcomeActor)(act)).clicked();
+				selected_theme = ((WelcomeActor)(act)).theme;
 				return false;
 			});
 		}
@@ -203,11 +258,19 @@ public class Ease.WelcomeWindow : Gtk.Window
 		});
 
 		open_button.clicked.connect((sender) => OpenDialog.run());
+		
+		resolution.set_active(DEFAULT_ACTIVE + 1);
+		
+		preview_width = (int)zoom_slider.get_value();
+		
+		// reflow previews without animation
+		preview_row_count = -1;
+		reflow_previews();
 	}
 	
 	private void set_resolution_box(int width, int height)
 	{
-		for (var i = 0; i < RESOLUTION_COUNT; i++)
+		for (var i = 0; i < RESOLUTIONS_X.length; i++)
 		{
 			if (width == RESOLUTIONS_X[i] && height == RESOLUTIONS_Y[i])
 			{
@@ -229,7 +292,7 @@ public class Ease.WelcomeWindow : Gtk.Window
 		for (; per_line * (preview_width + PREVIEW_PADDING) +
 		       PREVIEW_PADDING < embed.width;
 		     per_line++);
-		per_line--; // FIXME: the math is not strong in me at 2 AM
+		per_line--;
 		
 		// don't animate when the window first opens
 		if (preview_row_count == -1)
@@ -311,14 +374,13 @@ public class Ease.WelcomeWindow : Gtk.Window
 			}
 
 			// set the size of the preview
-			a.width = preview_width;
-			a.height = preview_width * preview_aspect;
+			a.set_dims(preview_width, preview_width * preview_aspect);
 
 			// go to the next line
 			if (++x_position >= per_line)
 			{
 				x_position = 0;
-				y_pixels += PREVIEW_PADDING + preview_width * preview_aspect;
+				y_pixels += PREVIEW_VERT_PADDING + preview_width * preview_aspect;
 			}
 		}
 
@@ -326,8 +388,8 @@ public class Ease.WelcomeWindow : Gtk.Window
 		preview_background.width = embed.width;
 		preview_background.height = x_position != 0
 		                          ? y_pixels + preview_width * preview_aspect +
-		                            PREVIEW_PADDING
-		                          : y_pixels + PREVIEW_PADDING;
+		                            PREVIEW_VERT_PADDING
+		                          : y_pixels + PREVIEW_VERT_PADDING;
 
 		// always fill the background
 		if (preview_background.height < embed.height)
diff --git a/themes/White.easetheme/Theme.json b/themes/White.easetheme/Theme.json
new file mode 100644
index 0000000..ae7fbef
--- /dev/null
+++ b/themes/White.easetheme/Theme.json
@@ -0,0 +1,56 @@
+{
+  "title" : "White",
+  "slides" : [
+    {
+      "transition" : "1",
+      "green" : "255",
+      "variant" : "0",
+      "automatically_advance" : "false",
+      "advance_delay" : "0",
+      "elements" : [
+        {
+          "font_variant" : "Normal",
+          "y" : "0.05",
+          "x" : "0.05",
+          "height" : "0.1",
+          "font_style" : "Normal",
+          "element_type" : "text",
+          "identifier" : "header",
+          "green" : "0",
+          "align" : "left",
+          "text" : "Lorem Ipsum Dolor",
+          "width" : "0.9",
+          "blue" : "0",
+          "font_weight" : "900",
+          "red" : "0",
+          "ease_name" : "header",
+          "font_name" : "Sans",
+          "font_size" : "36"
+        },
+        {
+          "font_variant" : "Normal",
+          "y" : "0.2",
+          "x" : "0.05",
+          "height" : "0.75",
+          "font_style" : "Normal",
+          "element_type" : "text",
+          "identifier" : "main-text",
+          "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.",
+          "width" : "0.9",
+          "blue" : "0",
+          "font_weight" : "500",
+          "red" : "0",
+          "ease_name" : "text",
+          "font_name" : "Sans",
+          "font_size" : "16"
+        }
+      ],
+      "blue" : "255",
+      "title" : "Standard",
+      "red" : "255",
+      "transition_time" : "0.5"
+    }
+  ]
+}



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