[mistelix] Implements WelcomeView, recent used projects



commit 7e77b8cd868516a231198792f0937511461d35a5
Author: Jordi Mas <jmas softcatala org>
Date:   Thu Jul 23 15:34:08 2009 +0200

    Implements WelcomeView, recent used projects

 data/Makefile.am                 |    4 +-
 data/button-dvd.svg              |  300 ++++++++++++++++++++++++
 data/button-slideshow.svg        |  473 ++++++++++++++++++++++++++++++++++++++
 mistelix.csproj                  |    4 +
 po/POTFILES.in                   |    2 +
 src/Defines.cs.in                |    1 +
 src/Makefile.am                  |    7 +-
 src/core/Button.cs               |   11 +-
 src/core/DvdMenu.cs              |   67 +++++-
 src/core/RecentFilesStorage.cs   |  158 +++++++++++++
 src/core/XmlStorage.cs           |    4 +-
 src/datamodel/ObservableList.cs  |   49 ++++-
 src/datamodel/Project.cs         |   18 ++-
 src/datamodel/RecentFile.cs      |  123 ++++++++++
 src/dialogs/AboutDialog.cs       |    2 +-
 src/dialogs/NewProjectDialog.cs  |    9 +
 src/mistelix.cs                  |  103 +++++++--
 src/mistelix.glade               |   19 ++-
 src/widgets/AuthoringPaneView.cs |    8 +-
 src/widgets/SlideShowView.cs     |    3 -
 src/widgets/WelcomeView.cs       |  425 ++++++++++++++++++++++++++++++++++
 21 files changed, 1742 insertions(+), 48 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 13eb8e8..be7c953 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -12,7 +12,9 @@ man_MANS = mistelix.1
 
 tango_icons = 				\
 	mistelix.png	\
-	mistelix.svg
+	mistelix.svg \
+	button-slideshow.svg \
+	button-dvd.svg 
 
 install-data-local:
 	@-$(mkinstalldirs) $(DESTDIR)$(hicolordir)/scalable/apps
