[gthumb] Added slideshow options to catalogs



commit 66d69e4d68ee36ffbff6f36bdd21b8547b3fa1b5
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Mon Feb 15 23:14:59 2010 +0100

    Added slideshow options to catalogs
    
    Added ability to specify a list of audio files to play during a
    slideshow, and to personalize the slidehoshow options for each
    catalog.

 configure.ac                                       |    3 +-
 extensions/catalogs/data/ui/catalog-properties.ui  |  166 +++++---
 extensions/catalogs/dlg-catalog-properties.c       |    1 +
 extensions/catalogs/gth-catalog.c                  |   14 +-
 extensions/catalogs/gth-catalog.h                  |    1 +
 extensions/catalogs/gth-file-source-catalogs.c     |   29 +-
 extensions/catalogs/main.c                         |   37 ++-
 extensions/search/callbacks.c                      |    3 +-
 extensions/slideshow/Makefile.am                   |    6 +-
 extensions/slideshow/actions.c                     |   38 ++-
 extensions/slideshow/callbacks.c                   |  307 +++++++++++++-
 extensions/slideshow/callbacks.h                   |   22 +-
 .../slideshow/data/ui/slideshow-preferences.ui     |  334 ++++++++++++---
 extensions/slideshow/gth-slideshow-preferences.c   |  461 ++++++++++++++++++++
 extensions/slideshow/gth-slideshow-preferences.h   |   72 +++
 extensions/slideshow/gth-slideshow.c               |   72 +++
 extensions/slideshow/gth-slideshow.h               |    3 +
 extensions/slideshow/main.c                        |    6 +
 extensions/slideshow/preferences.c                 |  103 +----
 extensions/slideshow/slideshow.extension.in.in     |    1 +
 gthumb/Makefile.am                                 |    2 +
 gthumb/glib-utils.c                                |   16 +
 gthumb/glib-utils.h                                |    1 +
 gthumb/gth-browser.c                               |    2 +-
 gthumb/gvaluehash.c                                |  201 +++++++++
 gthumb/gvaluehash.h                                |   79 ++++
 26 files changed, 1742 insertions(+), 238 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 84597f5..422fbe7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -177,7 +177,8 @@ if test x$enable_gstreamer = xyes ; then
 	PKG_CHECK_MODULES(GSTREAMER,
 			  [gstreamer-0.10 >= $GSTREAMER_REQUIRED
 			   gstreamer-interfaces-0.10],
-			  [enable_gstreamer=yes],
+			  [AC_DEFINE(HAVE_GSTREAMER, 1, [Have gstreamer])
+			   enable_gstreamer=yes],
 			  [enable_gstreamer=no])
 fi
 AC_SUBST(GSTREAMER_LIBS)
diff --git a/extensions/catalogs/data/ui/catalog-properties.ui b/extensions/catalogs/data/ui/catalog-properties.ui
index 1f56876..9dc6962 100644
--- a/extensions/catalogs/data/ui/catalog-properties.ui
+++ b/extensions/catalogs/data/ui/catalog-properties.ui
@@ -5,7 +5,6 @@
   <object class="GtkDialog" id="properties_dialog">
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Properties</property>
-    <property name="resizable">False</property>
     <property name="type_hint">normal</property>
     <property name="has_separator">False</property>
     <child internal-child="vbox">
@@ -20,103 +19,142 @@
             <property name="orientation">vertical</property>
             <property name="spacing">12</property>
             <child>
-              <object class="GtkVBox" id="vbox1">
+              <object class="GtkNotebook" id="properties_notebook">
                 <property name="visible">True</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">6</property>
-                <child>
-                  <object class="GtkLabel" id="label4">
-                    <property name="visible">True</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Catalog</property>
-                    <attributes>
-                      <attribute name="weight" value="bold"/>
-                    </attributes>
-                  </object>
-                  <packing>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
+                <property name="can_focus">True</property>
                 <child>
-                  <object class="GtkAlignment" id="alignment2">
+                  <object class="GtkVBox" id="general_vbox">
                     <property name="visible">True</property>
-                    <property name="left_padding">12</property>
+                    <property name="border_width">12</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
                     <child>
-                      <object class="GtkTable" id="table1">
+                      <object class="GtkVBox" id="vbox2">
                         <property name="visible">True</property>
-                        <property name="n_rows">2</property>
-                        <property name="n_columns">2</property>
-                        <property name="column_spacing">6</property>
-                        <property name="row_spacing">6</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
                         <child>
                           <object class="GtkLabel" id="label2">
                             <property name="visible">True</property>
                             <property name="xalign">0</property>
-                            <property name="label" translatable="yes">_Name:</property>
-                            <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">name_entry</property>
-                          </object>
-                          <packing>
-                            <property name="x_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkLabel" id="label3">
-                            <property name="visible">True</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">_Date:</property>
-                            <property name="use_underline">True</property>
-                          </object>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="x_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkEntry" id="name_entry">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="invisible_char">&#x25CF;</property>
-                            <property name="activates_default">True</property>
+                            <property name="label" translatable="yes">General</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
                           </object>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkHBox" id="date_container_box">
+                          <object class="GtkAlignment" id="alignment2">
                             <property name="visible">True</property>
+                            <property name="left_padding">12</property>
                             <child>
-                              <placeholder/>
+                              <object class="GtkTable" id="table1">
+                                <property name="visible">True</property>
+                                <property name="n_rows">2</property>
+                                <property name="n_columns">2</property>
+                                <property name="column_spacing">6</property>
+                                <property name="row_spacing">6</property>
+                                <child>
+                                  <object class="GtkLabel" id="label5">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">_Name:</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="mnemonic_widget">name_entry</property>
+                                  </object>
+                                  <packing>
+                                    <property name="x_options">GTK_FILL</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label6">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">_Date:</property>
+                                    <property name="use_underline">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkEntry" id="name_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="invisible_char">&#x25CF;</property>
+                                    <property name="activates_default">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkHBox" id="date_container_box">
+                                    <property name="visible">True</property>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="y_options">GTK_EXPAND</property>
+                                  </packing>
+                                </child>
+                              </object>
                             </child>
                           </object>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
                           </packing>
                         </child>
                       </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
                     </child>
                   </object>
+                </child>
+                <child type="tab">
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Catalog</property>
+                  </object>
                   <packing>
-                    <property name="position">1</property>
+                    <property name="tab_fill">False</property>
                   </packing>
                 </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
               </object>
               <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
                 <property name="position">0</property>
               </packing>
             </child>
           </object>
           <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
             <property name="position">1</property>
           </packing>
         </child>
diff --git a/extensions/catalogs/dlg-catalog-properties.c b/extensions/catalogs/dlg-catalog-properties.c
index d17184e..1d08186 100644
--- a/extensions/catalogs/dlg-catalog-properties.c
+++ b/extensions/catalogs/dlg-catalog-properties.c
@@ -240,6 +240,7 @@ dlg_catalog_properties (GthBrowser  *browser,
 
 	/* run dialog. */
 
+	gtk_widget_grab_focus (GET_WIDGET ("name_entry"));
 	gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (browser));
 	gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
 