diff --git a/data/button-dvd.svg b/data/button-dvd.svg
new file mode 100644
index 0000000..f62889f
--- /dev/null
+++ b/data/button-dvd.svg
@@ -0,0 +1,300 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="128"
+   height="128"
+   id="svg1306"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docbase="/home/dobey/Projects/gnome-icon-theme/scalable/devices"
+   sodipodi:docname="button-dvd.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs1308">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 24 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="48 : 24 : 1"
+       inkscape:persp3d-origin="24 : 16 : 1"
+       id="perspective40" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4409">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4411" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop4413" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6028">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop6030" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop6032" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6036">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop6038" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop6040" />
+    </linearGradient>
+    <linearGradient
+       id="aigrd2"
+       gradientUnits="userSpaceOnUse"
+       x1="12.2744"
+       y1="32.4165"
+       x2="35.391201"
+       y2="14.2033">
+      <stop
+         offset="0"
+         style="stop-color:#FBFBFB"
+         id="stop3043" />
+      <stop
+         offset="0.5"
+         style="stop-color:#B6B6B6"
+         id="stop3045" />
+      <stop
+         offset="1"
+         style="stop-color:#E4E4E4"
+         id="stop3047" />
+    </linearGradient>
+    <linearGradient
+       id="aigrd1"
+       gradientUnits="userSpaceOnUse"
+       x1="14.9966"
+       y1="11.1885"
+       x2="32.511002"
+       y2="34.307499">
+      <stop
+         offset="0"
+         style="stop-color:#EBEBEB"
+         id="stop3034" />
+      <stop
+         offset="0.5"
+         style="stop-color:#FFFFFF"
+         id="stop3036" />
+      <stop
+         offset="1"
+         style="stop-color:#EBEBEB"
+         id="stop3038" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient23419">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop23421" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop23423" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient23419"
+       id="radialGradient3507"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.25,0,31.22703)"
+       cx="23.334524"
+       cy="41.63604"
+       fx="23.334524"
+       fy="41.63604"
+       r="22.627417" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6028"
+       id="linearGradient3515"
+       gradientUnits="userSpaceOnUse"
+       x1="28.702885"
+       y1="31.494707"
+       x2="17.742729"
+       y2="18.366575" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6036"
+       id="linearGradient3519"
+       gradientUnits="userSpaceOnUse"
+       x1="10.50172"
+       y1="3.6100161"
+       x2="48.798885"
+       y2="54.698483"
+       gradientTransform="matrix(2.9371855,0,0,2.9014113,-6.9199222,-8.296319)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#aigrd2"
+       id="linearGradient3523"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(3.4966489,0,0,3.4540606,-19.327839,-15.54985)"
+       x1="12.2744"
+       y1="32.4165"
+       x2="35.391201"
+       y2="14.2033" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#aigrd1"
+       id="linearGradient3526"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(3.4966489,0,0,3.4540604,-19.327839,-15.549847)"
+       x1="14.9966"
+       y1="11.1885"
+       x2="32.511002"
+       y2="34.307499" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4409"
+       id="linearGradient4415"
+       x1="25.985928"
+       y1="24.919628"
+       x2="15.889072"
+       y2="11.669628"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.9371856,0,0,2.9014112,-5.2030784,-5.4536029)" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="0.22745098"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8284271"
+     inkscape:cx="26.448408"
+     inkscape:cy="60.066527"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     stroke="#888a85"
+     fill="#eeeeec"
+     inkscape:window-width="1280"
+     inkscape:window-height="725"
+     inkscape:window-x="0"
+     inkscape:window-y="52"
+     inkscape:showpageshadow="false" />
+  <metadata
+     id="metadata1311">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title>Removable drive</dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Andreas Nilsson</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:description />
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/"; />
+        <dc:source>http://www.gnome.org</dc:source>
+        <dc:contributor>
+          <cc:Agent>
+            <dc:title>Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:contributor>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/";>
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction"; />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution"; />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice"; />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks"; />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike"; />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode"; />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.55;fill:url(#radialGradient3507);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       id="path23417"
+       sodipodi:cx="23.334524"
+       sodipodi:cy="41.63604"
+       sodipodi:rx="22.627417"
+       sodipodi:ry="5.6568542"
+       d="M 45.961941,41.63604 A 22.627417,5.6568542 0 1 1 0.70710754,41.63604 A 22.627417,5.6568542 0 1 1 45.961941,41.63604 z"
+       inkscape:r_cx="true"
+       inkscape:r_cy="true"
+       transform="matrix(2.9371856,0,0,3.0937486,-3.9827367,-16.667197)" />
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       id="path3040"
+       d="M 64.591754,3.7928943 C 30.674249,3.7928943 3.4003841,30.73457 3.4003841,64.238961 C 3.4003841,97.743355 30.674249,124.68503 64.591754,124.68503 C 98.509253,124.68503 125.78312,97.743355 125.78312,64.238961 C 125.78312,30.73457 98.509253,3.7928943 64.591754,3.7928943 L 64.591754,3.7928943 z M 64.591754,78.74602 C 56.549459,78.74602 49.905826,72.183303 49.905826,64.238961 C 49.905826,56.294621 56.549459,49.731906 64.591754,49.731906 C 72.634045,49.731906 79.277681,56.294621 79.277681,64.238961 C 79.277681,72.183303 72.634045,78.74602 64.591754,78.74602 z"
+       style="fill:url(#linearGradient3526);fill-rule:nonzero;stroke:none;stroke-miterlimit:4" />
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       id="path3049"
+       d="M 64.591754,3.7928931 C 30.674249,3.7928931 3.4003844,30.73457 3.4003844,64.238961 C 3.4003844,97.743355 30.674249,124.68503 64.591754,124.68503 C 98.509253,124.68503 125.78312,97.743355 125.78312,64.238961 C 125.78312,30.73457 98.509253,3.7928931 64.591754,3.7928931 L 64.591754,3.7928931 z M 64.591754,78.74602 C 56.549459,78.74602 49.905826,72.183303 49.905826,64.238961 C 49.905826,56.294621 56.549459,49.731906 64.591754,49.731906 C 72.634045,49.731906 79.277681,56.294621 79.277681,64.238961 C 79.277681,72.183303 72.634045,78.74602 64.591754,78.74602 z"
+       style="fill:url(#linearGradient3523);fill-rule:nonzero;stroke:#808080;stroke-width:2.91924357;stroke-miterlimit:4;stroke-opacity:1" />
+    <path
+       style="fill:url(#linearGradient4415);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 62.089423,5.6075499 C 30.480339,6.7852632 5.4028858,32.35088 5.4028858,63.87495 C 5.4028858,78.165866 10.534512,91.15515 19.110239,101.26392 L 51.763969,73.960795 C 49.645255,71.513916 48.68217,68.032724 48.68217,64.600303 C 48.68217,54.763387 55.450295,48.447059 65.034582,48.447059 C 69.393147,48.447059 73.761218,50.400947 76.367615,53.513787 L 109.75564,26.573332 C 98.960983,13.737041 82.603489,5.6075499 64.300286,5.6075499 C 63.534383,5.6075499 62.848044,5.5792847 62.089423,5.6075499 z"
+       id="path3531"
+       sodipodi:nodetypes="csccssccsc" />
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       id="path3051"
+       d="M 64.591754,34.922624 C 48.01843,34.922624 34.913941,48.248234 34.913941,64.238961 C 34.913941,80.610429 48.403861,93.555302 64.591754,93.555302 C 81.165074,93.555302 94.269563,80.229695 94.269563,64.238961 C 94.269563,47.867499 80.779646,34.922624 64.591754,34.922624 L 64.591754,34.922624 z M 64.591754,80.229695 C 55.726954,80.229695 48.403861,72.995795 48.403861,64.238961 C 48.403861,55.482134 55.726954,48.248234 64.591754,48.248234 C 73.456553,48.248234 80.779646,55.482134 80.779646,64.238961 C 80.779646,72.995795 73.456553,80.229695 64.591754,80.229695 z"
+       style="opacity:0.51098902;fill:#eeeeec;fill-rule:nonzero;stroke:none;stroke-miterlimit:4" />
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       style="opacity:0.54644811;fill:none;fill-rule:nonzero;stroke:url(#linearGradient3519);stroke-width:2.91924357;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 64.591742,6.7978817 C 32.360405,6.7978817 6.4424249,32.400192 6.4424249,64.238952 C 6.4424249,96.077716 32.360405,121.68002 64.591742,121.68002 C 96.823075,121.68002 122.74105,96.077716 122.74105,64.238952 C 122.74105,32.400192 96.823075,6.7978817 64.591742,6.7978817 L 64.591742,6.7978817 z"
+       id="path5264"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       inkscape:r_cy="true"
+       inkscape:r_cx="true"
+       sodipodi:type="arc"
+       style="opacity:0.67213111;fill:none;fill-opacity:0.31638417;fill-rule:nonzero;stroke:url(#linearGradient3515);stroke-width:0.93053865;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       id="path6026"
+       sodipodi:cx="24.306795"
+       sodipodi:cy="24.930641"
+       sodipodi:rx="6.0987959"
+       sodipodi:ry="6.0987959"
+       d="M 30.405591,24.930641 A 6.0987959,6.0987959 0 1 1 18.207999,24.930641 A 6.0987959,6.0987959 0 1 1 30.405591,24.930641 z"
+       transform="matrix(2.9371856,0,0,2.9014112,-6.6603102,-8.0398657)" />
+  </g>
+</svg>
diff --git a/data/button-slideshow.svg b/data/button-slideshow.svg
new file mode 100644
index 0000000..56883cd
--- /dev/null
+++ b/data/button-slideshow.svg
@@ -0,0 +1,473 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="193.89"
+   height="144.98"
+   viewBox="0 0 999 747"
+   id="svg4798"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="button_slideshow.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <metadata
+     id="metadata4911">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs4909">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 72.490501 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="193.88699 : 72.490501 : 1"
+       inkscape:persp3d-origin="96.943497 : 48.327001 : 1"
+       id="perspective122" />
+    <linearGradient
+       id="linearGradient11802">
+      <stop
+         style="stop-color:#6d5a1a;stop-opacity:1;"
+         offset="0"
+         id="stop11804" />
+      <stop
+         style="stop-color:#50480f;stop-opacity:1;"
+         offset="1"
+         id="stop11806" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient10825">
+      <stop
+         style="stop-color:#a5c4c7;stop-opacity:1;"
+         offset="0"
+         id="stop10827" />
+      <stop
+         style="stop-color:#86a4b7;stop-opacity:0.99607843;"
+         offset="1"
+         id="stop10829" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient10817">
+      <stop
+         style="stop-color:#cac08d;stop-opacity:1;"
+         offset="0"
+         id="stop10819" />
+      <stop
+         style="stop-color:#a9976f;stop-opacity:1;"
+         offset="1"
+         id="stop10821" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient10809">
+      <stop
+         style="stop-color:#fcc719;stop-opacity:1;"
+         offset="0"
+         id="stop10811" />
+      <stop
+         style="stop-color:#fca519;stop-opacity:1;"
+         offset="1"
+         id="stop10813" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5914">
+      <stop
+         style="stop-color:#9b9b9b;stop-opacity:1;"
+         offset="0"
+         id="stop5916" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop5918" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5906">
+      <stop
+         style="stop-color:#9b9b9b;stop-opacity:1;"
+         offset="0"
+         id="stop5908" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop5910" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5898">
+      <stop
+         style="stop-color:#9b9b9b;stop-opacity:1;"
+         offset="0"
+         id="stop5900" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop5902" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5890">
+      <stop
+         style="stop-color:#9b9b9b;stop-opacity:1;"
+         offset="0"
+         id="stop5892" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop5894" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5882">
+      <stop
+         style="stop-color:#9b9b9b;stop-opacity:1;"
+         offset="0"
+         id="stop5884" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop5886" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5882"
+       id="linearGradient5888"
+       x1="110.44397"
+       y1="69"
+       x2="113.85258"
+       y2="246.98146"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5890"
+       id="linearGradient5896"
+       x1="114.42544"
+       y1="260"
+       x2="115.85258"
+       y2="436"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5898"
+       id="linearGradient5904"
+       x1="619.5"
+       y1="47"
+       x2="619.5"
+       y2="171.42628"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9393988,0.3428262,-0.3428262,0.9393988,98.72165,-249.05025)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5906"
+       id="linearGradient5912"
+       x1="740.05475"
+       y1="145"
+       x2="696.8526"
+       y2="328.31424"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9393988,0.3428262,-0.3428262,0.9393988,98.72165,-249.05025)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5914"
+       id="linearGradient5920"
+       x1="796.203"
+       y1="303.68576"
+       x2="802.07501"
+       y2="428.5181"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9393988,0.3428262,-0.3428262,0.9393988,98.72165,-249.05025)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10809"
+       id="linearGradient11861"
+       gradientUnits="userSpaceOnUse"
+       x1="451"
+       y1="575.5"
+       x2="514"
+       y2="575.5"
+       gradientTransform="matrix(0.8426833,-0.5384096,0.5384096,0.8426833,194.10088,483.46287)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10825"
+       id="linearGradient11863"
+       gradientUnits="userSpaceOnUse"
+       x1="451"
+       y1="472.5"
+       x2="514"
+       y2="472.5"
+       gradientTransform="matrix(0.8426833,-0.5384096,0.5384096,0.8426833,194.10088,483.46287)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient11802"
+       id="linearGradient11865"
+       gradientUnits="userSpaceOnUse"
+       x1="450.91949"
+       y1="441.42639"
+       x2="514.04706"
+       y2="441.42639"
+       gradientTransform="matrix(0.8426833,-0.5384096,0.5384096,0.8426833,194.10088,483.46287)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10817"
+       id="linearGradient11867"
+       gradientUnits="userSpaceOnUse"
+       x1="450.96487"
+       y1="702.28851"
+       x2="514.03253"
+       y2="702.28851"
+       gradientTransform="matrix(0.8426833,-0.5384096,0.5384096,0.8426833,194.10088,483.46287)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10809"
+       id="linearGradient11884"
+       gradientUnits="userSpaceOnUse"
+       x1="451"
+       y1="575.5"
+       x2="514"
+       y2="575.5"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-289.04946,715.03125)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10825"
+       id="linearGradient11886"
+       gradientUnits="userSpaceOnUse"
+       x1="451"
+       y1="472.5"
+       x2="514"
+       y2="472.5"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-289.04946,715.03125)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient11802"
+       id="linearGradient11888"
+       gradientUnits="userSpaceOnUse"
+       x1="450.91949"
+       y1="441.42639"
+       x2="514.04706"
+       y2="441.42639"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-289.04946,715.03125)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10817"
+       id="linearGradient11890"
+       gradientUnits="userSpaceOnUse"
+       x1="450.96487"
+       y1="702.28851"
+       x2="514.03253"
+       y2="702.28851"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-289.04946,715.03125)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10817"
+       id="linearGradient11894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.80387,301.44256)"
+       x1="450.96487"
+       y1="702.28851"
+       x2="514.03253"
+       y2="702.28851" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient11802"
+       id="linearGradient11897"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.80387,301.44256)"
+       x1="450.91949"
+       y1="441.42639"
+       x2="514.04706"
+       y2="441.42639" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10825"
+       id="linearGradient11900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.80387,301.44256)"
+       x1="451"
+       y1="472.5"
+       x2="514"
+       y2="472.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10809"
+       id="linearGradient11903"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.80387,301.44256)"
+       x1="451"
+       y1="575.5"
+       x2="514"
+       y2="575.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10809"
+       id="linearGradient12951"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.0438089,306.55385)"
+       x1="451"
+       y1="575.5"
+       x2="514"
+       y2="575.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10825"
+       id="linearGradient12953"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.0438089,306.55385)"
+       x1="451"
+       y1="472.5"
+       x2="514"
+       y2="472.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient11802"
+       id="linearGradient12955"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.0438089,306.55385)"
+       x1="450.91949"
+       y1="441.42639"
+       x2="514.04706"
+       y2="441.42639" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10817"
+       id="linearGradient12957"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9239176,-0.3825916,0.3825916,0.9239176,-5.0438089,306.55385)"
+       x1="450.96487"
+       y1="702.28851"
+       x2="514.03253"
+       y2="702.28851" />
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-height="725"
+     inkscape:window-width="1280"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     guidetolerance="10.0"
+     gridtolerance="10.0"
+     objecttolerance="10.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:zoom="2.0555692"
+     inkscape:cx="126.09362"
+     inkscape:cy="90.189701"
+     inkscape:window-x="0"
+     inkscape:window-y="52"
+     inkscape:current-layer="svg4798"
+     showgrid="false"
+     showborder="true"
+     inkscape:showpageshadow="true"
+     borderlayer="false" />
+  <g
+     id="g12970"
+     transform="matrix(1.6852358,-0.2458681,0.335056,1.2366462,-731.96121,252.64767)"
+     inkscape:export-filename="/home/jordi/dev/mistelix-fresh/data/mistelix48.png"
+     inkscape:export-xdpi="41.238842"
+     inkscape:export-ydpi="41.238842">
+    <path
+       id="path4800"
+       d="M 444.19103,76.089585 L 686.18874,-35.723238 L 692.42171,-32.38406 L 699.9369,-29.641452 L 708.3024,-23.394986 L 719.48611,-16.120053 L 724.43685,-12.184295 L 729.98417,-6.9663147 L 743.30041,3.2158956 L 749.4443,9.7161092 L 755.58819,16.216314 L 761.38925,23.655915 L 767.19031,31.095517 L 772.99137,38.535127 L 778.4496,46.914125 L 783.56501,56.232529 L 787.74102,65.208105 L 792.5136,75.465905 L 796.94335,86.663102 L 799.49431,97.174654 L 801.70243,108.62559 L 803.7459,114.69389 L 804.84997,120.41937 L 805.43304,133.40628 L 806.01613,146.39321 L 806.25638,160.31952 L 804.27501,174.49958 L 803.23303,189.02247 L 802.4583,194.06229 L 801.34074,200.04151 L 800.73068,210.46398 L 801.06001,221.22927 L 802.67157,231.39799 L 804.28312,241.56672 L 807.17691,251.13886 L 811.01009,261.05383 L 813.90387,270.62598 L 815.6936,274.47266 L 819.36211,279.00499 L 823.19529,288.91996 L 828.99634,296.35956 L 834.45458,304.73857 L 840.25564,312.17817 L 846.0567,319.61777 L 858.59822,33
 4.83981 L 871.22881,346.90082 L 877.71553,352.46163 L 883.85941,358.96183 L 896.57909,367.86182 L 907.41997,376.07616 L 916.38205,383.60484 L 923.21159,388.22625 L 929.44456,391.56543 L 687.44684,503.37825 L 680.87104,500.97847 L 672.75928,496.95364 L 662.51498,490.02152 L 652.27067,483.08941 L 638.6116,473.84659 L 624.35594,463.32155 L 611.12877,449.97831 L 598.24443,435.69568 L 592.44337,428.25608 L 586.38857,418.59485 L 580.93034,410.21585 L 575.81493,400.89745 L 573.42864,395.76854 L 571.04234,390.63965 L 565.33036,380.03902 L 561.84001,369.18464 L 558.94622,359.6125 L 556.14153,346.87932 L 555.21562,334.8318 L 554.28972,322.78428 L 554.04946,308.85798 L 554.22762,302.53592 L 555.68801,295.61731 L 557.66938,281.43725 L 558.78694,275.45803 L 558.62227,270.07538 L 559.57516,258.71351 L 560.52806,247.35165 L 559.25933,236.24353 L 557.64776,226.07481 L 555.09681,215.56326 L 553.48525,205.39454 L 549.30924,196.41897 L 545.47606,186.50399 L 542.23945,177.87124 L 536.78122,169.
 49224 L 531.66581,160.17384 L 526.80415,153.07706 L 521.00309,145.63745 L 515.20203,138.19785 L 510.34037,131.10108 L 504.19649,124.60086 L 497.70977,119.04006 L 485.67575,108.26127 L 474.23829,98.764711 L 464.33682,90.893204 L 450.08116,80.36816 L 444.19103,76.089585 z"
+       style="fill:#2b2725" />
+    <path
+       id="path4802"
+       d="M 538.87053,61.674638 L 659.3997,5.5968095 L 665.28983,9.8753979 L 671.17997,14.153981 L 678.60609,20.057614 L 685.68937,26.900649 L 693.71205,34.086498 L 701.39191,42.211763 L 706.85014,50.590759 L 710.51865,55.123088 L 713.84435,60.594818 L 717.17004,66.066539 L 719.55633,71.195445 L 724.32891,81.453245 L 728.16209,91.368221 L 729.5199,99.315314 L 732.50277,105.72644 L 732.66743,111.10909 L 610.51322,168.72288 L 608.46975,162.65458 L 602.59311,146.6713 L 597.82052,136.4135 L 592.10855,125.81288 L 586.99315,116.49448 L 581.87774,107.17608 L 576.07667,99.736469 L 569.33622,91.954037 L 555.51247,77.32858 L 543.47845,66.54979 L 538.87053,61.674638 z"
+       style="fill:url(#linearGradient5904);fill-opacity:1" />
+    <path
+       id="path4804"
+       d="M 633.2748,360.12537 L 753.55021,301.82591 L 762.84163,320.11989 L 767.36047,328.15607 L 771.8793,336.19224 L 777.33753,344.57125 L 781.85637,352.60743 L 789.53623,360.73268 L 797.81265,370.14017 L 807.02848,379.89047 L 815.05116,387.07633 L 830.84279,399.22642 L 836.73292,403.50501 L 710.98578,465.13014 L 704.15624,460.50873 L 698.95176,454.35135 L 690.92907,447.1655 L 673.34774,431.16872 L 665.41414,420.82185 L 658.67368,413.03942 L 654.15485,405.00324 L 648.69661,396.62423 L 640.94117,379.95531 L 633.2748,360.12537 z"
+       style="fill:url(#linearGradient5920);fill-opacity:1" />
+    <path
+       id="path4806"
+       d="M 617.06203,194.53205 L 623.65405,331.06609 L 746.74766,273.79512 L 741.34878,139.82553 L 617.06203,194.53205 z"
+       style="fill:url(#linearGradient5912);fill-opacity:1" />
+    <g
+       id="g12940"
+       transform="matrix(0.9393988,0.3428262,-0.3428262,0.9393988,98.72165,-249.05025)">
+      <path
+         style="fill:#ffffff"
+         d="M 499,186 L 479,179 L 494,161 L 514,169 L 499,186 z"
+         id="path4808" />
+      <path
+         style="fill:#ffffff"
+         d="M 545,210 L 526,199 L 543,184 L 563,195 L 545,210 z"
+         id="path4810" />
+      <path
+         style="fill:#ffffff"
+         d="M 584,244 L 569,229 L 584,211 L 600,227 L 584,244 z"
+         id="path4812" />
+      <path
+         style="fill:#ffffff"
+         d="M 615,294 L 605,272 L 618,253 L 630,275 L 615,294 z"
+         id="path4814" />
+      <path
+         style="fill:#ffffff"
+         d="M 653,400 L 640,377 L 657,361 L 670,385 L 653,400 z"
+         id="path4816" />
+      <path
+         style="fill:#ffffff"
+         d="M 688,437 L 668,419 L 686,405 L 705,422 L 688,437 z"
+         id="path4818" />
+      <path
+         style="fill:#ffffff"
+         d="M 733,464 L 708,451 L 727,438 L 751,451 L 733,464 z"
+         id="path4820" />
+      <path
+         style="fill:#ffffff"
+         d="M 781,483 L 755,474 L 771,458 L 798,466 L 781,483 z"
+         id="path4822" />
+      <path
+         style="fill:#ffffff"
+         d="M 628,345 L 623,321 L 641,301 L 646,325 L 628,345 z"
+         id="path4824" />
+    </g>
+    <g
+       id="g12959"
+       transform="matrix(0.9393988,0.3428262,-0.3428262,0.9393988,98.72165,-249.05025)">
+      <path
+         style="fill:#ffffff"
+         d="M 647,39 L 627,32 L 642,14 L 663,22 L 647,39 z"
+         id="path4826" />
+      <path
+         style="fill:#ffffff"
+         d="M 692,62 L 674,52 L 692,37 L 710,48 L 692,62 z"
+         id="path4828" />
+      <path
+         style="fill:#ffffff"
+         d="M 732,97 L 717,82 L 733,64 L 747,79 L 732,97 z"
+         id="path4830" />
+      <path
+         style="fill:#ffffff"
+         d="M 763,146 L 753,124 L 766,106 L 778,128 L 763,146 z"
+         id="path4832" />
+      <path
+         style="fill:#ffffff"
+         d="M 801,252 L 787,229 L 805,214 L 818,237 L 801,252 z"
+         id="path4834" />
+      <path
+         style="fill:#ffffff"
+         d="M 835,290 L 816,272 L 834,257 L 853,274 L 835,290 z"
+         id="path4836" />
+      <path
+         style="fill:#ffffff"
+         d="M 881,317 L 857,304 L 875,290 L 899,304 L 881,317 z"
+         id="path4838" />
+      <path
+         style="fill:#ffffff"
+         d="M 929,335 L 903,327 L 920,310 L 945,319 L 929,335 z"
+         id="path4840" />
+      <path
+         style="fill:#ffffff"
+         d="M 776,198 L 771,174 L 789,153 L 794,178 L 776,198 z"
+         id="path4842" />
+    </g>
+  </g>
+</svg>
diff --git a/mistelix.csproj b/mistelix.csproj
index 1129122..e84fb13 100644
--- a/mistelix.csproj
+++ b/mistelix.csproj
@@ -97,6 +97,10 @@
     <Compile Include="src\widgets\VideosFileView.cs" />
     <Compile Include="src\Defines.cs" />
     <Compile Include="src\widgets\GtkUtils.cs" />
+    <Compile Include="src\widgets\WelcomeView.cs" />
+    <Compile Include="src\core\RecentFilesStorage.cs" />
+    <Compile Include="src\datamodel\RecentFile.cs" />
+    <Compile Include="src\widgets\CairoImageCellRenderer.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="src\mistelix.glade">
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7b8df35..366a79a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -19,6 +19,7 @@ src/core/SlideShowsProjectBuilder.cs
 src/core/ThumbnailSizeManager.cs
 src/datamodel/AspectRatio.cs
 src/datamodel/ProjectBuilder.cs
+src/datamodel/RecentFile.cs
 src/datamodel/TextPosition.cs
 src/dialogs/AboutDialog.cs
 src/dialogs/AddSlideDialog.cs
@@ -37,4 +38,5 @@ src/widgets/GtkUtils.cs
 src/widgets/ProjectElementView.cs
 src/widgets/SlideShowImageView.cs
 src/widgets/SlideShowView.cs
+src/widgets/WelcomeView.cs
 