diff --git a/extensions/catalogs/gth-catalog.c b/extensions/catalogs/gth-catalog.c
index f6cf36b..6cad6ff 100644
--- a/extensions/catalogs/gth-catalog.c
+++ b/extensions/catalogs/gth-catalog.c
@@ -53,6 +53,8 @@ gth_catalog_finalize (GObject *object)
 {
 	GthCatalog *catalog = GTH_CATALOG (object);
 
+	g_value_hash_unref (catalog->attributes);
+
 	if (catalog->priv != NULL) {
 		if (catalog->priv->file != NULL)
 			g_object_unref (catalog->priv->file);
@@ -80,7 +82,7 @@ base_create_root (GthCatalog  *catalog,
 
 
 static void
-base_read_from_doc (GthCatalog  *catalog,
+base_read_from_doc (GthCatalog *catalog,
 		    DomElement *root)
 {
 	GList      *file_list;
@@ -111,6 +113,9 @@ base_read_from_doc (GthCatalog  *catalog,
 	}
 	gth_catalog_set_file_list (catalog, file_list);
 
+	/* FIXME */
+	gth_hook_invoke ("gth-catalog-read-from-doc", catalog, root);
+
 	_g_object_list_unref (file_list);
 }
 
@@ -219,6 +224,9 @@ base_write_to_doc (GthCatalog  *catalog,
 			g_free (uri);
 		}
 	}
+
+	/* FIXME */
+	gth_hook_invoke ("gth-catalog-write-to-doc", catalog, doc, root);
 }
 
 
@@ -241,6 +249,7 @@ gth_catalog_class_init (GthCatalogClass *class)
 static void
 gth_catalog_init (GthCatalog *catalog)
 {
+	catalog->attributes = g_value_hash_new ();
 	catalog->priv = g_new0 (GthCatalogPrivate, 1);
 	catalog->priv->date_time = gth_datetime_new ();
 	catalog->priv->file_hash = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, NULL, NULL);
@@ -764,6 +773,9 @@ gth_catalog_update_metadata (GthCatalog  *catalog,
 				    file_data->info,
 				    catalog->priv->name,
 				    catalog->priv->date_time);
+
+	/* FIXME */
+	gth_hook_invoke ("gth-catalog-write-metadata", catalog, file_data);
 }
 
 
diff --git a/extensions/catalogs/gth-catalog.h b/extensions/catalogs/gth-catalog.h
index a6dc470..ec51bc1 100644
--- a/extensions/catalogs/gth-catalog.h
+++ b/extensions/catalogs/gth-catalog.h
@@ -48,6 +48,7 @@ typedef struct _GthCatalogClass    GthCatalogClass;
 struct _GthCatalog
 {
 	GObject __parent;
+	GValueHash *attributes;
 	GthCatalogPrivate *priv;
 };
 
diff --git a/extensions/catalogs/gth-file-source-catalogs.c b/extensions/catalogs/gth-file-source-catalogs.c
index 8acc65a..fe6e157 100644
--- a/extensions/catalogs/gth-file-source-catalogs.c
+++ b/extensions/catalogs/gth-file-source-catalogs.c
@@ -245,6 +245,9 @@ write_metadata_load_buffer_ready_cb (void     **buffer,
 				       g_file_info_get_attribute_string (metadata_op->file_data->info, "sort::type"),
 				       g_file_info_get_attribute_boolean (metadata_op->file_data->info, "sort::inverse"));
 
+	/* FIXME */
+	gth_hook_invoke ("gth-catalog-read-metadata", metadata_op->catalog, metadata_op->file_data);
+
 	catalog_buffer = gth_catalog_to_data (metadata_op->catalog, &catalog_size);
 	gio_file = gth_catalog_file_to_gio_file (metadata_op->file_data->file);
 	g_write_file_async (gio_file,
@@ -356,6 +359,7 @@ read_metadata_info_ready_cb (GList    *files,
 {
 	ReadMetadataOpData *read_metadata = user_data;
 	GthFileData        *result;
+	GFile              *gio_file;
 
 	if (error != NULL) {
 		read_metadata->callback (G_OBJECT (read_metadata->file_source), error, read_metadata->data);
@@ -365,28 +369,15 @@ read_metadata_info_ready_cb (GList    *files,
 
 	result = files->data;
 	g_file_info_copy_into (result->info, read_metadata->file_data->info);
-
 	update_file_info (read_metadata->file_source, read_metadata->file_data->file, read_metadata->file_data->info);
 
-	if (_g_file_attributes_matches_any (read_metadata->attributes,
-					    "sort::*,"
-					    "general::event-date,"
-					    "standard::display-name,standard::sort-order"))
-	{
-		GFile *gio_file;
-
-		gio_file = gth_catalog_file_to_gio_file (read_metadata->file_data->file);
-		gth_catalog_load_from_file_async (gio_file,
-						  gth_file_source_get_cancellable (read_metadata->file_source),
-						  read_metadata_catalog_ready_cb,
-						  read_metadata);
+	gio_file = gth_catalog_file_to_gio_file (read_metadata->file_data->file);
+	gth_catalog_load_from_file_async (gio_file,
+					  gth_file_source_get_cancellable (read_metadata->file_source),
+					  read_metadata_catalog_ready_cb,
+					  read_metadata);
 
-		g_object_unref (gio_file);
-	}
-	else {
-		read_metadata->callback (G_OBJECT (read_metadata->file_source), NULL, read_metadata->data);
-		read_metadata_free (read_metadata);
-	}
+	g_object_unref (gio_file);
 }
 
 
diff --git a/extensions/catalogs/main.c b/extensions/catalogs/main.c
index 6ad566e..ab12d52 100644
--- a/extensions/catalogs/main.c
+++ b/extensions/catalogs/main.c
@@ -32,15 +32,48 @@ G_MODULE_EXPORT void
 gthumb_extension_activate (void)
 {
 	/**
-	 * Called to create the catalog class from the given file data.
+	 * Called to create the catalog class from the given file content.
 	 *
-	 * @buffer (const char *): the file data.
+	 * @buffer (const char *): the file content.
 	 * @return (GthCatalog *): return a pointer to the object that can
 	 * handle the catalog data, or NULL if the data type doesn't match.
 	 **/
 	gth_hook_register ("gth-catalog-load-from-data", 1);
 
 	/**
+	 * Called to update the catalog data from a given file data.
+	 *
+	 * @catalog (GthCatalog *): the catalog to update
+	 * @file_data (GthFileData *): the file data
+	 **/
+	gth_hook_register ("gth-catalog-read-metadata", 2);
+
+	/**
+	 * Called to update the file metadata from a catalog.
+	 *
+	 * @catalog (GthCatalog *): the catalog
+	 * @file_data (GthFileData *): the file data to update
+	 **/
+	gth_hook_register ("gth-catalog-write-metadata", 2);
+
+	/**
+	 * Called to update the catalog from a xml file.
+	 *
+	 * @catalog (GthCatalog *):
+	 * @root (DomElement *):
+	 */
+	gth_hook_register ("gth-catalog-read-from-doc", 2);
+
+	/**
+	 * Called to update a xml file from a catalog.
+	 *
+	 * @catalog (GthCatalog *):
+	 * @doc (DomDocument *):
+	 * @root (DomElement *):
+	 */
+	gth_hook_register ("gth-catalog-write-to-doc", 3);
+
+	/**
 	 * Called to add sections to the catalog properties dialog.
 	 *
 	 * @builder   (GtkBuilder *): the builder relative to the window
diff --git a/extensions/search/callbacks.c b/extensions/search/callbacks.c
index 75c13c0..105eb85 100644
--- a/extensions/search/callbacks.c
+++ b/extensions/search/callbacks.c
@@ -169,8 +169,9 @@ search__dlg_catalog_properties (GtkBuilder  *builder,
 
 	vbox = gtk_vbox_new (FALSE, 6);
 	gtk_widget_show (vbox);
-	gtk_box_pack_start (GTK_BOX (_gtk_builder_get_widget (builder, "main_vbox")), vbox, FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (_gtk_builder_get_widget (builder, "general_vbox")), vbox, FALSE, FALSE, 0);
 
+	/* Translators: This is not a verb, it's a name as in "the search properties". */
 	label = gtk_label_new (_("Search"));
 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 	attrs = pango_attr_list_new ();
diff --git a/extensions/slideshow/Makefile.am b/extensions/slideshow/Makefile.am
index 447094b..e511d8d 100644
--- a/extensions/slideshow/Makefile.am
+++ b/extensions/slideshow/Makefile.am
@@ -12,15 +12,17 @@ libslideshow_la_SOURCES = 		\
 	callbacks.h			\
 	gth-slideshow.c			\
 	gth-slideshow.h			\
+	gth-slideshow-preferences.c	\
+	gth-slideshow-preferences.h	\
 	gth-transition.c		\
 	gth-transition.h		\
 	main.c				\
 	preferences.c			\
 	preferences.h
 
-libslideshow_la_CFLAGS = $(GTHUMB_CFLAGS) $(CLUTTER_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb 
+libslideshow_la_CFLAGS = $(GTHUMB_CFLAGS) $(CLUTTER_CFLAGS) $(GSTREAMER_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb 
 libslideshow_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
-libslideshow_la_LIBADD = $(GTHUMB_LIBS) $(CLUTTER_LIBS)
+libslideshow_la_LIBADD = $(GTHUMB_LIBS) $(CLUTTER_LIBS) $(GSTREAMER_LIBS) ../catalogs/libcatalogs.la
 libslideshow_la_DEPENDENCIES = $(top_builddir)/gthumb/gthumb$(EXEEXT)
 
 extensioninidir = $(extensiondir)
diff --git a/extensions/slideshow/actions.c b/extensions/slideshow/actions.c
index 7033396..8c76840 100644
--- a/extensions/slideshow/actions.c
+++ b/extensions/slideshow/actions.c
@@ -33,11 +33,12 @@ void
 gth_browser_activate_action_view_slideshow (GtkAction  *action,
 					    GthBrowser *browser)
 {
-	GList     *items;
-	GList     *file_list;
-	GtkWidget *slideshow;
-	char      *transition_id;
-	GList     *transitions = NULL;
+	GList       *items;
+	GList       *file_list;
+	GtkWidget   *slideshow;
+	GthFileData *location;
+	char        *transition_id;
+	GList       *transitions = NULL;
 
 	items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (browser)));
 	if ((items == NULL) || (items->next == NULL))
@@ -46,11 +47,26 @@ gth_browser_activate_action_view_slideshow (GtkAction  *action,
 		file_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (browser)), items);
 
 	slideshow = gth_slideshow_new (browser, file_list);
-	gth_slideshow_set_delay (GTH_SLIDESHOW (slideshow), eel_gconf_get_float (PREF_SLIDESHOW_CHANGE_DELAY, 5) * 1000);
-	gth_slideshow_set_automatic (GTH_SLIDESHOW (slideshow), eel_gconf_get_boolean (PREF_SLIDESHOW_AUTOMATIC, TRUE));
-	gth_slideshow_set_loop (GTH_SLIDESHOW (slideshow), eel_gconf_get_boolean (PREF_SLIDESHOW_WRAP_AROUND, FALSE));
 
-	transition_id = eel_gconf_get_string (PREF_SLIDESHOW_TRANSITION, DEFAULT_TRANSITION);
+	location = gth_browser_get_location_data (browser);
+	if (g_file_info_get_attribute_status (location->info, "slideshow::personalize") == G_FILE_ATTRIBUTE_STATUS_SET) {
+		gth_slideshow_set_delay (GTH_SLIDESHOW (slideshow), g_file_info_get_attribute_int32 (location->info, "slideshow::delay") / 10.0);
+		gth_slideshow_set_automatic (GTH_SLIDESHOW (slideshow), g_file_info_get_attribute_boolean (location->info, "slideshow::auto"));
+		gth_slideshow_set_loop (GTH_SLIDESHOW (slideshow), g_file_info_get_attribute_boolean (location->info, "slideshow::loop"));
+		transition_id = g_strdup (g_file_info_get_attribute_string (location->info, "slideshow::transition"));
+	}
+	else {
+		gth_slideshow_set_delay (GTH_SLIDESHOW (slideshow), eel_gconf_get_float (PREF_SLIDESHOW_CHANGE_DELAY, 5) * 1000);
+		gth_slideshow_set_automatic (GTH_SLIDESHOW (slideshow), eel_gconf_get_boolean (PREF_SLIDESHOW_AUTOMATIC, TRUE));
+		gth_slideshow_set_loop (GTH_SLIDESHOW (slideshow), eel_gconf_get_boolean (PREF_SLIDESHOW_WRAP_AROUND, FALSE));
+		transition_id = eel_gconf_get_string (PREF_SLIDESHOW_TRANSITION, DEFAULT_TRANSITION);
+	}
+
+	if (g_file_info_get_attribute_status (location->info, "slideshow::audio-files") == G_FILE_ATTRIBUTE_STATUS_SET)
+		gth_slideshow_set_playlist (GTH_SLIDESHOW (slideshow),
+					    g_file_info_get_attribute_stringv (location->info, "slideshow::audio-files"),
+					    g_file_info_get_attribute_boolean (location->info, "slideshow::audio-loop"));
+
 	if (strcmp (transition_id, "random") == 0) {
 		GList *scan;
 
@@ -75,8 +91,8 @@ gth_browser_activate_action_view_slideshow (GtkAction  *action,
 	}
 	gth_slideshow_set_transitions (GTH_SLIDESHOW (slideshow), transitions);
 
-	gtk_window_fullscreen (GTK_WINDOW (slideshow));
-	/*gtk_window_set_default_size (GTK_WINDOW (slideshow), 700, 700);*/
+	/*gtk_window_fullscreen (GTK_WINDOW (slideshow)); FIXME */
+	gtk_window_set_default_size (GTK_WINDOW (slideshow), 700, 700);
 	gtk_window_present (GTK_WINDOW (slideshow));
 
 	_g_object_list_unref (transitions);
diff --git a/extensions/slideshow/callbacks.c b/extensions/slideshow/callbacks.c
index f34e1dd..5f9ed8f 100644
--- a/extensions/slideshow/callbacks.c
+++ b/extensions/slideshow/callbacks.c
@@ -3,7 +3,7 @@
 /*
  *  GThumb
  *
- *  Copyright (C) 2009 Free Software Foundation, Inc.
+ *  Copyright (C) 2009-2010 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -26,6 +26,9 @@
 #include <glib-object.h>
 #include <gthumb.h>
 #include "actions.h"
+#include "callbacks.h"
+#include "gth-slideshow-preferences.h"
+#include "preferences.h"
 
 
 #define BROWSER_DATA_KEY "slideshow-browser-data"
@@ -131,3 +134,305 @@ ss__slideshow_cb (GthBrowser *browser)
 {
 	gth_browser_activate_action_view_slideshow (NULL, browser);
 }
+
+
+void
+ss__gth_catalog_read_metadata (GthCatalog  *catalog,
+			       GthFileData *file_data)
+{
+	if (g_file_info_get_attribute_status (file_data->info, "slideshow::personalize") == G_FILE_ATTRIBUTE_STATUS_SET)
+		g_value_hash_set_boolean (catalog->attributes,
+					  "slideshow::personalize",
+					  g_file_info_get_attribute_boolean (file_data->info, "slideshow::personalize"));
+	if (g_file_info_get_attribute_status (file_data->info, "slideshow::auto") == G_FILE_ATTRIBUTE_STATUS_SET)
+		g_value_hash_set_boolean (catalog->attributes,
+					  "slideshow::auto",
+					  g_file_info_get_attribute_boolean (file_data->info, "slideshow::auto"));
+	if (g_file_info_get_attribute_status (file_data->info, "slideshow::loop") == G_FILE_ATTRIBUTE_STATUS_SET)
+		g_value_hash_set_boolean (catalog->attributes,
+					  "slideshow::loop",
+					  g_file_info_get_attribute_boolean (file_data->info, "slideshow::loop"));
+	if (g_file_info_get_attribute_status (file_data->info, "slideshow::delay") == G_FILE_ATTRIBUTE_STATUS_SET)
+		g_value_hash_set_float (catalog->attributes,
+					"slideshow::delay",
+					g_file_info_get_attribute_int32 (file_data->info, "slideshow::delay") / 10.0);
+	if (g_file_info_get_attribute_status (file_data->info, "slideshow::transition") == G_FILE_ATTRIBUTE_STATUS_SET)
+		g_value_hash_set_string (catalog->attributes,
+					 "slideshow::transition",
+				 	 g_file_info_get_attribute_string (file_data->info, "slideshow::transition"));
+	if (g_file_info_get_attribute_status (file_data->info, "slideshow::audio-loop") == G_FILE_ATTRIBUTE_STATUS_SET)
+		g_value_hash_set_boolean (catalog->attributes,
+					  "slideshow::audio-loop",
+					  g_file_info_get_attribute_boolean (file_data->info, "slideshow::audio-loop"));
+	if (g_file_info_get_attribute_status (file_data->info, "slideshow::audio-files") == G_FILE_ATTRIBUTE_STATUS_SET)
+		g_value_hash_set_stringv (catalog->attributes,
+					  "slideshow::audio-files",
+					  g_file_info_get_attribute_stringv (file_data->info, "slideshow::audio-files"));
+}
+
+
+void
+ss__gth_catalog_write_metadata (GthCatalog  *catalog,
+			        GthFileData *file_data)
+{
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::personalize")) {
+		g_file_info_set_attribute_boolean (file_data->info,
+						   "slideshow::personalize",
+						   g_value_hash_get_boolean (catalog->attributes, "slideshow::personalize"));
+		g_file_info_set_attribute_status (file_data->info,
+						  "slideshow::personalize",
+						  G_FILE_ATTRIBUTE_STATUS_SET);
+	}
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::auto")) {
+		g_file_info_set_attribute_boolean (file_data->info,
+						   "slideshow::auto",
+						   g_value_hash_get_boolean (catalog->attributes, "slideshow::auto"));
+		g_file_info_set_attribute_status (file_data->info,
+						  "slideshow::auto",
+						  G_FILE_ATTRIBUTE_STATUS_SET);
+	}
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::loop")) {
+		g_file_info_set_attribute_boolean (file_data->info,
+						   "slideshow::loop",
+						   g_value_hash_get_boolean (catalog->attributes, "slideshow::loop"));
+		g_file_info_set_attribute_status (file_data->info,
+						  "slideshow::loop",
+						  G_FILE_ATTRIBUTE_STATUS_SET);
+	}
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::delay")) {
+		g_file_info_set_attribute_int32 (file_data->info,
+						 "slideshow::delay",
+						 (int) g_value_hash_get_float (catalog->attributes, "slideshow::delay") * 10.0);
+		g_file_info_set_attribute_status (file_data->info,
+						  "slideshow::delay",
+						  G_FILE_ATTRIBUTE_STATUS_SET);
+	}
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::transition")) {
+		g_file_info_set_attribute_string (file_data->info,
+						  "slideshow::transition",
+						  g_value_hash_get_string (catalog->attributes, "slideshow::transition"));
+		g_file_info_set_attribute_status (file_data->info,
+						  "slideshow::transition",
+						  G_FILE_ATTRIBUTE_STATUS_SET);
+	}
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::audio-loop")) {
+		g_file_info_set_attribute_boolean (file_data->info,
+						   "slideshow::audio-loop",
+						   g_value_hash_get_boolean (catalog->attributes, "slideshow::audio-loop"));
+		g_file_info_set_attribute_status (file_data->info,
+						  "slideshow::audio-loop",
+						  G_FILE_ATTRIBUTE_STATUS_SET);
+	}
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::audio-files")) {
+		g_file_info_set_attribute_stringv (file_data->info,
+						   "slideshow::audio-files",
+						   g_value_hash_get_stringv (catalog->attributes, "slideshow::audio-files"));
+		g_file_info_set_attribute_status (file_data->info,
+						  "slideshow::audio-files",
+						  G_FILE_ATTRIBUTE_STATUS_SET);
+	}
+}
+
+
+void
+ss__gth_catalog_read_from_doc (GthCatalog *catalog,
+			       DomElement *root)
+{
+	DomElement *node;
+
+	for (node = root->first_child; node; node = node->next_sibling) {
+		DomElement *child;
+
+		if (g_strcmp0 (node->tag_name, "slideshow") != 0)
+			continue;
+
+		g_value_hash_set_boolean (catalog->attributes,
+					  "slideshow::personalize",
+					  g_strcmp0 (dom_element_get_attribute (node, "personalize"), "true") == 0);
+		g_value_hash_set_boolean (catalog->attributes,
+					  "slideshow::auto",
+					  g_strcmp0 (dom_element_get_attribute (node, "auto"), "true") == 0);
+		g_value_hash_set_boolean (catalog->attributes,
+					  "slideshow::loop",
+					  g_strcmp0 (dom_element_get_attribute (node, "loop"), "true") == 0);
+
+		for (child = node->first_child; child; child = child->next_sibling) {
+			if (g_strcmp0 (child->tag_name, "delay") == 0) {
+				float delay;
+
+				sscanf (dom_element_get_inner_text (child), "%f", &delay);
+				g_value_hash_set_float (catalog->attributes,
+							"slideshow::delay",
+							delay);
+			}
+			else if (g_strcmp0 (child->tag_name, "transition") == 0) {
+				g_value_hash_set_string (catalog->attributes,
+							 "slideshow::transition",
+							 dom_element_get_inner_text (child));
+			}
+			else if (g_strcmp0 (child->tag_name, "audio") == 0) {
+				DomElement  *file;
+				GList       *audio_files;
+
+				g_value_hash_set_boolean (catalog->attributes,
+							  "slideshow::audio-loop",
+							  g_strcmp0 (dom_element_get_attribute (child, "loop"), "true") == 0);
+
+				audio_files = NULL;
+				for (file = child->first_child; file; file = file->next_sibling) {
+					if (g_strcmp0 (file->tag_name, "file") == 0)
+						audio_files = g_list_prepend (audio_files, g_strdup (dom_element_get_attribute (file, "uri")));
+				}
+				audio_files = g_list_reverse (audio_files);
+				if (audio_files != NULL) {
+					char **audio_files_v;
+
+					audio_files_v = _g_string_list_to_strv (audio_files);
+					g_value_hash_set_stringv (catalog->attributes,
+								  "slideshow::audio-files",
+								  audio_files_v);
+
+					g_strfreev (audio_files_v);
+				}
+				else
+					g_value_hash_unset (catalog->attributes, "slideshow::audio-files");
+
+				_g_string_list_free (audio_files);
+			}
+		}
+	}
+}
+
+
+void
+ss__gth_catalog_write_to_doc (GthCatalog  *catalog,
+			      DomDocument *doc,
+			      DomElement  *root)
+{
+	DomElement  *slideshow;
+	char        *delay;
+	DomElement  *audio;
+	char       **audio_files;
+	int          i;
+
+	slideshow = dom_document_create_element (doc,
+						 "slideshow",
+						 "personalize", (g_value_hash_get_boolean (catalog->attributes, "slideshow::personalize") ? "true" : "false"),
+						 "auto", (g_value_hash_get_boolean (catalog->attributes, "slideshow::auto") ? "true" : "false"),
+						 "loop", (g_value_hash_get_boolean (catalog->attributes, "slideshow::loop") ? "true" : "false"),
+						 NULL);
+	dom_element_append_child (root, slideshow);
+
+	delay = g_strdup_printf ("%f", g_value_hash_get_float (catalog->attributes, "slideshow::delay"));
+	dom_element_append_child (slideshow,
+				  dom_document_create_element_with_text (doc, delay, "delay", NULL));
+	g_free (delay);
+
+	dom_element_append_child (slideshow,
+				  dom_document_create_element_with_text (doc,
+									 g_value_hash_get_string (catalog->attributes, "slideshow::transition"),
+									 "transition",
+									 NULL));
+
+	audio = dom_document_create_element (doc,
+					     "audio",
+					     "loop", (g_value_hash_get_boolean (catalog->attributes, "slideshow::audio-loop") ? "true" : "false"),
+					     NULL);
+	dom_element_append_child (slideshow, audio);
+
+	audio_files = g_value_hash_get_stringv (catalog->attributes, "slideshow::audio-files");
+	for (i = 0; audio_files[i] != NULL; i++)
+		dom_element_append_child (audio, dom_document_create_element (doc, "file", "uri", audio_files[i], NULL));
+}
+
+
+void
+ss__dlg_catalog_properties (GtkBuilder  *builder,
+			    GthFileData *file_data,
+			    GthCatalog  *catalog)
+{
+	GtkWidget *slideshow_preferences;
+	GtkWidget *label;
+
+	if (! g_value_hash_is_set (catalog->attributes, "slideshow::personalize")
+	    || ! g_value_hash_get_boolean (catalog->attributes, "slideshow::personalize"))
+	{
+		char *current_transition;
+
+		current_transition = eel_gconf_get_string (PREF_SLIDESHOW_TRANSITION, DEFAULT_TRANSITION);
+		slideshow_preferences = gth_slideshow_preferences_new (current_transition,
+								       eel_gconf_get_boolean (PREF_SLIDESHOW_AUTOMATIC, TRUE),
+								       eel_gconf_get_float (PREF_SLIDESHOW_CHANGE_DELAY, 5.0),
+								       eel_gconf_get_boolean (PREF_SLIDESHOW_WRAP_AROUND, FALSE));
+		gtk_widget_set_sensitive (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences), "personalize_box"), FALSE);
+
+		g_free (current_transition);
+	}
+	else {
+		slideshow_preferences = gth_slideshow_preferences_new (g_value_hash_get_string (catalog->attributes, "slideshow::transition"),
+								       g_value_hash_get_boolean (catalog->attributes, "slideshow::auto"),
+								       g_value_hash_get_float (catalog->attributes, "slideshow::delay"),
+								       g_value_hash_get_boolean (catalog->attributes, "slideshow::loop"));
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences), "personalize_checkbutton")), TRUE);
+		gtk_widget_set_sensitive (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences), "personalize_box"), TRUE);
+	}
+
+	if (g_value_hash_is_set (catalog->attributes, "slideshow::audio-files"))
+		gth_slideshow_preferences_set_audio (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences),
+						     g_value_hash_get_boolean (catalog->attributes, "slideshow::audio-loop"),
+						     g_value_hash_get_stringv (catalog->attributes, "slideshow::audio-files"));
+
+	gtk_widget_show (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences), "personalize_checkbutton"));
+	gtk_widget_hide (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences), "slideshow_label"));
+	gtk_widget_show (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences), "playlist_box"));
+	gtk_widget_show (slideshow_preferences);
+
+	label = gtk_label_new (_("Slideshow"));
+	gtk_widget_show (label);
+
+	gtk_notebook_append_page (GTK_NOTEBOOK (_gtk_builder_get_widget (builder, "properties_notebook")), slideshow_preferences, label);
+	g_object_set_data (G_OBJECT (builder), "slideshow_preferences", slideshow_preferences);
+}
+
+
+void
+ss__dlg_catalog_properties_save (GtkBuilder  *builder,
+				 GthFileData *file_data,
+				 GthCatalog  *catalog)
+{
+	GtkWidget  *slideshow_preferences;
+	char       *transition_id;
+	char      **files;
+
+	slideshow_preferences = g_object_get_data (G_OBJECT (builder), "slideshow_preferences");
+
+	g_value_hash_set_boolean (catalog->attributes,
+				  "slideshow::personalize",
+				  gth_slideshow_preferences_get_personalize (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences)));
+
+	transition_id = gth_slideshow_preferences_get_transition_id (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences));
+	g_value_hash_set_string (catalog->attributes,
+				 "slideshow::transition",
+				 transition_id);
+	g_free (transition_id);
+
+	g_value_hash_set_boolean (catalog->attributes,
+				  "slideshow::auto",
+				  gth_slideshow_preferences_get_automatic (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences)));
+	g_value_hash_set_float (catalog->attributes,
+				"slideshow::delay",
+				gth_slideshow_preferences_get_delay (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences)));
+	g_value_hash_set_boolean (catalog->attributes,
+				  "slideshow::loop",
+				  gth_slideshow_preferences_get_wrap_around (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences)));
+
+	files = gth_slideshow_preferences_get_audio_files (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences));
+	g_value_hash_set_stringv (catalog->attributes,
+				  "slideshow::audio-files",
+				  files);
+	g_strfreev (files);
+
+	g_value_hash_set_boolean (catalog->attributes,
+				  "slideshow::audio-loop",
+				  gth_slideshow_preferences_get_audio_loop (GTH_SLIDESHOW_PREFERENCES (slideshow_preferences)));
+}
diff --git a/extensions/slideshow/callbacks.h b/extensions/slideshow/callbacks.h
index f1e4cf7..75c10a2 100644
--- a/extensions/slideshow/callbacks.h
+++ b/extensions/slideshow/callbacks.h
@@ -24,9 +24,25 @@
 #define CALLBACKS_H
 
 #include <gthumb.h>
+#include <extensions/catalogs/gth-catalog.h>
 
-void ss__gth_browser_construct_cb          (GthBrowser *browser);
-void ss__gth_browser_update_sensitivity_cb (GthBrowser *browser);
-void ss__slideshow_cb                      (GthBrowser *browser);
+void ss__gth_browser_construct_cb          (GthBrowser  *browser);
+void ss__gth_browser_update_sensitivity_cb (GthBrowser  *browser);
+void ss__slideshow_cb                      (GthBrowser  *browser);
+void ss__gth_catalog_read_metadata         (GthCatalog  *catalog,
+					    GthFileData *file_data);
+void ss__gth_catalog_write_metadata        (GthCatalog  *catalog,
+					    GthFileData *file_data);
+void ss__gth_catalog_read_from_doc         (GthCatalog  *catalog,
+					    DomElement  *root);
+void ss__gth_catalog_write_to_doc          (GthCatalog  *catalog,
+					    DomDocument *doc,
+					    DomElement  *root);
+void ss__dlg_catalog_properties            (GtkBuilder  *builder,
+					    GthFileData *file_data,
+					    GthCatalog  *catalog);
+void ss__dlg_catalog_properties_save       (GtkBuilder  *builder,
+					    GthFileData *file_data,
+					    GthCatalog  *catalog);
 
 #endif /* CALLBACKS_H */
diff --git a/extensions/slideshow/data/ui/slideshow-preferences.ui b/extensions/slideshow/data/ui/slideshow-preferences.ui
index ebae268..073517d 100644
--- a/extensions/slideshow/data/ui/slideshow-preferences.ui
+++ b/extensions/slideshow/data/ui/slideshow-preferences.ui
@@ -3,53 +3,205 @@
   <requires lib="gtk+" version="2.16"/>
   <!-- interface-naming-policy project-wide -->
   <object class="GtkAdjustment" id="delay_adjustment">
-    <property name="value">5</property>
     <property name="lower">0.10000000000000001</property>
     <property name="upper">100</property>
+    <property name="value">5</property>
     <property name="step_increment">0.10000000000000001</property>
   </object>
+  <object class="GtkListStore" id="files_liststore">
+    <columns>
+      <!-- column-name icon -->
+      <column type="GdkPixbuf"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name uri -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
   <object class="GtkVBox" id="preferences_page">
     <property name="visible">True</property>
     <property name="border_width">12</property>
     <property name="orientation">vertical</property>
-    <property name="spacing">6</property>
+    <property name="spacing">12</property>
     <child>
-      <object class="GtkHBox" id="transition_box">
+      <object class="GtkVBox" id="vbox1">
         <property name="visible">True</property>
+        <property name="orientation">vertical</property>
         <property name="spacing">6</property>
         <child>
-          <object class="GtkLabel" id="transition_label">
+          <object class="GtkVBox" id="vbox2">
             <property name="visible">True</property>
-            <property name="label" translatable="yes">_Transition effect:</property>
-            <property name="use_underline">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkCheckButton" id="personalize_checkbutton">
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="draw_indicator">True</property>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_Personalize</property>
+                    <property name="use_underline">True</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="slideshow_label">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Slideshow</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
           </object>
           <packing>
-            <property name="expand">False</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
-          <placeholder/>
+          <object class="GtkAlignment" id="alignment2">
+            <property name="visible">True</property>
+            <property name="left_padding">12</property>
+            <child>
+              <object class="GtkVBox" id="personalize_box">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkHBox" id="transition_box">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="transition_label">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">_Transition effect:</property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkCheckButton" id="automatic_checkbutton">
+                        <property name="label" translatable="yes" comments="This is the first part of the phrase &quot;change automatically, every x seconds&quot;, where x is an input control that let the user choose a value.">_Change automatically, every</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="delay_options_box">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkSpinButton" id="change_delay_spinbutton">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                            <property name="adjustment">delay_adjustment</property>
+                            <property name="digits">1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label2">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">seconds</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="wrap_around_checkbutton">
+                    <property name="label" translatable="yes">_Restart when finished</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
         </child>
       </object>
       <packing>
         <property name="expand">False</property>
-        <property name="fill">False</property>
         <property name="position">0</property>
       </packing>
     </child>
     <child>
-      <object class="GtkHBox" id="hbox1">
-        <property name="visible">True</property>
+      <object class="GtkVBox" id="playlist_box">
+        <property name="orientation">vertical</property>
         <property name="spacing">6</property>
         <child>
-          <object class="GtkCheckButton" id="automatic_checkbutton">
-            <property name="label" translatable="yes" comments="This is the first part of the phrase &quot;change automatically, every x seconds&quot;, where x is an input control that let the user choose a value.">_Change automatically, every</property>
+          <object class="GtkLabel" id="label1">
             <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">False</property>
-            <property name="use_underline">True</property>
-            <property name="draw_indicator">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Playlist</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -57,49 +209,131 @@
           </packing>
         </child>
         <child>
-          <object class="GtkSpinButton" id="change_delay_spinbutton">
+          <object class="GtkAlignment" id="alignment1">
             <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="invisible_char">&#x25CF;</property>
-            <property name="adjustment">delay_adjustment</property>
-            <property name="digits">1</property>
+            <property name="left_padding">12</property>
+            <child>
+              <object class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTreeView" id="files_treeview">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="model">files_liststore</property>
+                            <property name="headers_visible">False</property>
+                            <property name="reorderable">True</property>
+                            <property name="search_column">1</property>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                                <child>
+                                  <object class="GtkCellRendererPixbuf" id="cellrendererpixbuf1"/>
+                                  <attributes>
+                                    <attribute name="pixbuf">0</attribute>
+                                  </attributes>
+                                </child>
+                                <child>
+                                  <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                                  <attributes>
+                                    <attribute name="text">1</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVBox" id="vbox4">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkButton" id="add_file_button">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <child>
+                              <object class="GtkImage" id="image1">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-add</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="remove_file_button">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <child>
+                              <object class="GtkImage" id="image2">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-remove</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="loop_checkbutton">
+                    <property name="label" translatable="yes">Play c_ontinuously</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
           </object>
           <packing>
-            <property name="expand">False</property>
             <property name="position">1</property>
           </packing>
         </child>
-        <child>
-          <object class="GtkLabel" id="label2">
-            <property name="visible">True</property>
-            <property name="label" translatable="yes">seconds</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="position">2</property>
-          </packing>
-        </child>
       </object>
       <packing>
-        <property name="expand">False</property>
-        <property name="fill">False</property>
         <property name="position">1</property>
       </packing>
     </child>
-    <child>
-      <object class="GtkCheckButton" id="wrap_around_checkbutton">
-        <property name="label" translatable="yes">_Restart when finished</property>
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="receives_default">False</property>
-        <property name="use_underline">True</property>
-        <property name="draw_indicator">True</property>
-      </object>
-      <packing>
-        <property name="expand">False</property>
-        <property name="fill">False</property>
-        <property name="position">2</property>
-      </packing>
-    </child>
   </object>
 </interface>
diff --git a/extensions/slideshow/gth-slideshow-preferences.c b/extensions/slideshow/gth-slideshow-preferences.c
new file mode 100644
index 0000000..e41b4f0
--- /dev/null
+++ b/extensions/slideshow/gth-slideshow-preferences.c
@@ -0,0 +1,461 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <gthumb.h>
+#include "gth-slideshow-preferences.h"
+#include "gth-transition.h"
+
+
+enum {
+	TRANSITION_COLUMN_ID,
+	TRANSITION_COLUMN_DISPLAY_NAME
+};
+
+
+enum {
+	FILE_COLUMN_ICON,
+	FILE_COLUMN_NAME,
+	FILE_COLUMN_URI
+};
+
+
+struct _GthSlideshowPreferencesPrivate {
+	GtkBuilder *builder;
+	GtkWidget  *transition_combobox;
+};
+
+
+static gpointer parent_class = NULL;
+
+
+static void
+gth_slideshow_preferences_init (GthSlideshowPreferences *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_SLIDESHOW_PREFERENCES, GthSlideshowPreferencesPrivate);
+	self->priv->builder = NULL;
+}
+
+
+static void
+gth_slideshow_preferences_finalize (GObject *object)
+{
+	GthSlideshowPreferences *self = GTH_SLIDESHOW_PREFERENCES (object);
+
+	g_object_unref (self->priv->builder);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_slideshow_preferences_class_init (GthSlideshowPreferencesClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthSlideshowPreferencesPrivate));
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = gth_slideshow_preferences_finalize;
+}
+
+
+GType
+gth_slideshow_preferences_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo type_info = {
+			sizeof (GthSlideshowPreferencesClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_slideshow_preferences_class_init,
+			NULL,
+			NULL,
+			sizeof (GthSlideshowPreferences),
+			0,
+			(GInstanceInitFunc) gth_slideshow_preferences_init
+		};
+
+		type = g_type_register_static (GTK_TYPE_VBOX,
+					       "GthSlideshowPreferences",
+					       &type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+
+static void
+personalize_checkbutton_toggled_cb (GtkToggleButton *button,
+				    gpointer         user_data)
+{
+	GthSlideshowPreferences *self = user_data;
+
+	gtk_widget_set_sensitive (_gtk_builder_get_widget (self->priv->builder, "personalize_box"),
+				  gtk_toggle_button_get_active (button));
+}
+
+
+static void
+automatic_checkbutton_toggled_cb (GtkToggleButton *button,
+				  gpointer         user_data)
+{
+	GthSlideshowPreferences *self = user_data;
+
+	gtk_widget_set_sensitive (_gtk_builder_get_widget (self->priv->builder, "delay_options_box"),
+				  gtk_toggle_button_get_active (button));
+}
+
+
+static void
+remove_file_button_clicked_cb (GtkButton *button,
+			       gpointer   user_data)
+{
+	GthSlideshowPreferences *self = user_data;
+	GtkTreeModel            *model;
+	GtkTreeIter              iter;
+
+	if (! gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (_gtk_builder_get_widget (self->priv->builder, "files_treeview"))), &model, &iter))
+		return;
+
+	gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+}
+
+
+static void
+file_chooser_dialog_response_cb (GtkDialog *dialog,
+				 int        response,
+				 gpointer   user_data)
+{
+	GthSlideshowPreferences *self = user_data;
+
+	switch (response) {
+	case GTK_RESPONSE_DELETE_EVENT:
+	case GTK_RESPONSE_CANCEL:
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		break;
+
+	case GTK_RESPONSE_OK:
+		{
+			GSList       *files;
+			GthIconCache *icon_cache;
+			GtkListStore *list_store;
+			GSList       *scan;
+
+			files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (dialog));
+			icon_cache = gth_icon_cache_new_for_widget(GTK_WIDGET (self), GTK_ICON_SIZE_MENU);
+			list_store = (GtkListStore *) gtk_builder_get_object (self->priv->builder, "files_liststore");
+			gtk_list_store_clear (list_store);
+			for (scan = files; scan; scan = scan->next) {
+				GFile       *file = scan->data;
+				GIcon       *icon;
+				GdkPixbuf   *pixbuf;
+				char        *uri;
+				char        *name;
+				GtkTreeIter  iter;
+
+				icon = g_content_type_get_icon ("audio");
+				pixbuf = gth_icon_cache_get_pixbuf (icon_cache, icon);
+				uri = g_file_get_uri (file);
+				name = _g_file_get_display_name (file);
+
+				gtk_list_store_append (list_store, &iter);
+				gtk_list_store_set (list_store, &iter,
+						    FILE_COLUMN_ICON, pixbuf,
+						    FILE_COLUMN_NAME, name,
+						    FILE_COLUMN_URI, uri,
+						    -1);
+
+				g_free (name);
+				g_free (uri);
+				g_object_unref (pixbuf);
+			}
+
+			gth_icon_cache_free (icon_cache);
+			g_slist_foreach (files, (GFunc) g_object_unref, NULL);
+			g_slist_free (files);
+		}
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		break;
+	}
+}
+
+
+static void
+add_file_button_clicked_cb (GtkButton *button,
+			    gpointer   user_data)
+{
+	GthSlideshowPreferences *self = user_data;
+	GtkWidget               *dialog;
+
+	dialog = gtk_file_chooser_dialog_new (_("Choose the files to play"),
+					      GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+					      GTK_FILE_CHOOSER_ACTION_OPEN,
+					      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+					      GTK_STOCK_OK, GTK_RESPONSE_OK,
+					      NULL);
+	gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
+	g_signal_connect (dialog,
+			  "response",
+			  G_CALLBACK (file_chooser_dialog_response_cb),
+			  self);
+	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+	gtk_widget_show (dialog);
+}
+
+
+static void
+gth_slideshow_preferences_construct (GthSlideshowPreferences *self,
+				     const char              *current_transition,
+				     gboolean                 automatic,
+				     float                    delay,
+				     gboolean                 wrap_around)
+{
+	GtkListStore    *model;
+	GtkCellRenderer *renderer;
+	GList           *transitions;
+	int              i, i_active;
+	GList           *scan;
+	GtkTreeIter      iter;
+
+	self->priv->builder = _gtk_builder_new_from_file ("slideshow-preferences.ui", "slideshow");
+	gtk_container_add (GTK_CONTAINER (self), _gtk_builder_get_widget (self->priv->builder, "preferences_page"));
+
+	model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+	self->priv->transition_combobox = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model));
+	g_object_unref (model);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->priv->transition_combobox),
+				    renderer,
+				    TRUE);
+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self->priv->transition_combobox),
+					renderer,
+					"text", TRANSITION_COLUMN_DISPLAY_NAME,
+					NULL);
+
+	transitions = gth_main_get_registered_objects (GTH_TYPE_TRANSITION);
+	for (i = 0, i_active = 0, scan = transitions; scan; scan = scan->next, i++) {
+		GthTransition *transition = scan->data;
+
+		if (g_strcmp0 (gth_transition_get_id (transition), current_transition) == 0)
+			i_active = i;
+
+		gtk_list_store_append (model, &iter);
+		gtk_list_store_set (model, &iter,
+				    TRANSITION_COLUMN_ID, gth_transition_get_id (transition),
+				    TRANSITION_COLUMN_DISPLAY_NAME, gth_transition_get_display_name (transition),
+				    -1);
+	}
+
+	if (g_strcmp0 ("random", current_transition) == 0)
+		i_active = i;
+	gtk_list_store_append (model, &iter);
+	gtk_list_store_set (model, &iter,
+			    TRANSITION_COLUMN_ID, "random",
+			    TRANSITION_COLUMN_DISPLAY_NAME, _("Random"),
+			    -1);
+
+	gtk_combo_box_set_active (GTK_COMBO_BOX (self->priv->transition_combobox), i_active);
+	gtk_widget_show (self->priv->transition_combobox);
+	gtk_container_add (GTK_CONTAINER (_gtk_builder_get_widget (self->priv->builder, "transition_box")), self->priv->transition_combobox);
+
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "automatic_checkbutton")), automatic);
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (_gtk_builder_get_widget (self->priv->builder, "change_delay_spinbutton")), delay);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "wrap_around_checkbutton")), wrap_around);
+
+	/* signals */
+
+	g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "personalize_checkbutton"),
+			  "toggled",
+			  G_CALLBACK (personalize_checkbutton_toggled_cb),
+			  self);
+	g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "automatic_checkbutton"),
+			  "toggled",
+			  G_CALLBACK (automatic_checkbutton_toggled_cb),
+			  self);
+	g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "remove_file_button"),
+			  "clicked",
+			  G_CALLBACK (remove_file_button_clicked_cb),
+			  self);
+	g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "add_file_button"),
+			  "clicked",
+			  G_CALLBACK (add_file_button_clicked_cb),
+			  self);
+}
+
+
+GtkWidget *
+gth_slideshow_preferences_new (const char *transition,
+			       gboolean    automatic,
+			       float       delay,
+			       gboolean    wrap_around)
+{
+	GtkWidget *widget;
+
+	widget = g_object_new (GTH_TYPE_SLIDESHOW_PREFERENCES, NULL);
+	gth_slideshow_preferences_construct (GTH_SLIDESHOW_PREFERENCES (widget),
+					     transition,
+					     automatic,
+					     delay,
+					     wrap_around);
+
+	return widget;
+}
+
+
+void
+gth_slideshow_preferences_set_audio (GthSlideshowPreferences  *self,
+				     gboolean                  loop,
+				     char                    **files)
+{
+	GthIconCache *icon_cache;
+	GtkListStore *list_store;
+	int           i;
+
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "loop_checkbutton")), loop);
+
+	icon_cache = gth_icon_cache_new_for_widget(GTK_WIDGET (self), GTK_ICON_SIZE_MENU);
+	list_store = (GtkListStore *) gtk_builder_get_object (self->priv->builder, "files_liststore");
+	gtk_list_store_clear (list_store);
+	for (i = 0; files[i] != NULL; i++) {
+		GIcon       *icon;
+		GdkPixbuf   *pixbuf;
+		GFile       *file;
+		char        *name;
+		GtkTreeIter  iter;
+
+		icon = g_content_type_get_icon ("audio");
+		pixbuf = gth_icon_cache_get_pixbuf (icon_cache, icon);
+		file = g_file_new_for_uri (files[i]);
+		name = _g_file_get_display_name (file);
+
+		gtk_list_store_append (list_store, &iter);
+		gtk_list_store_set (list_store, &iter,
+				    FILE_COLUMN_ICON, pixbuf,
+				    FILE_COLUMN_NAME, name,
+				    FILE_COLUMN_URI, files[i],
+				    -1);
+
+		g_free (name);
+		g_object_unref (file);
+		g_object_unref (pixbuf);
+	}
+
+	gth_icon_cache_free (icon_cache);
+}
+
+
+GtkWidget *
+gth_slideshow_preferences_get_widget (GthSlideshowPreferences *self,
+				      const char              *name)
+{
+	if (strcmp (name, "transition_combobox") == 0)
+		return self->priv->transition_combobox;
+	else
+		return _gtk_builder_get_widget (self->priv->builder, name);
+}
+
+
+gboolean
+gth_slideshow_preferences_get_personalize (GthSlideshowPreferences *self)
+{
+	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (self->priv->builder, "personalize_checkbutton")));
+}
+
+
+char *
+gth_slideshow_preferences_get_transition_id (GthSlideshowPreferences *self)
+{
+	GtkTreeIter   iter;
+	GtkTreeModel *tree_model;
+	char         *transition_id;
+
+	if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self->priv->transition_combobox), &iter))
+		return NULL;
+
+	tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (self->priv->transition_combobox));
+	gtk_tree_model_get (tree_model, &iter, TRANSITION_COLUMN_ID, &transition_id, -1);
+
+	return transition_id;
+}
+
+gboolean
+gth_slideshow_preferences_get_automatic (GthSlideshowPreferences *self)
+{
+	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (self->priv->builder, "automatic_checkbutton")));
+}
+
+
+float
+gth_slideshow_preferences_get_delay (GthSlideshowPreferences *self)
+{
+	return (float) gtk_spin_button_get_value (GTK_SPIN_BUTTON (gtk_builder_get_object (self->priv->builder, "change_delay_spinbutton")));
+}
+
+
+gboolean
+gth_slideshow_preferences_get_wrap_around (GthSlideshowPreferences *self)
+{
+	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (self->priv->builder, "wrap_around_checkbutton")));
+}
+
+
+char **
+gth_slideshow_preferences_get_audio_files (GthSlideshowPreferences *self)
+{
+	GtkTreeModel  *tree_model;
+	GtkTreeIter    iter;
+	char         **files_v;
+	GList         *files = NULL;
+
+	tree_model = (GtkTreeModel *) gtk_builder_get_object (self->priv->builder, "files_liststore");
+	if (gtk_tree_model_get_iter_first (tree_model, &iter)) {
+		do {
+			char *uri;
+
+			gtk_tree_model_get (tree_model, &iter,
+					    FILE_COLUMN_URI, &uri,
+					    -1);
+			files = g_list_prepend (files, uri);
+		}
+		while (gtk_tree_model_iter_next (tree_model, &iter));
+	}
+	files = g_list_reverse (files);
+	files_v = _g_string_list_to_strv (files);
+
+	_g_string_list_free (files);
+
+	return files_v;
+}
+
+
+gboolean
+gth_slideshow_preferences_get_audio_loop (GthSlideshowPreferences *self)
+{
+	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (self->priv->builder, "loop_checkbutton")));
+}
diff --git a/extensions/slideshow/gth-slideshow-preferences.h b/extensions/slideshow/gth-slideshow-preferences.h
new file mode 100644
index 0000000..b0b0c57
--- /dev/null
+++ b/extensions/slideshow/gth-slideshow-preferences.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_SLIDESHOW_PREFERENCES_H
+#define GTH_SLIDESHOW_PREFERENCES_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_SLIDESHOW_PREFERENCES              (gth_slideshow_preferences_get_type ())
+#define GTH_SLIDESHOW_PREFERENCES(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_SLIDESHOW_PREFERENCES, GthSlideshowPreferences))
+#define GTH_SLIDESHOW_PREFERENCES_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_SLIDESHOW_PREFERENCES_TYPE, GthSlideshowPreferencesClass))
+#define GTH_IS_SLIDESHOW_PREFERENCES(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_SLIDESHOW_PREFERENCES))
+#define GTH_IS_SLIDESHOW_PREFERENCES_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_SLIDESHOW_PREFERENCES))
+#define GTH_SLIDESHOW_PREFERENCES_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_SLIDESHOW_PREFERENCES, GthSlideshowPreferencesClass))
+
+typedef struct _GthSlideshowPreferences         GthSlideshowPreferences;
+typedef struct _GthSlideshowPreferencesClass    GthSlideshowPreferencesClass;
+typedef struct _GthSlideshowPreferencesPrivate  GthSlideshowPreferencesPrivate;
+
+struct _GthSlideshowPreferences
+{
+	GtkVBox __parent;
+	GthSlideshowPreferencesPrivate *priv;
+};
+
+struct _GthSlideshowPreferencesClass
+{
+	GtkVBoxClass __parent_class;
+};
+
+GType         gth_slideshow_preferences_get_type           (void);
+GtkWidget *   gth_slideshow_preferences_new                (const char               *transition,
+						            gboolean                  automatic,
+						            float                     delay,
+						            gboolean                  wrap_around);
+void          gth_slideshow_preferences_set_audio          (GthSlideshowPreferences  *self,
+							    gboolean                  loop,
+							    char                    **files);
+GtkWidget *   gth_slideshow_preferences_get_widget         (GthSlideshowPreferences  *self,
+						            const char               *name);
+gboolean      gth_slideshow_preferences_get_personalize    (GthSlideshowPreferences  *self);
+char *        gth_slideshow_preferences_get_transition_id  (GthSlideshowPreferences  *self);
+gboolean      gth_slideshow_preferences_get_automatic      (GthSlideshowPreferences  *self);
+float         gth_slideshow_preferences_get_delay          (GthSlideshowPreferences  *self);
+gboolean      gth_slideshow_preferences_get_wrap_around    (GthSlideshowPreferences  *self);
+char **       gth_slideshow_preferences_get_audio_files    (GthSlideshowPreferences  *self);
+gboolean      gth_slideshow_preferences_get_audio_loop     (GthSlideshowPreferences  *self);
+
+G_END_DECLS
+
+#endif /* GTH_SLIDESHOW_PREFERENCES_H */
diff --git a/extensions/slideshow/gth-slideshow.c b/extensions/slideshow/gth-slideshow.c
index 15d82c6..d0da6db 100644
--- a/extensions/slideshow/gth-slideshow.c
+++ b/extensions/slideshow/gth-slideshow.c
@@ -24,6 +24,9 @@
 #include <gtk/gtk.h>
 #include <clutter/clutter.h>
 #include <clutter-gtk/clutter-gtk.h>
+#if HAVE_GSTREAMER
+#include <gst/gst.h>
+#endif
 #include "gth-slideshow.h"
 #include "gth-transition.h"
 
@@ -50,6 +53,12 @@ struct _GthSlideshowPrivate {
 	GRand             *rand;
 	gboolean           first_show;
 	gboolean           one_loaded;
+	char             **audio_files;
+	gboolean           audio_loop;
+#if HAVE_GSTREAMER
+	int                current_audio_file;
+	GstElement        *playbin;
+#endif
 };
 
 
@@ -388,6 +397,7 @@ gth_slideshow_init (GthSlideshow *self)
 	self->priv->n_transitions = 0;
 	self->priv->rand = g_rand_new ();
 	self->priv->first_show = TRUE;
+	self->priv->audio_files = NULL;
 
 	self->priv->preloader = gth_image_preloader_new ();
 	g_signal_connect (self->priv->preloader,
@@ -413,6 +423,15 @@ gth_slideshow_finalize (GObject *object)
 	_g_object_unref (self->priv->timeline);
 	_g_object_list_unref (self->priv->transitions);
 	g_rand_free (self->priv->rand);
+	g_strfreev (self->priv->audio_files);
+
+#if HAVE_GSTREAMER
+	if (self->priv->playbin != NULL) {
+		gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
+		gst_object_unref (GST_OBJECT (self->priv->playbin));
+		self->priv->playbin = NULL;
+	}
+#endif
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -502,6 +521,29 @@ stage_input_cb (ClutterStage *stage,
 }
 
 
+#if HAVE_GSTREAMER
+static void
+bus_message_cb (GstBus     *bus,
+                GstMessage *message,
+                gpointer    user_data)
+{
+	GthSlideshow *self = user_data;
+
+	if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS) {
+		self->priv->current_audio_file++;
+		if ((self->priv->audio_files[self->priv->current_audio_file] == NULL)
+		    && self->priv->audio_loop)
+		{
+			self->priv->current_audio_file = 0;
+		}
+		gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
+		g_object_set (G_OBJECT (self->priv->playbin), "uri", self->priv->audio_files[self->priv->current_audio_file], NULL);
+		gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
+	}
+}
+#endif
+
+
 static void
 gth_slideshow_show_cb (GtkWidget    *widget,
 		       GthSlideshow *self)