diff --git a/src/Defines.cs.in b/src/Defines.cs.in
index e723789..1f663f5 100644
--- a/src/Defines.cs.in
+++ b/src/Defines.cs.in
@@ -32,6 +32,7 @@ static public class Defines
 	public const string DATA_DIR = "@prefix@/share/mistelix/";
 	public const string PROJECT_EXTENSION = ".mistelix";
 	public const string PROJECT_EXTENSION_FILTER = "*.mistelix";
+	public const string RECENTLY_USED = "mistelix.recently";
 
 	public const string SPUMUX_FILE = "spumux_mistelix.xml";
 	public const string DVDAUTHOR_FILE = "dvd_mistelix.xml";
diff --git a/src/Makefile.am b/src/Makefile.am
index 5cb895c..f7fcd13 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -70,7 +70,10 @@ MISTELIX_CSDISTFILES =				\
 	$(srcdir)/widgets/DataImageSurface.cs \
 	$(srcdir)/core/ResolutionManager.cs \
 	$(srcdir)/dialogs/AudioSelectionDialog.cs \
-	$(srcdir)/widgets/CairoImageCellRenderer.cs
+	$(srcdir)/widgets/CairoImageCellRenderer.cs \
+	$(srcdir)/widgets/WelcomeView.cs \
+	$(srcdir)/datamodel/RecentFile.cs \
+	$(srcdir)/core/RecentFilesStorage.cs
 
 ASSEMBLIES = \
 	 $(MISTELIX_LIBS)    		\
@@ -81,6 +84,8 @@ RESOURCES =										\
 -resource:$(srcdir)/mistelix.glade  \
 -resource:$(top_srcdir)/data/mistelix.svg  \
 -resource:$(top_srcdir)/data/mistelix.png  \
+-resource:$(top_srcdir)/data/button-slideshow.svg \
+-resource:$(top_srcdir)/data/button-dvd.svg  \
 -resource:$(srcdir)/mistelix.addin.xml
 
 mistelixdir = $(libdir)/mistelix
diff --git a/src/core/Button.cs b/src/core/Button.cs
index 0f2fc11..fcb9630 100644
--- a/src/core/Button.cs
+++ b/src/core/Button.cs
@@ -45,6 +45,7 @@ namespace Mistelix.Core
 
 		}
 
+		// TODO: A button belongs to a project and it should be specified in the constructor once
 		public Button (int x, int y, int linked_id) : base (x, y, linked_id)
 		{
 		}
@@ -77,7 +78,13 @@ namespace Mistelix.Core
 				return true;
 
 			return false;
-		}		
+		}
+
+		// Indicates if the thumbnail is actually created or if the draw operation will need
+		// to create it
+		public bool IsThumbnailCreated {
+			get { return thumbnail != null; }
+		}
 
 		public void Draw (Cairo.Context cr, Project project)
 		{
@@ -133,7 +140,7 @@ namespace Mistelix.Core
 			}
 		}
 	
-		void LoadThumbnail (Project project)
+		public void LoadThumbnail (Project project)
 		{
 			Gdk.Pixbuf pix = null;
 			Resolution size;
diff --git a/src/core/DvdMenu.cs b/src/core/DvdMenu.cs
index 51c6943..cffda20 100644
--- a/src/core/DvdMenu.cs
+++ b/src/core/DvdMenu.cs
@@ -26,6 +26,7 @@ using System.IO;
 using Gdk;
 using Mono.Addins;
 using System.Collections.Generic;
+using System.ComponentModel;
 
 using Mistelix.Transitions;
 using Mistelix.DataModel;
@@ -38,6 +39,10 @@ namespace Mistelix.Core
 	{
 		Project project;
 		Gdk.Pixbuf background;
+		bool asynchronous;
+		BackgroundWorker thumbnailing;
+
+		public EventHandler CompletedThumbnails;
 
 		public DvdMenu (Project project)
 		{
@@ -50,6 +55,22 @@ namespace Mistelix.Core
 			Dispose (false);
 		}
 
+		public bool Asynchronous {
+			get { return asynchronous; }
+			set {
+				if (value == true && CompletedThumbnails == null)
+					throw new InvalidOperationException ("You must set the CompletedThumbnail event");
+
+				asynchronous = value;
+
+				if (value == true) {
+					thumbnailing = new BackgroundWorker ();
+					thumbnailing.WorkerSupportsCancellation = true;
+					thumbnailing.DoWork += new DoWorkEventHandler (DoWork);
+				}
+			}
+		}
+
 		public void Dispose ()
 		{
 			Dispose (true);
@@ -74,8 +95,6 @@ namespace Mistelix.Core
 			string outfile;
 			sw = new SlideShow ();
 			sw.filename = Defines.MAIN_MENU_FILE_SRC;
-			//sw.audiofile = "/home/jordi/dev/mistelix/themes/music/incompetech/Fluidscape.mp3";
-			//sw.audiofile = "/home/jordi/dev/mistelix/libmistelix/song.mp3";
 			sw.images.Add (new SlideImage (project.FileToFullPath (Defines.MAIN_MENU_FILE_PNG), string.Empty, 5, TransitionManager.None.Name));
 			outfile = sw.Generate (project, null);
 		
@@ -134,6 +153,8 @@ namespace Mistelix.Core
 	
 		public void Draw (Cairo.Context cr, Core.Button moving_button)
 		{
+			bool fire_thread = false;
+
 			// See: http://kapo-cpp.blogspot.com/2008/01/drawing-pixbuf-to-cairo-context-2.html
 
 			if (background != null) {
@@ -148,23 +169,57 @@ namespace Mistelix.Core
 			foreach (Button button in project.Buttons)
 			{
 				if (moving_button != null && moving_button.LinkedId == button.LinkedId) {
-					// In the button object the image is already loaded and cached
-					button.Draw (cr, project, moving_button.X, moving_button.Y);
+					if (DrawButton (cr, button) == true)
+						fire_thread = true;
+
 					GtkUtils.DrawSelectionBox (cr, moving_button.X, moving_button.Y, button.DrawingBoxWidth, button.DrawingBoxHeight);
 				}
-				else
-					button.Draw (cr, project);
+				else {
+					if (DrawButton (cr, button) == true)
+						fire_thread = true;
+				}
+			}
+
+			if (fire_thread == true) {
+				if (thumbnailing.IsBusy == false)
+					thumbnailing.RunWorkerAsync (this);
 			}
 		}
+		
+		// return value indicates if the thumbnail of the button was not painted and we need to create them after
+		bool DrawButton (Cairo.Context cr, Button button)
+		{
+			if (asynchronous == false || 
+				(asynchronous == true && button.IsThumbnailCreated)) {
+				// In the button object the image is already loaded and cached
+				button.Draw (cr, project);
+				return false;
+			}
+			return true;
+		}
 
 		public void Save (string file)
 		{
 			Cairo.ImageSurface s = new Cairo.ImageSurface (Cairo.Format.Rgb24, project.Details.Width, project.Details.Height);
 			Cairo.Context cr = new Cairo.Context (s);
+			bool previous = asynchronous;
+
+			asynchronous = false;
 			Draw (cr, null);
+			asynchronous = previous;
+
 			s.WriteToPng (file);
 			((IDisposable)cr).Dispose ();
 			s.Destroy ();
 		}
+
+		void DoWork (object sender, DoWorkEventArgs e)
+		{
+			foreach (Button button in project.Buttons)
+				button.LoadThumbnail (project);
+
+			if (CompletedThumbnails != null)
+				CompletedThumbnails (this, EventArgs.Empty);
+		}
 	}
 }
diff --git a/src/core/RecentFilesStorage.cs b/src/core/RecentFilesStorage.cs
new file mode 100644
index 0000000..582e308
--- /dev/null
+++ b/src/core/RecentFilesStorage.cs
@@ -0,0 +1,158 @@
+//
+// Copyright (C) 2009 Jordi Mas i Hernandez, jmas softcatala org
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+using System.Collections;
+using System.Xml.Schema;
+
+using Mistelix.DataModel;
+
+
+namespace Mistelix.Core
+{
+	//
+	//  Implementation of a recent changed files list stored in XML
+	//  http://standards.freedesktop.org/recent-file-spec/recent-file-spec-0.2.html
+	//
+	class RecentFilesStorage 
+	{
+		string filename;
+		string config_path;
+		ObservableList <RecentFile> items;
+
+		const int MAX_ITEMS = 5;
+		const string RECENT_TAG = "RecentFiles";
+
+		public ObservableList <RecentFile>.CollectionChangedEventHandler ListUpdated;
+
+		public RecentFilesStorage ()
+		{
+			config_path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+			config_path = Path.Combine (config_path, Defines.APPNAME_LOWER);
+			filename = Path.Combine (config_path, Defines.RECENTLY_USED);
+
+			items = new ObservableList <RecentFile> ();
+			Read ();
+
+			items.CollectionChanged += OnCollectionChanged;
+		}
+		
+		public ObservableList <RecentFile> Items {
+			get { return items; }
+		}
+
+		void OnCollectionChanged (object sender, ObservableList <RecentFile>.CollectionChangedEventArgs e)
+		{
+			if (ListUpdated != null)
+				ListUpdated (sender, e);
+		}
+
+		// Adds a recently opened file
+		public void Add (string filename)
+		{
+			RecentFile item;
+	
+			Logger.Debug ("RecentFilesStorage.Add {0}, count {1}", filename, items.Count);
+
+			// If the file was already in the list, just update the timestamp
+			for (int i = 0; i < Items.Count; i++)
+			{
+				if (items[i].filename == filename) {
+					item = items[i];
+					item.UpdateTimeStamp ();
+					Logger.Debug ("RecentFilesStorage.Add has updated {0} (prev {1})", item, items[i]);
+					items[i] = item;
+					Write ();
+					return;
+				}
+			}
+
+			item = new RecentFile (filename);
+
+			if (items.Count >= MAX_ITEMS) {
+				DateTime timestamp;
+				int older = -1;
+
+				// Remove the older item
+				for (int i = 0; i < Items.Count; i++)
+				{
+					if (i == 0 || items[i].timestamp < timestamp) {
+						timestamp = items[i].timestamp;
+						older = i;
+					}
+				}
+
+				Logger.Debug ("RecentFilesStorage.Add. Count > MAX_ITEMS. Older item {0}", items [older]);
+				items.Remove (items[older]);
+			}
+
+			items.Add (item);
+			Write ();
+		}
+
+		public void Read ()
+		{	
+			if (File.Exists (filename) == false)
+				return;
+
+			XmlStorage ps = new XmlStorage ();
+			ps.Load (filename);
+
+			XmlNode root;
+			string from_node = RECENT_TAG;
+
+			if ((ps.xml_document.ChildNodes.Count > 0) == false)
+				return;
+
+			root = ps.xml_document.ChildNodes [1];
+
+			RecentFile item = new RecentFile ();
+			foreach (XmlNode node in root.ChildNodes) 
+			{
+				XmlSerializer serializer = new XmlSerializer (typeof (RecentFile));
+				StringReader reader = new StringReader (node.OuterXml);
+				item = (RecentFile) serializer.Deserialize (reader);
+				reader.Close ();
+				items.Add (item);
+			}
+		}
+
+		public void Write ()
+		{
+			if (!Directory.Exists (config_path))
+				Directory.CreateDirectory (config_path);
+
+			XmlStorage storage = new XmlStorage ();
+			storage.New (RECENT_TAG);
+
+			foreach (RecentFile item in items)
+				storage.Add <RecentFile> (item);
+
+			storage.Save (filename);
+		}
+	}
+}
diff --git a/src/core/XmlStorage.cs b/src/core/XmlStorage.cs
index 6c5af5d..e8cca93 100644
--- a/src/core/XmlStorage.cs
+++ b/src/core/XmlStorage.cs
@@ -48,7 +48,7 @@ namespace Mistelix.Core
 	//
 	public class XmlStorage
 	{
-		XmlDocument xml_document;
+		public XmlDocument xml_document;
 
 		public XmlStorage ()
 		{
@@ -164,7 +164,7 @@ namespace Mistelix.Core
 			return fragment.ChildNodes[0].Name;
 		}
 
-		bool GetFromNode <T> (XmlNode from_node, ref T item)
+		public bool GetFromNode <T> (XmlNode from_node, ref T item)
 		{
 			XmlSerializer serializer = new XmlSerializer (typeof (T));
 			StringReader reader = new StringReader (from_node.InnerXml);
diff --git a/src/datamodel/ObservableList.cs b/src/datamodel/ObservableList.cs
index 16ae752..c42a58b 100644
--- a/src/datamodel/ObservableList.cs
+++ b/src/datamodel/ObservableList.cs
@@ -32,7 +32,37 @@ namespace Mistelix.DataModel
 	// This an ObservableList that fires events when it is modified
 	public class ObservableList <T> : IEnumerable <T>
 	{
-		public virtual event EventHandler CollectionChanged;
+		public enum ChangeType
+		{
+			ElementChanged,
+			ElementRemoved,
+			ListCleared,
+			ElementAdded
+		}
+
+		public delegate void CollectionChangedEventHandler (object sender, CollectionChangedEventArgs e);
+
+		public class CollectionChangedEventArgs: EventArgs
+		{
+			ChangeType type;
+			T item;
+
+			public CollectionChangedEventArgs (ChangeType type, T item)
+			{
+				this.type = type;
+				this.item = item;
+			}
+
+			public ChangeType Type {
+				get {return type;}
+			}
+
+			public T Item {
+				get {return item;}
+			}
+		}
+
+		public virtual event CollectionChangedEventHandler CollectionChanged;
 		List <T> list;
 
 		public ObservableList ()
@@ -50,7 +80,7 @@ namespace Mistelix.DataModel
 				list [index] = value;
 
 				if (CollectionChanged != null)
-					CollectionChanged (this, EventArgs.Empty);
+					CollectionChanged (this, new CollectionChangedEventArgs (ChangeType.ElementChanged, value));
 			}
 		}
 
@@ -59,7 +89,7 @@ namespace Mistelix.DataModel
 			list.Clear ();
 
 			if (CollectionChanged != null)
-				CollectionChanged (this, EventArgs.Empty);
+				CollectionChanged (this, new CollectionChangedEventArgs (ChangeType.ListCleared, default (T)));
 		}
 
 		public void Add (T item)
@@ -67,7 +97,7 @@ namespace Mistelix.DataModel
 			list.Add (item);
 
 			if (CollectionChanged != null)
-				CollectionChanged (this, EventArgs.Empty);
+				CollectionChanged (this, new CollectionChangedEventArgs (ChangeType.ElementAdded, item));
 		}
 
 		public void Remove (T item)
@@ -75,7 +105,16 @@ namespace Mistelix.DataModel
 			list.Remove (item);
 
 			if (CollectionChanged != null)
-				CollectionChanged (this, EventArgs.Empty);
+				CollectionChanged (this, new CollectionChangedEventArgs (ChangeType.ElementRemoved, item));
+		}
+
+		public void RemoveAt (int pos)
+		{
+			T item = this [pos];
+			list.RemoveAt (pos);
+
+			if (CollectionChanged != null)
+				CollectionChanged (this, new CollectionChangedEventArgs (ChangeType.ElementRemoved, item));
 		}
 
 		IEnumerator <T> IEnumerable <T>.GetEnumerator ()