@@ -509,6 +551,26 @@ gth_slideshow_show_cb (GtkWidget    *widget,
 	if (! self->priv->first_show)
 		return;
 
+#if HAVE_GSTREAMER
+	if ((self->priv->audio_files != NULL) && (self->priv->audio_files[0] != NULL)) {
+		self->priv->current_audio_file = 0;
+		if (self->priv->playbin == NULL) {
+			GstBus *bus;
+
+			gst_init_check (NULL, NULL, NULL); /* FIXME */
+
+			self->priv->playbin = gst_element_factory_make ("playbin", "playbin");
+			bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->playbin));
+			gst_bus_add_signal_watch (bus);
+			g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), self);
+		}
+		else
+			gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
+		g_object_set (G_OBJECT (self->priv->playbin), "uri", self->priv->audio_files[self->priv->current_audio_file], NULL);
+		gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
+	}
+#endif
+
 	_gth_slideshow_load_current_image (self);
 	self->priv->first_show = FALSE;
 }
@@ -602,3 +664,13 @@ gth_slideshow_set_transitions (GthSlideshow *self,
 	self->priv->transitions = _g_object_list_ref (transitions);
 	self->priv->n_transitions = g_list_length (self->priv->transitions);
 }
+
+
+void
+gth_slideshow_set_playlist (GthSlideshow  *self,
+			    char         **files,
+			    gboolean       loop)
+{
+	self->priv->audio_files = g_strdupv (files);
+	self->priv->audio_loop = loop;
+}
diff --git a/extensions/slideshow/gth-slideshow.h b/extensions/slideshow/gth-slideshow.h
index 2d8c2aa..813f7c0 100644
--- a/extensions/slideshow/gth-slideshow.h
+++ b/extensions/slideshow/gth-slideshow.h
@@ -68,6 +68,9 @@ void             gth_slideshow_set_loop        (GthSlideshow     *self,
 					        gboolean          loop);
 void             gth_slideshow_set_transitions (GthSlideshow     *self,
 					        GList            *transitions);
+void             gth_slideshow_set_playlist    (GthSlideshow     *self,
+						char            **files,
+						gboolean          loop);
 
 G_END_DECLS
 
diff --git a/extensions/slideshow/main.c b/extensions/slideshow/main.c
index 1654df6..15a9328 100644
--- a/extensions/slideshow/main.c
+++ b/extensions/slideshow/main.c
@@ -318,6 +318,12 @@ gthumb_extension_activate (void)
 	gth_hook_add_callback ("gth-browser-construct", 10, G_CALLBACK (ss__gth_browser_construct_cb), NULL);
 	gth_hook_add_callback ("gth-browser-update-sensitivity", 10, G_CALLBACK (ss__gth_browser_update_sensitivity_cb), NULL);
 	gth_hook_add_callback ("dlg-preferences-construct", 20, G_CALLBACK (ss__dlg_preferences_construct_cb), NULL);
+	gth_hook_add_callback ("gth-catalog-read-metadata", 10, G_CALLBACK (ss__gth_catalog_read_metadata), NULL);
+	gth_hook_add_callback ("gth-catalog-write-metadata", 10, G_CALLBACK (ss__gth_catalog_write_metadata), NULL);
+	gth_hook_add_callback ("gth-catalog-read-from-doc", 10, G_CALLBACK (ss__gth_catalog_read_from_doc), NULL);
+	gth_hook_add_callback ("gth-catalog-write-to-doc", 10, G_CALLBACK (ss__gth_catalog_write_to_doc), NULL);
+	gth_hook_add_callback ("dlg-catalog-properties", 10, G_CALLBACK (ss__dlg_catalog_properties), NULL);
+	gth_hook_add_callback ("dlg-catalog-properties-save", 10, G_CALLBACK (ss__dlg_catalog_properties_save), NULL);
 }
 
 