diff --git a/src/datamodel/Project.cs b/src/datamodel/Project.cs
index 49151dc..e52fd3c 100644
--- a/src/datamodel/Project.cs
+++ b/src/datamodel/Project.cs
@@ -213,6 +213,22 @@ namespace Mistelix.DataModel
 
 			for (int i = 0; i < Elements.Count; i++)
 				Logger.Debug ("Project.Save. {0}", Elements[i]);
-		}		
+		}
+
+		public Gdk.Pixbuf GetThumbnail (int width, int height)
+		{
+			Gdk.Pixbuf thumbnail = null;
+
+			foreach (VisibleProjectElement element in Elements)
+			{
+				if ((typeof (SlideShow) != element.GetType ()) &&
+					(typeof (Video) != element.GetType ()))
+						continue;
+
+				thumbnail = element.GetThumbnail (width, height);
+				break;
+			}
+			return thumbnail;
+		}
 	}
 }
diff --git a/src/datamodel/RecentFile.cs b/src/datamodel/RecentFile.cs
new file mode 100644
index 0000000..e272585
--- /dev/null
+++ b/src/datamodel/RecentFile.cs
@@ -0,0 +1,123 @@
+//
+// Copyright (C) 2009 Jordi Mas i Hernandez, jmas softcatala org
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+using Mono.Unix;
+
+using System.Xml.Schema;
+
+namespace Mistelix.DataModel
+{
+	// Contains a RecentFile element that is a part of recent list of files accessed
+	public struct RecentFile : IXmlSerializable
+	{
+		static readonly DateTime UnixStartTime = new DateTime (1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+		static readonly string mistelix_mime = "application/mistelix";
+
+		public string filename;
+		public string mimetype;
+		public DateTime timestamp;
+
+		public RecentFile (string filename)
+		{
+			this.filename = filename;
+			mimetype = mistelix_mime;
+			timestamp = DateTime.UtcNow;
+		}
+
+		public string TimeSinceEdited {
+			get {
+				TimeSpan sincelast = DateTime.UtcNow - timestamp;
+
+				if (sincelast.Days >= 1)
+					return String.Format (Catalog.GetPluralString ("{0} day", "{0} days", sincelast.Days), sincelast.Days);
+				if (sincelast.Hours >= 1)
+					return String.Format (Catalog.GetPluralString ("{0} hour", "{0} hours", sincelast.Hours), sincelast.Hours);
+				if (sincelast.Minutes > 0)
+					return String.Format (Catalog.GetPluralString ("{0} minute", "{0} minutes", sincelast.Minutes), sincelast.Minutes);
+			
+				return Catalog.GetString ("Less than a minute");
+			}
+		}
+
+		public void UpdateTimeStamp ()
+		{
+			timestamp = DateTime.UtcNow;
+		}
+
+		public void WriteXml (XmlWriter writer)
+		{
+			writer.WriteElementString ("filename", filename);
+			writer.WriteElementString ("mimetype", mimetype);
+			writer.WriteElementString ("timestamp", ToUnixTime (timestamp).ToString ());
+		}
+
+		public void ReadXml (XmlReader reader)
+		{
+			mimetype = mistelix_mime;
+
+			while (reader.Read ()) 
+			{
+				if (reader.NodeType != XmlNodeType.Element)
+					continue;
+
+				switch (reader.LocalName) {
+				case "filename":
+					filename = reader.ReadElementString ();
+					break;
+				case "timestamp": {
+					int time = reader.ReadElementContentAsInt ();
+					timestamp = ToDateTime (time);
+					break;
+					}
+				default:
+					break;
+				}
+			}
+		}
+		
+		public XmlSchema GetSchema ()
+		{
+			  return null;
+		}
+
+		static int ToUnixTime (DateTime time)
+		{
+			return (int)(time - UnixStartTime).TotalSeconds;
+		}
+
+		static DateTime ToDateTime (int unixTime)
+		{
+			return UnixStartTime.AddSeconds (unixTime);
+		}
+
+		public override string ToString  ()
+		{
+			return "Filename: " + filename + " timestamp " + timestamp;
+		}
+	}
+}
diff --git a/src/dialogs/AboutDialog.cs b/src/dialogs/AboutDialog.cs
index 4f6dac8..0f2d4bd 100644
--- a/src/dialogs/AboutDialog.cs
+++ b/src/dialogs/AboutDialog.cs
@@ -52,7 +52,7 @@ namespace Mistelix.Dialogs
 			Logo = LoadFromAssembly ("mistelix.svg");
 
 			Copyright = "Jordi Mas i Hernandez <jmas softcatala org>";
-			Comments = Catalog.GetString ("Mistelix is a DVD authoring application with also Theora slideshow creation capabilities.");
+			Comments = Catalog.GetString ("Mistelix is a DVD authoring application also with slideshow creation capabilities.");
 			Website = "http://www.mistelix.org";;
 			WebsiteLabel = Catalog.GetString ("Mistelix web site");
 			TranslatorCredits = translators;
diff --git a/src/dialogs/NewProjectDialog.cs b/src/dialogs/NewProjectDialog.cs
index 8fabf71..7a07827 100644
--- a/src/dialogs/NewProjectDialog.cs
+++ b/src/dialogs/NewProjectDialog.cs
@@ -40,6 +40,7 @@ namespace Mistelix.Dialogs
 		[Glade.Widget] Gtk.Entry output_dir;
 		[Glade.Widget] Gtk.Entry name;
 		[Glade.Widget] Gtk.RadioButton slideshows_radio;
+		[Glade.Widget] Gtk.RadioButton dvd_radio;
 		[Glade.Widget] Gtk.Label vformat_label;
 		[Glade.Widget] Gtk.Label aratio_label;
 		[Glade.Widget] Gtk.Label resolution_label;
@@ -103,6 +104,14 @@ namespace Mistelix.Dialogs
 			ProjectTypeSensitive ();
 		}
 