diff --git a/extensions/slideshow/preferences.c b/extensions/slideshow/preferences.c
index f9fbc32..e318c82 100644
--- a/extensions/slideshow/preferences.c
+++ b/extensions/slideshow/preferences.c
@@ -22,6 +22,7 @@
 
 #include <config.h>
 #include <glib/gi18n.h>
+#include "gth-slideshow-preferences.h"
 #include "gth-transition.h"
 #include "preferences.h"
 
@@ -30,22 +31,14 @@
 #define BROWSER_DATA_KEY "slideshow-preference-data"
 
 
-enum {
-	TRANSITION_COLUMN_ID,
-	TRANSITION_COLUMN_DISPLAY_NAME
-};
-
-
 typedef struct {
-	GtkBuilder *builder;
-	GtkWidget  *transition_combobox;
+	GtkWidget *preferences_page;
 } BrowserData;
 
 
 static void
 browser_data_free (BrowserData *data)
 {
-	g_object_unref (data->builder);
 	g_free (data);
 }
 
@@ -54,16 +47,11 @@ static void
 transition_combobox_changed_cb (GtkComboBox *combo_box,
 				BrowserData *data)
 {
-	GtkTreeIter   iter;
-	GtkTreeModel *tree_model;
-	char         *transition_id;
-
-	if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (data->transition_combobox), &iter))
-		return;
+	char *transition_id;
 
-	tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (data->transition_combobox));
-	gtk_tree_model_get (tree_model, &iter, TRANSITION_COLUMN_ID, &transition_id, -1);
-	eel_gconf_set_string (PREF_SLIDESHOW_TRANSITION, transition_id);
+	transition_id = gth_slideshow_preferences_get_transition_id (GTH_SLIDESHOW_PREFERENCES (data->preferences_page));
+	if (transition_id != NULL)
+		eel_gconf_set_string (PREF_SLIDESHOW_TRANSITION, transition_id);
 
 	g_free (transition_id);
 }
@@ -98,84 +86,35 @@ ss__dlg_preferences_construct_cb (GtkWidget  *dialog,
 				  GthBrowser *browser,
 				  GtkBuilder *dialog_builder)
 {
-	BrowserData     *data;
-	GtkWidget       *notebook;
-	GtkWidget       *page;
-	GtkListStore    *model;
-	GtkCellRenderer *renderer;
-	char            *current_transition;
-	GList           *transitions;
-	GList           *scan;
-	GtkTreeIter      iter;
-	int              i, i_active;
-	GtkWidget       *label;
-
-	data = g_new0 (BrowserData, 1);
-	data->builder = _gtk_builder_new_from_file ("slideshow-preferences.ui", "slideshow");
+	BrowserData *data;
+	GtkWidget   *notebook;
+	char        *current_transition;
+	GtkWidget   *label;
 
 	notebook = _gtk_builder_get_widget (dialog_builder, "notebook");
 
-	page = _gtk_builder_get_widget (data->builder, "preferences_page");
-	gtk_widget_show (page);
-
-	model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
-	data->transition_combobox = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model));
-	g_object_unref (model);
-
-	renderer = gtk_cell_renderer_text_new ();
-	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->transition_combobox),
-				    renderer,
-				    TRUE);
-	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->transition_combobox),
-					renderer,
-					"text", TRANSITION_COLUMN_DISPLAY_NAME,
-					NULL);
-
+	data = g_new0 (BrowserData, 1);
 	current_transition = eel_gconf_get_string (PREF_SLIDESHOW_TRANSITION, DEFAULT_TRANSITION);
-	transitions = gth_main_get_registered_objects (GTH_TYPE_TRANSITION);
-	for (i = 0, i_active = 0, scan = transitions; scan; scan = scan->next, i++) {
-		GthTransition *transition = scan->data;
-
-		if (strcmp (gth_transition_get_id (transition), current_transition) == 0)
-			i_active = i;
-
-		gtk_list_store_append (model, &iter);
-		gtk_list_store_set (model, &iter,
-				    TRANSITION_COLUMN_ID, gth_transition_get_id (transition),
-				    TRANSITION_COLUMN_DISPLAY_NAME, gth_transition_get_display_name (transition),
-				    -1);
-	}
-
-	if (strcmp ("random", current_transition) == 0)
-		i_active = i;
-	gtk_list_store_append (model, &iter);
-	gtk_list_store_set (model, &iter,
-			    TRANSITION_COLUMN_ID, "random",
-			    TRANSITION_COLUMN_DISPLAY_NAME, _("Random"),
-			    -1);
-
-	gtk_combo_box_set_active (GTK_COMBO_BOX (data->transition_combobox), i_active);
-	gtk_widget_show (data->transition_combobox);
-	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("transition_box")), data->transition_combobox);
+	data->preferences_page = gth_slideshow_preferences_new (current_transition,
+							        eel_gconf_get_boolean (PREF_SLIDESHOW_AUTOMATIC, TRUE),
+							        eel_gconf_get_float (PREF_SLIDESHOW_CHANGE_DELAY, 5.0),
+							        eel_gconf_get_boolean (PREF_SLIDESHOW_WRAP_AROUND, FALSE));
+	gtk_widget_show (data->preferences_page);
 	g_free (current_transition);
 
-	gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("change_delay_spinbutton")), eel_gconf_get_float (PREF_SLIDESHOW_CHANGE_DELAY, 5));
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("automatic_checkbutton")), eel_gconf_get_boolean (PREF_SLIDESHOW_AUTOMATIC, TRUE));
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("wrap_around_checkbutton")), eel_gconf_get_boolean (PREF_SLIDESHOW_WRAP_AROUND, FALSE));
-
-	g_signal_connect (G_OBJECT (data->transition_combobox),
+	g_signal_connect (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (data->preferences_page), "transition_combobox"),
 			  "changed",
 			  G_CALLBACK (transition_combobox_changed_cb),
 			  data);
-	g_signal_connect (G_OBJECT (GET_WIDGET ("automatic_checkbutton")),
+	g_signal_connect (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (data->preferences_page), "automatic_checkbutton"),
 			  "toggled",
 			  G_CALLBACK (automatic_checkbutton_toggled_cb),
 			  data);
-	g_signal_connect (G_OBJECT (GET_WIDGET ("wrap_around_checkbutton")),
+	g_signal_connect (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (data->preferences_page), "wrap_around_checkbutton"),
 			  "toggled",
 			  G_CALLBACK (wrap_around_checkbutton_toggled_cb),
 			  data);
-	g_signal_connect (G_OBJECT (GET_WIDGET ("change_delay_spinbutton")),
+	g_signal_connect (gth_slideshow_preferences_get_widget (GTH_SLIDESHOW_PREFERENCES (data->preferences_page), "change_delay_spinbutton"),
 			  "value-changed",
 			  G_CALLBACK (change_delay_spinbutton_value_changed_cb),
 			  data);
@@ -183,7 +122,7 @@ ss__dlg_preferences_construct_cb (GtkWidget  *dialog,
 	label = gtk_label_new (_("Slideshow"));
 	gtk_widget_show (label);
 
-	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label);
+	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), data->preferences_page, label);
 
 	g_object_set_data_full (G_OBJECT (dialog), BROWSER_DATA_KEY, data, (GDestroyNotify) browser_data_free);
 }