+		public NewProjectDialog (ProjectType type) : this ()
+		{
+			if (type == ProjectType.DVD) {
+				slideshows_radio.Active = false;
+				dvd_radio.Active = true;
+			}
+		}
+
 		public Project NewProject { 
 			get { return project; }
 		}
diff --git a/src/mistelix.cs b/src/mistelix.cs
index e2916ae..9ac8d39 100644
--- a/src/mistelix.cs
+++ b/src/mistelix.cs
@@ -62,12 +62,16 @@ namespace Mistelix
 		[Glade.Widget] Gtk.CheckMenuItem safearea_menuitem;
 		[Glade.Widget] Gtk.MenuItem add_slideshow;
 		[Glade.Widget] Gtk.MenuItem add_videos;
+		[Glade.Widget] Gtk.VBox rightpaned_vbox;
+		[Glade.Widget] Gtk.VBox buttons_vbox;
 
 		AuthoringPaneView authoring_view;
 		Project project;
 		ProjectElementView element_view;
+		WelcomeView welcome_view;
 		static string base_dir;
 		static Preferences preferences;
+		static RecentFilesStorage recent_files;
 		static bool debugging;
 
 		static Mistelix ()
@@ -80,6 +84,7 @@ namespace Mistelix
 			SetupService setupService = new SetupService (AddinManager.Registry);
 
 			preferences = new Preferences ();
+			recent_files = new RecentFilesStorage ();
 
 			if (String.Compare (Environment.GetEnvironmentVariable ("MISTELIX_DEBUG"), "true", false) == 0) { 
 				debugging = true;
@@ -105,8 +110,6 @@ namespace Mistelix
 			foreach (TypeExtensionNode node in EffectManager.List)
 				Logger.Info ("Extension:" + node.CreateInstance ());
 			
-			OnProjectView ();
-
 			//project = authoring_view.Project = element_view.Project = new Project ();
 			//project.Load ("/home/jordi/dev/mistelix/projects/Project_TwoSlidesShows.mistelix");
 			//element_view.LoadElements ();
@@ -115,12 +118,13 @@ namespace Mistelix
 			if (preferences.GetBoolValue (Preferences.SafeAreaKey) == true)
 				safearea_menuitem.Active = true;
 
-			SensitiveToProjectContext ();
+			CreateViews ();
+
 			app_window.IconName = "mistelix";
-			vpaned.Position = 700; // W (left)
 			hpaned.Position = 30; // H (right), controls position of top "Project elements" button bar
+			vpaned.Position = 700; // W (left)
 			LoadWindowPosition ();
-			app_window.ShowAll ();
+			SensitiveToProjectContext ();
 		}
 
 		public static Preferences Preferences {
@@ -131,11 +135,14 @@ namespace Mistelix
 			get { return debugging; }
 		}
 
+		public static RecentFilesStorage RecentFiles {
+			get { return recent_files; }
+		}
+
 		// Options only available in a project context
 		void SensitiveToProjectContext ()
 		{
 			bool active = (project == null ? false : true);
-			bool dvd_project = true;
 
 			save_project.Sensitive = active;
 			save_as.Sensitive = active;
@@ -143,16 +150,26 @@ namespace Mistelix
 			slideshows_button.Sensitive = active;
 			project_properties.Sensitive = active;
 			add_slideshow.Sensitive = active;
-
-			// Options sensitive to projecte type
-			if (project != null && project.Details.Type == ProjectType.Slideshows)
-				dvd_project = false;
- 
-			if (dvd_project) 
-				left_vbox.ShowAll ();
-			else {
+	
+			if (project == null) { // WelcomeView
+				rightpaned_vbox.Remove (scrolledwin_elements);
 				left_vbox.HideAll ();
-				active = false;
+				buttons_vbox.HideAll ();
+				welcome_view.ShowAll ();
+			}
+			else {
+				welcome_view.HideAll ();
+				buttons_vbox.ShowAll ();
+				rightpaned_vbox.Add (scrolledwin_elements);
+				if (project.Details.Type == ProjectType.DVD) {
+					left_vbox.ShowAll ();
+					element_view.ShowAll ();
+				}
+				else {
+					left_vbox.HideAll ();
+					element_view.ShowAll ();
+					active = false;
+				}
 			}
 
 			add_videos.Sensitive = active;
@@ -161,10 +178,15 @@ namespace Mistelix
 			videos_button.Sensitive = active;
 		}
 
-		void OnProjectView ()
+		void CreateViews ()
 		{
 			authoring_view = new AuthoringPaneView (left_vbox, project);
-			
+
+			welcome_view = new WelcomeView (rightpaned_vbox, recent_files);
+			welcome_view.ProjectOpen += new WelcomeView.ProjectOpenEventHandler (OnOpenRecentProject);
+			welcome_view.ButtonDVD += new EventHandler (OnProjectDVD);
+			welcome_view.ButtonSlideshow += new EventHandler (OnProjectSlideshow);
+
 			element_view = new ProjectElementView (project);
 			scrolledwin_elements.Add (element_view);
 		}
@@ -175,7 +197,9 @@ namespace Mistelix
 
 			try {
 				SetProcessName (Defines.APPNAME_LOWER);
-			} catch {}
+			} catch (Exception e) {
+				Logger.Error ("Mistelix.Main. Cannot set process");
+			}
 
 			gui.Run ();
 		}
@@ -342,7 +366,12 @@ namespace Mistelix
 
 		void OnNewProject (object sender, EventArgs args)
 		{	
-			NewProjectDialog dialog = new NewProjectDialog ();
+			NewProject (ProjectType.Slideshows);
+		}
+
+		void NewProject (ProjectType type)
+		{	
+			NewProjectDialog dialog = new NewProjectDialog (type);
 			if (dialog.Run () == ResponseType.Ok) {
 				project = authoring_view.Project = element_view.Project = dialog.NewProject;
 				SensitiveToProjectContext ();
@@ -352,6 +381,16 @@ namespace Mistelix
 			}
 		}
 
+		void OnProjectDVD (object sender, EventArgs args)
+		{
+			NewProject (ProjectType.DVD);
+		}
+
+		void OnProjectSlideshow (object sender, EventArgs args)
+		{
+			NewProject (ProjectType.Slideshows);
+		}
+
 		void OnThemeBrowser (object sender, EventArgs args)
 		{
 			ThemeSelectionDialog dialog = new ThemeSelectionDialog (project);
@@ -364,7 +403,6 @@ namespace Mistelix
 		{
 			ResponseType response;
 			string filename;
-			Project load_project;
 			FileFilter filter = new FileFilter ();
 			FileChooserDialog chooser_dialog = new FileChooserDialog (
 				Catalog.GetString ("Open Project") , null, FileChooserAction.Open);
@@ -386,16 +424,26 @@ namespace Mistelix
 			if (response != ResponseType.Ok)
 				return;
 
+			OpenProject (filename);
+		}
+
+		void OpenProject (string filename)
+		{
 			try {
+				Project load_project;
+
 				load_project = new Project ();
 				load_project.Load (filename);
 				project = authoring_view.Project = element_view.Project = load_project;
 				SensitiveToProjectContext ();
+				app_window.QueueDraw ();
 				element_view.LoadElements ();
 				authoring_view.UpdateTheme ();
 				app_window.QueueDraw ();
 				UpdateWindowTitle ();
 
+				recent_files.Add (filename);
+
 			} catch (Exception e) {
 				
 				String msg;
@@ -407,6 +455,8 @@ namespace Mistelix
 				md.Run ();
 				md.Destroy ();
 			}
+
+
 		}
 
 		void OnSaveProject (object sender, EventArgs args)
@@ -463,6 +513,7 @@ namespace Mistelix
 			try 
 			{
 				project.Save (filename);
+				recent_files.Add (filename);
 				return;
 			} 
 			catch (Exception e)
@@ -518,6 +569,17 @@ namespace Mistelix
 				String.Format ("{0} - {1}", project.Details.Name, Defines.APPNAME);
 		}
 
+		void OnClose (object sender, EventArgs args)
+		{
+			project = null;
+			SensitiveToProjectContext ();
+		}
+
+		void OnOpenRecentProject (object sender, WelcomeView.ProjectOpenEventHandlerEventArgs args)
+		{
+			OpenProject (args.Project);
+		}
+
 		[DllImport ("libc")] // Linux
 		private static extern int prctl (int option, byte [] arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5);
 