diff --git a/extensions/slideshow/slideshow.extension.in.in b/extensions/slideshow/slideshow.extension.in.in
index 2cf6159..de7672c 100644
--- a/extensions/slideshow/slideshow.extension.in.in
+++ b/extensions/slideshow/slideshow.extension.in.in
@@ -9,3 +9,4 @@ Icon=x-office-presentation
 [Loader]
 Type=module
 File=%LIBRARY%
+After=catalogs
\ No newline at end of file
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index a30d276..9a80830 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -108,6 +108,7 @@ PUBLIC_HEADER_FILES = 					\
 	gth-viewer-page.h				\
 	gth-window.h					\
 	gtk-utils.h					\
+	gvaluehash.h					\
 	main.h						\
 	pixbuf-cache.h					\
 	pixbuf-io.h					\
@@ -224,6 +225,7 @@ gthumb_SOURCES = 					\
 	gth-window.c					\
 	gth-window-actions-callbacks.c			\
 	gtk-utils.c					\
+	gvaluehash.c					\
 	main.c						\
 	pixbuf-cache.c					\
 	pixbuf-io.c					\
diff --git a/gthumb/glib-utils.c b/gthumb/glib-utils.c
index b7987df..ca1dcd2 100644
--- a/gthumb/glib-utils.c
+++ b/gthumb/glib-utils.c
@@ -1432,6 +1432,22 @@ _g_string_list_dup (GList *string_list)
 }
 
 
+char **
+_g_string_list_to_strv (GList *string_list)
+{
+	char  **strv;
+	GList  *scan;
+	int     i;
+
+	strv = g_new0 (char *, g_list_length (string_list));
+	for (scan = string_list, i = 0; scan; scan = scan->next)
+		strv[i++] = g_strdup ((char *)scan->data);
+	strv[i++] = NULL;
+
+	return strv;
+}
+
+
 GType
 g_string_list_get_type (void)
 {
diff --git a/gthumb/glib-utils.h b/gthumb/glib-utils.h
index b1cc1ba..5e2cfec 100644
--- a/gthumb/glib-utils.h
+++ b/gthumb/glib-utils.h
@@ -208,6 +208,7 @@ gboolean        same_uri                         (const char *uri1,
 						  const char *uri2);
 void            _g_string_list_free              (GList      *string_list);
 GList *         _g_string_list_dup               (GList      *string_list);
+char **         _g_string_list_to_strv           (GList      *string_list);
 GType           g_string_list_get_type           (void);
 GList *         get_file_list_from_url_list      (char       *url_list);
 const char *    _g_uri_get_basename              (const char *uri);
diff --git a/gthumb/gth-browser.c b/gthumb/gth-browser.c
index 7f34351..c732930 100644
--- a/gthumb/gth-browser.c
+++ b/gthumb/gth-browser.c
@@ -1258,7 +1258,7 @@ load_data_load_next_folder (LoadData *load_data)
 	if ((load_data->action != GTH_ACTION_LIST_CHILDREN) && g_file_equal (folder_to_load, load_data->requested_folder->file))
 		gth_file_source_read_metadata (load_data->file_source,
 					       load_data->requested_folder,
-					       GFILE_BASIC_ATTRIBUTES ",access::*,sort::*",
+					       "*", /*GFILE_BASIC_ATTRIBUTES ",access::*,sort::*", FIXME*/
 					       requested_folder_attributes_ready_cb,
      					       load_data);
 	else
diff --git a/gthumb/gvaluehash.c b/gthumb/gvaluehash.c
new file mode 100644
index 0000000..da7de12
--- /dev/null
+++ b/gthumb/gvaluehash.c
@@ -0,0 +1,201 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <config.h>
+#include "gvaluehash.h"
+#include "glib-utils.h"
+
+
+struct _GValueHash {
+	int         ref;
+	GHashTable *table;
+};
+
+
+static void
+_g_value_free (gpointer data)
+{
+	GValue *value = data;
+
+	g_value_reset (value);
+	g_free (value);
+}
+
+
+GValueHash *
+g_value_hash_new (void)
+{
+	GValueHash *self;
+
+	self = g_new0 (GValueHash, 1);
+	self->ref = 1;
+	self->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _g_value_free);
+
+	return self;
+}
+
+
+void
+g_value_hash_ref (GValueHash *self)
+{
+	self->ref++;
+}
+
+
+void
+g_value_hash_unref (GValueHash *self)
+{
+	self->ref--;
+	if (self->ref > 0)
+		return;
+	g_hash_table_destroy (self->table);
+	g_free (self);
+}
+
+
+void
+g_value_hash_set_boolean (GValueHash *self,
+			  const char *key,
+			  gboolean    b_value)
+{
+	GValue *priv_value;
+
+	priv_value = g_new0 (GValue, 1);
+	g_value_init (priv_value, G_TYPE_BOOLEAN);
+	g_value_set_boolean (priv_value, b_value);
+	g_hash_table_insert (self->table, g_strdup (key), priv_value);
+}
+
+
+void
+g_value_hash_set_float (GValueHash *self,
+			const char *key,
+			float       f_value)
+{
+	GValue *priv_value;
+
+	priv_value = g_new0 (GValue, 1);
+	g_value_init (priv_value, G_TYPE_FLOAT);
+	g_value_set_float (priv_value, f_value);
+	g_hash_table_insert (self->table, g_strdup (key), priv_value);
+}
+
+
+void
+g_value_hash_set_int (GValueHash *self,
+		      const char *key,
+		      int         i_value)
+{
+	GValue *priv_value;
+
+	priv_value = g_new0 (GValue, 1);
+	g_value_init (priv_value, G_TYPE_INT);
+	g_value_set_int (priv_value, i_value);
+	g_hash_table_insert (self->table, g_strdup (key), priv_value);
+}
+
+
+void
+g_value_hash_set_string (GValueHash *self,
+			 const char *key,
+			 const char *s_value)
+{
+	GValue *priv_value;
+
+	priv_value = g_new0 (GValue, 1);
+	g_value_init (priv_value, G_TYPE_STRING);
+	g_value_set_string (priv_value, s_value);
+	g_hash_table_insert (self->table, g_strdup (key), priv_value);
+}
+
+
+void
+g_value_hash_set_stringv (GValueHash  *self,
+			  const char  *key,
+			  char       **value)
+{
+	GValue *priv_value;
+
+	priv_value = g_new0 (GValue, 1);
+	g_value_init (priv_value, G_TYPE_STRV);
+	g_value_set_boxed (priv_value, value);
+	g_hash_table_insert (self->table, g_strdup (key), priv_value);
+}
+
+
+void
+g_value_hash_set_string_list (GValueHash *self,
+			      const char *key,
+			      GList      *l_value)
+{
+	GValue *priv_value;
+
+	priv_value = g_new0 (GValue, 1);
+	g_value_init (priv_value, G_TYPE_STRING_LIST);
+	g_value_set_boxed (priv_value, l_value);
+	g_hash_table_insert (self->table, g_strdup (key), priv_value);
+}
+
+
+void
+g_value_hash_set_value (GValueHash *self,
+			const char *key,
+			GValue     *value)
+{
+	GValue *priv_value;
+
+	priv_value = g_new0 (GValue, 1);
+	g_value_copy (value, priv_value);
+	g_hash_table_insert (self->table, g_strdup (key), priv_value);
+}
+
+
+GValue *
+g_value_hash_get_value (GValueHash *self,
+			const char *key)
+{
+	return g_hash_table_lookup (self->table, key);
+}
+
+
+gboolean
+g_value_hash_is_set (GValueHash *self,
+		     const char *key)
+{
+	return (g_hash_table_lookup (self->table, key) != NULL);
+}
+
+
+void
+g_value_hash_unset (GValueHash *self,
+		    const char *key)
+{
+	g_hash_table_remove (self->table, key);
+}
+
+
+void
+g_value_hash_clear (GValueHash *self)
+{
+	g_hash_table_remove_all (self->table);
+}
diff --git a/gthumb/gvaluehash.h b/gthumb/gvaluehash.h
new file mode 100644
index 0000000..55c21dd
--- /dev/null
+++ b/gthumb/gvaluehash.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef G_VALUE_HASH_H
+#define G_VALUE_HASH_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GValueHash:
+ *
+ * A #GValueHash contains an hash table of strings associated to #GValue
+ * elements.
+ */
+typedef struct _GValueHash GValueHash;
+
+GValueHash *   g_value_hash_new (void);
+void           g_value_hash_ref             (GValueHash  *self);
+void           g_value_hash_unref           (GValueHash  *self);
+void           g_value_hash_set_boolean     (GValueHash  *self,
+					     const char  *key,
+					     gboolean     value);
+void           g_value_hash_set_float       (GValueHash  *self,
+					     const char  *key,
+					     float        value);
+void           g_value_hash_set_int         (GValueHash  *self,
+					     const char  *key,
+					     int          value);
+void           g_value_hash_set_string      (GValueHash  *self,
+					     const char  *key,
+					     const char  *value);
+void           g_value_hash_set_stringv     (GValueHash  *self,
+					     const char  *key,
+					     char       **value);
+void           g_value_hash_set_string_list (GValueHash  *self,
+					     const char  *key,
+					     GList       *value);
+void           g_value_hash_set_value       (GValueHash  *self,
+					     const char  *key,
+					     GValue      *value);
+GValue *       g_value_hash_get_value       (GValueHash  *self,
+					     const char  *key);
+gboolean       g_value_hash_is_set          (GValueHash  *self,
+					     const char  *key);
+#define        g_value_hash_get_boolean(self, key)          (g_value_get_boolean (g_value_hash_get_value ((self), (key))))
+#define        g_value_hash_get_float(self, key)            (g_value_get_float (g_value_hash_get_value ((self), (key))))
+#define        g_value_hash_get_int(self, key)              (g_value_get_int (g_value_hash_get_value ((self), (key))))
+#define        g_value_hash_get_string(self, key)           (g_value_get_string (g_value_hash_get_value ((self), (key))))
+#define        g_value_hash_get_stringv(self, key)          ((char **) g_value_get_boxed (g_value_hash_get_value ((self), (key))))
+#define        g_value_hash_get_string_list(self, key)      ((GList *) g_value_get_boxed (g_value_hash_get_value ((self), (key))))))
+void           g_value_hash_unset           (GValueHash *self,
+					     const char *key);
+void           g_value_hash_clear           (GValueHash *self);
+
+G_END_DECLS
+
+#endif /* G_VALUE_HASH_H */



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