@@ -543,3 +605,4 @@ namespace Mistelix
 		}
 	}
 }
+
diff --git a/src/mistelix.glade b/src/mistelix.glade
index 4eca3a2..2f0a6c3 100644
--- a/src/mistelix.glade
+++ b/src/mistelix.glade
@@ -49,7 +49,7 @@
 		      <signal name="activate" handler="OnNewProject" last_modification_time="Sat, 11 Oct 2008 15:55:34 GMT"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image33">
+			<widget class="GtkImage" id="image39">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-new</property>
 			  <property name="icon_size">1</property>
@@ -70,7 +70,7 @@
 		      <signal name="activate" handler="OnOpenProject" last_modification_time="Sat, 11 Oct 2008 15:55:34 GMT"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image34">
+			<widget class="GtkImage" id="image40">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-open</property>
 			  <property name="icon_size">1</property>
@@ -91,7 +91,7 @@
 		      <signal name="activate" handler="OnSaveProject" last_modification_time="Sat, 11 Oct 2008 15:55:34 GMT"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image35">
+			<widget class="GtkImage" id="image41">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-save</property>
 			  <property name="icon_size">1</property>
@@ -112,7 +112,7 @@
 		      <signal name="activate" handler="OnSaveAs" last_modification_time="Tue, 30 Jun 2009 05:18:51 GMT"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image36">
+			<widget class="GtkImage" id="image42">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-save-as</property>
 			  <property name="icon_size">1</property>
@@ -132,6 +132,15 @@
 		  </child>
 
 		  <child>
+		    <widget class="GtkImageMenuItem" id="close">
+		      <property name="visible">True</property>
+		      <property name="label">gtk-close</property>
+		      <property name="use_stock">True</property>
+		      <signal name="activate" handler="OnClose" last_modification_time="Tue, 21 Jul 2009 09:30:04 GMT"/>
+		    </widget>
+		  </child>
+
+		  <child>
 		    <widget class="GtkImageMenuItem" id="quit1">
 		      <property name="visible">True</property>
 		      <property name="label">gtk-quit</property>
@@ -351,7 +360,7 @@
 		  <property name="position">0</property>
 
 		  <child>
-		    <widget class="GtkVBox" id="vbox4">
+		    <widget class="GtkVBox" id="buttons_vbox">
 		      <property name="visible">True</property>
 		      <property name="homogeneous">False</property>
 		      <property name="spacing">0</property>
diff --git a/src/widgets/AuthoringPaneView.cs b/src/widgets/AuthoringPaneView.cs
index 74cd67a..8449819 100644
--- a/src/widgets/AuthoringPaneView.cs
+++ b/src/widgets/AuthoringPaneView.cs
@@ -77,13 +77,19 @@ namespace Mistelix.Widgets
 					menu.Dispose ();
 				
 				menu = new DvdMenu (project);
+				menu.CompletedThumbnails += delegate 
+				{ 
+					Application.Invoke (delegate { QueueDraw (); });
+				};
+
+				menu.Asynchronous = true;
 
 				if (project != null)
 				{
 					project.Buttons.CollectionChanged += delegate 
 					{ 
 						Logger.Debug ("AuthoringPaneView.Project. CollectionChanged");
-						QueueDraw ();
+						Application.Invoke (delegate { QueueDraw (); });
 					};
 				}
 			}
diff --git a/src/widgets/SlideShowView.cs b/src/widgets/SlideShowView.cs
index 91993a8..1fe6a60 100644
--- a/src/widgets/SlideShowView.cs
+++ b/src/widgets/SlideShowView.cs
@@ -23,8 +23,6 @@
 //
 
 using System;
-using System.Media;
-
 using Cairo;
 using Gtk;
 using Gdk;
@@ -89,7 +87,6 @@ namespace Mistelix.Widgets
 		int current_index_image;
 		SlideImage current_image;
 		Transition transition;
-		SoundPlayer player;
 		bool paused = false;
 		const uint MinTime = 100;
 		bool started = false;
diff --git a/src/widgets/WelcomeView.cs b/src/widgets/WelcomeView.cs
new file mode 100644
index 0000000..0a5a845
--- /dev/null
+++ b/src/widgets/WelcomeView.cs
@@ -0,0 +1,425 @@
+//
+// Copyright (C) 2009 Jordi Mas i Hernandez, jmas softcatala org
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using Gtk;
+using Gdk;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Mono.Unix;
+
+using Mistelix.Widgets;
+using Mistelix.DataModel;
+using Mistelix.Core;
+
+namespace Mistelix.Widgets
+{
+	class WelcomeView : Gtk.HBox
+	{
+		// Used to report when a file has been open from the recent file list
+		public delegate void ProjectOpenEventHandler (object sender, ProjectOpenEventHandlerEventArgs e);
+
+		public class ProjectOpenEventHandlerEventArgs: EventArgs 
+		{
+			string project;
+
+			public ProjectOpenEventHandlerEventArgs (string project)
+			{
+				this.project = project;
+			}
+
+			public string Project {
+				get { return project; }
+			}
+		}
+		
+		// Contains the list of recent projects
+		class RecentProjectsTreeView : TreeView
+		{
+			ListStore tree_store;
+			WelcomeView view;
+			BackgroundWorker thumbnailing;
+			bool empty;
+
+			public RecentProjectsTreeView (WelcomeView view)
+			{
+				this.view = view;
+				Model = tree_store = CreateStore ();
+				RulesHint = true;
+
+				AppendColumn (Catalog.GetString ("Project"), new CellRendererText (), "text", COL_NAME);
+				AppendColumn (Catalog.GetString ("Last Accessed"), new CellRendererText (), "text", COL_DATE);
+				AppendColumn (Catalog.GetString ("Thumbnail"), new CellRendererPixbuf (), "pixbuf", COL_PIXBUF);
+				RowActivated += OnRowActivate;
+				tree_store.SetSortFunc (0, new Gtk.TreeIterCompareFunc (SortByTime));
+
+				thumbnailing = new BackgroundWorker ();
+				thumbnailing.DoWork += new DoWorkEventHandler (DoWork);
+
+				PopulateRecentFiles ();
+			}
+
+			ListStore CreateStore ()
+			{
+				// Image filename, date, Image, full filename, thumbnail_done
+				return new ListStore (typeof (string), typeof (string), typeof (Gdk.Pixbuf) , typeof (RecentFile), typeof (bool));
+			}
+
+			void OnRowActivate (object o, RowActivatedArgs args)
+			{	
+				if (view.ProjectOpen == null)
+					return;
+				
+				TreeIter iter;
+				tree_store.GetIter (out iter, args.Path);
+				RecentFile recent;
+
+				if (tree_store.GetValue (iter, COL_OBJECT) == null)
+					return;
+
+				recent = (RecentFile) tree_store.GetValue (iter, COL_OBJECT);
+				view.ProjectOpen (this, new ProjectOpenEventHandlerEventArgs (recent.filename));
+			}
+
+			public void PopulateRecentFiles ()
+			{
+				tree_store.Clear ();
+				if (view.recent_files.Items.Count == 0)
+				{
+					tree_store.AppendValues (Catalog.GetString ("No recent files"), "", null, null);
+					empty = true;
+					return;
+				}
+
+				empty = false;
+				foreach (RecentFile recent in view.recent_files.Items)
+				{
+					FileInfo fi = new FileInfo (recent.filename);
+					tree_store.AppendValues (fi.Name, recent.TimeSinceEdited, null, recent, false);
+				}
+
+				tree_store.SetSortColumnId (0, SortType.Descending);
+
+				if (thumbnailing.IsBusy == false)
+					thumbnailing.RunWorkerAsync (tree_store);
+			}
+
+			public void AddRecentFile (RecentFile recent)
+			{
+				if (empty == true) {
+					empty = false;
+					tree_store.Clear ();
+				}
+
+				FileInfo fi = new FileInfo (recent.filename);
+				tree_store.AppendValues (fi.Name, recent.TimeSinceEdited, null, recent, false);
+				tree_store.SetSortColumnId (0, SortType.Descending);
+
+				if (thumbnailing.IsBusy == false)
+					thumbnailing.RunWorkerAsync (tree_store);
+			}
+
+			public void UpdateRecentFile (RecentFile item)
+			{
+				tree_store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)  
+				{
+					RecentFile recent = (RecentFile) tree_store.GetValue (iter, COL_OBJECT);
+					if (recent.filename == item.filename)
+					{
+						tree_store.SetValue (iter, COL_OBJECT, item);
+						tree_store.SetValue (iter, COL_DATE, item.TimeSinceEdited);
+						tree_store.SetValue (iter, COL_THUMBNAIL, false);
+						return true;
+					}
+					return false;
+				});
+
+				tree_store.SetSortColumnId (0, SortType.Descending);
+
+				if (thumbnailing.IsBusy == false)
+					thumbnailing.RunWorkerAsync (tree_store);
+			}
+
+			public void RemoveRecentFile  (RecentFile item)
+			{
+				tree_store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)  
+				{
+					RecentFile recent = (RecentFile) tree_store.GetValue (iter, COL_OBJECT);
+					if (recent.filename == item.filename)
+					{
+						tree_store.Remove (ref iter);
+						return true;
+					}
+					return false;
+				});
+			}
+
+			int SortByTime (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b)
+			{
+				RecentFile recent_a, recent_b;
+
+				if (model.GetValue (a, COL_OBJECT) == null || model.GetValue (b, COL_OBJECT) == null)
+					return 0;
+
+				recent_a = (RecentFile) model.GetValue (a, COL_OBJECT);
+				recent_b = (RecentFile) model.GetValue (b, COL_OBJECT);
+		
+				return DateTime.Compare (recent_a.timestamp, recent_b.timestamp);
+			}
+
+			static void DoWork (object sender, DoWorkEventArgs e)
+			{
+				ListStore store = (ListStore) e.Argument;
+				Logger.Debug ("WelcomeView.DoWork. Start");
+
+				store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)  
+				{
+					RecentFile recent;
+					Gdk.Pixbuf im;
+					Project project;
+
+					if (store.GetValue (iter, COL_OBJECT) == null)
+						return false;
+
+					if (((bool) store.GetValue (iter, COL_THUMBNAIL)) == true)
+						return false;
+
+					recent = (RecentFile) store.GetValue (iter, COL_OBJECT);
+
+					try {
+						project = new Project ();
+						project.Load (recent.filename);
+
+						im = project.GetThumbnail (ThumbnailSizeManager.Current.Width, 
+						             ThumbnailSizeManager.Current.Height);
+
+						Logger.Debug ("WelcomeView.DoWork. Thumbnailed {0}", recent.filename);
+					}
+							
+					catch (Exception ex) {
+						Logger.Error ("WelcomeView.Dowork. Exception: " + ex.Message);
+						return false;
+					}
+
+					Application.Invoke (delegate {
+							if (im != null)
+								store.SetValue (iter, COL_PIXBUF, im);
+
+							store.SetValue (iter, COL_THUMBNAIL, true);
+						} );
+
+					return false;
+				});
+
+				Logger.Debug ("WelcomeView.DoWork. End");
+			}
+		}
+
+
+		Gtk.VBox action_vbox;
+		Gtk.Label welcome_label;
+		Gtk.Table action_table;
+		Gtk.Button dvd_button;
+		Gtk.Button slideshow_button;
+		Gtk.VBox dvd_vbox;
+		Gtk.Label recent_label;
+		RecentProjectsTreeView recent_treeview;
+		Gtk.Button buttonCancel;
+		Gtk.Button buttonOk;
+
+		RecentFilesStorage recent_files;
+
+		const int COL_NAME = 0;
+		const int COL_DATE = 1;
+		const int COL_PIXBUF = 2;
+		const int COL_OBJECT = 3;
+		const int COL_THUMBNAIL = 4;
+		const int BUTTON_SIZE = 80;
+
+		public ProjectOpenEventHandler ProjectOpen;
+		public EventHandler ButtonDVD;
+		public EventHandler ButtonSlideshow;
+
+		public WelcomeView (Gtk.VBox box, RecentFilesStorage recent_files)
+		{
+			string text;
+
+			this.recent_files = recent_files;
+			this.recent_files.ListUpdated += new ObservableList <RecentFile>.CollectionChangedEventHandler (OnListUpdated);
+
+			Homogeneous = true;
+			Spacing = 6;
+			BorderWidth = 20;
+			// action_vbox packs all the elements in the left column
+			action_vbox = new Gtk.VBox();
+			action_vbox.Spacing = 30;
+
+			// Welcome label
+			welcome_label = new Gtk.Label ();
+			text = Catalog.GetString ("<b>Welcome to Mistelix</b>");
+			text += "\n\n";
+			text += Catalog.GetString ("Mistelix is a DVD authoring application also\nwith Theora slideshow creation capabilities.");
+			welcome_label.Markup = text;
+			action_vbox.Add (welcome_label);
+			Gtk.Box.BoxChild w2 = ((Gtk.Box.BoxChild)(action_vbox[welcome_label]));
+			w2.Position = 0;
+			w2.Expand = false;
+			w2.Fill = false;
+
+			// Action table
+			action_table = new Gtk.Table (((uint)(2)), ((uint)(1)), false);
+			action_table.RowSpacing = ((uint)(50));
+			action_table.ColumnSpacing = ((uint)(1));
+
+			// DVD button
+			dvd_button = new Gtk.Button ();
+			dvd_button.CanFocus = true;
+			dvd_button.UseUnderline = true;
+			dvd_button.Clicked += new EventHandler (OnButtonDVD);
+
+			Gtk.Alignment w3 = new Gtk.Alignment(0.5F, 0.5F, 0F, 0F);
+			Gtk.HBox w4 = new Gtk.HBox ();
+			w4.Spacing = 2;
+			Gtk.Image w5 = new Gtk.Image();
+			Pixbuf pb = new Pixbuf (null, "button-dvd.svg");
+			w5.Pixbuf = pb.ScaleSimple (BUTTON_SIZE, BUTTON_SIZE, InterpType.Hyper);
+			w4.Add (w5);
+
+			Gtk.Label w7 = new Gtk.Label ();
+			text = Catalog.GetString ("Create a DVD project");
+			text += "\n\n";
+			text += Catalog.GetString ("Create a DVD with videos and slideshows\nthat can be played in DVD players.");
+			w7.LabelProp = text;
+
+			w7.UseUnderline = true;
+			w4.Add (w7);
+			w3.Add (w4);
+			dvd_button.Add (w3);
+			action_table.Add (dvd_button);
+			Gtk.Table.TableChild w11 = ((Gtk.Table.TableChild)(action_table[dvd_button]));
+			w11.TopAttach = ((uint)(1));
+			w11.BottomAttach = ((uint)(2));
+			w11.XOptions = ((Gtk.AttachOptions)(4));
+			w11.YOptions = ((Gtk.AttachOptions)(4));
+
+			// Slideshown button
+			slideshow_button = new Gtk.Button ();
+			slideshow_button.CanFocus = true;
+			slideshow_button.UseUnderline = true;
+			slideshow_button.Clicked += new EventHandler (OnButtonSlideshow);
+
+			Gtk.Alignment w12 = new Gtk.Alignment (0.5F, 0.5F, 0F, 0F);
+			Gtk.HBox w13 = new Gtk.HBox ();
+			w13.Spacing = 2;
+			Gtk.Image w14 = new Gtk.Image ();
+			pb = new Pixbuf (null, "button-slideshow.svg");
+			w14.Pixbuf = pb.ScaleSimple (BUTTON_SIZE, BUTTON_SIZE, InterpType.Hyper);
+			
+			w13.Add(w14);
+			// Container child GtkHBox.Gtk.Container+ContainerChild
+			Gtk.Label w16 = new Gtk.Label ();
+			w16.LabelProp = Mono.Unix.Catalog.GetString ("Create a Slideshow\n\nSlideshown from a collection of images\nthat can be played in any PC.");
+			w16.UseUnderline = true;
+			w13.Add (w16);
+			w12.Add (w13);
+			slideshow_button.Add (w12);
+			action_table.Add(slideshow_button);
+			Gtk.Table.TableChild w20 = ((Gtk.Table.TableChild)(action_table[slideshow_button]));
+			w20.XOptions = ((Gtk.AttachOptions)(4));
+			w20.YOptions = ((Gtk.AttachOptions)(4));
+			action_vbox.Add(action_table);
+			Gtk.Box.BoxChild w21 = ((Gtk.Box.BoxChild)(action_vbox[action_table]));
+			w21.Position = 1;
+			w21.Expand = false;
+			w21.Fill = false;
+
+			Add (action_vbox);
+			Gtk.Box.BoxChild w22 = (Gtk.Box.BoxChild) this[action_vbox];
+			w22.Position = 0;
+			w22.Expand = false;
+			w22.Fill = false;
+
+			// dvd_vbox packs all the elements in the left column
+			dvd_vbox = new Gtk.VBox ();
+			dvd_vbox.Spacing = 30;
+			// Container child dvd_vbox.Gtk.Box+BoxChild
+			recent_label = new Gtk.Label ();
+			recent_label.Markup = Mono.Unix.Catalog.GetString ("<b>Recent Projects</b>");
+			dvd_vbox.Add(recent_label);
+			Gtk.Box.BoxChild w23 = ((Gtk.Box.BoxChild)(dvd_vbox[recent_label]));
+			w23.Position = 0;
+			w23.Expand = false;
+			w23.Fill = false;
+
+			// Recent projects Treeview
+			recent_treeview = new RecentProjectsTreeView (this);
+			dvd_vbox.Add (recent_treeview);
+
+			Gtk.Box.BoxChild w24 = ((Gtk.Box.BoxChild)(dvd_vbox[recent_treeview]));
+			w24.Position = 1;
+			w24.Expand = false;
+
+			Add(dvd_vbox);
+			Gtk.Box.BoxChild w25 = (Gtk.Box.BoxChild) this[dvd_vbox];
+			w25.Position = 1;
+			w25.Expand = false;
+			w25.Fill = false;
+			box.Add(this);
+		}
+
+		void OnButtonDVD (object sender, EventArgs args)
+		{
+			if (ButtonDVD != null)
+				ButtonDVD (sender, args);
+		}
+
+		void OnButtonSlideshow (object sender, EventArgs args)
+		{
+			if (ButtonSlideshow != null)
+				ButtonSlideshow (sender, args);
+		}
+
+		void OnListUpdated (object sender, ObservableList <RecentFile>.CollectionChangedEventArgs e)
+		{
+			Logger.Debug ("WelcomeView.OnListUpdated. type:{0} item:s{1}", e.Type, e.Item);
+
+			switch (e.Type) {
+			case ObservableList<RecentFile>.ChangeType.ElementChanged:
+				recent_treeview.UpdateRecentFile (e.Item);
+				break;
+			case ObservableList<RecentFile>.ChangeType.ElementAdded:
+				recent_treeview.AddRecentFile (e.Item);
+				break;
+			case ObservableList<RecentFile>.ChangeType.ElementRemoved:
+				recent_treeview.RemoveRecentFile (e.Item);
+				break;
+			case ObservableList<RecentFile>.ChangeType.ListCleared:				
+				recent_treeview.PopulateRecentFiles ();
+				break;
+			default:
+				break;
+			}
+		}
+	}
+}



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