[gthumb/ext: 17/18] added ability to save the video screenshot



commit e22ba0bb8e1577002bb9a17777659084ea3d2d9d
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Fri Nov 6 01:10:03 2009 +0100

    added ability to save the video screenshot
    
    disable the screenshot command if no video is available

 extensions/gstreamer/Makefile.am                   |    3 +-
 extensions/gstreamer/data/Makefile.am              |   16 +
 .../gstreamer/data/gthumb-gstreamer.schemas.in     |   17 +
 extensions/gstreamer/data/ui/save-screenshot.ui    |  171 ++++++++++
 extensions/gstreamer/gth-media-viewer-page.c       |  349 ++++++++++++++++----
 extensions/gstreamer/preferences.h                 |   28 ++
 extensions/image_viewer/gth-image-viewer-page.c    |  158 +--------
 extensions/image_viewer/preferences.h              |    2 +-
 gthumb/Makefile.am                                 |    2 +
 gthumb/glib-utils.c                                |   24 ++
 gthumb/glib-utils.h                                |    1 +
 gthumb/gth-file-chooser-dialog.c                   |  276 ++++++++++++++++
 gthumb/gth-file-chooser-dialog.h                   |   61 ++++
 13 files changed, 903 insertions(+), 205 deletions(-)
---
diff --git a/extensions/gstreamer/Makefile.am b/extensions/gstreamer/Makefile.am
index b7b1c92..3b2b559 100644
--- a/extensions/gstreamer/Makefile.am
+++ b/extensions/gstreamer/Makefile.am
@@ -14,7 +14,8 @@ libgstreamer_la_SOURCES = 			\
 	gth-media-viewer-page.h			\
 	gth-metadata-provider-gstreamer.c	\
 	gth-metadata-provider-gstreamer.h	\
-	main.c
+	main.c					\
+	preferences.h
 
 libgstreamer_la_CPPFLAGS = $(GTHUMB_CFLAGS) $(GSTREAMER_CFLAGS) $(DISABLE_DEPRECATED) $(WARNINGS) -I$(top_srcdir) -I$(top_builddir)/gthumb 
 libgstreamer_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
diff --git a/extensions/gstreamer/data/Makefile.am b/extensions/gstreamer/data/Makefile.am
index 4d5385d..65c0e27 100644
--- a/extensions/gstreamer/data/Makefile.am
+++ b/extensions/gstreamer/data/Makefile.am
@@ -1,2 +1,18 @@
 SUBDIRS = ui
+
+schemadir = @GCONF_SCHEMA_FILE_DIR@
+schema_in_files = gthumb-gstreamer.schemas.in
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+ INTLTOOL_SCHEMAS_RULE@
+
+if GCONF_SCHEMAS_INSTALL
+install-data-local:
+	GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(top_builddir)/extensions/image_viewer/data/$(schema_DATA)
+endif
+
+EXTRA_DIST = $(schema_in_files)
+
+CLEANFILES = $(schema_DATA)
+
 -include $(top_srcdir)/git.mk
diff --git a/extensions/gstreamer/data/gthumb-gstreamer.schemas.in b/extensions/gstreamer/data/gthumb-gstreamer.schemas.in
new file mode 100644
index 0000000..0fe9cf1
--- /dev/null
+++ b/extensions/gstreamer/data/gthumb-gstreamer.schemas.in
@@ -0,0 +1,17 @@
+<gconfschemafile>
+    <schemalist>
+
+      <schema>
+	<key>/schemas/apps/gthumb/ext/gstreamer/screenshot_location</key>
+	<applyto>/apps/gthumb/ext/gstreamer/screenshot_location</applyto>
+	<owner>gthumb</owner>
+	<type>string</type>
+	<default>~</default>
+	<locale name="C">
+	  <short></short>
+	  <long></long>
+	</locale>
+      </schema>
+
+    </schemalist>
+</gconfschemafile>
diff --git a/extensions/gstreamer/data/ui/save-screenshot.ui b/extensions/gstreamer/data/ui/save-screenshot.ui
new file mode 100644
index 0000000..1a254c3
--- /dev/null
+++ b/extensions/gstreamer/data/ui/save-screenshot.ui
@@ -0,0 +1,171 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkDialog" id="save_screenshot_dialog">
+    <property name="border_width">5</property>
+    <property name="resizable">False</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkScrolledWindow" id="preview_scrolledwindow">
+                    <property name="width_request">240</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">never</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <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">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <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="label1">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Name:</property>
+                        <property name="use_underline">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label2">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Destination:</property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="filename_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x25CF;</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkFileChooserButton" id="destination_filechooserbutton">
+                        <property name="visible">True</property>
+                        <property name="local_only">False</property>
+                        <property name="create_folders">False</property>
+                        <property name="action">select-folder</property>
+                        <property name="title" translatable="yes">Choose a folder</property>
+                      </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>
+                      </packing>
+                    </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>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="ok_button">
+                <property name="label">gtk-save</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <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="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">cancel_button</action-widget>
+      <action-widget response="0">ok_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/extensions/gstreamer/gth-media-viewer-page.c b/extensions/gstreamer/gth-media-viewer-page.c
index 365dae3..699b7a9 100644
--- a/extensions/gstreamer/gth-media-viewer-page.c
+++ b/extensions/gstreamer/gth-media-viewer-page.c
@@ -29,11 +29,13 @@
 #include <gthumb.h>
 #include "gstreamer-utils.h"
 #include "gth-media-viewer-page.h"
+#include "preferences.h"
 
 
 #define GTH_MEDIA_VIEWER_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTH_TYPE_MEDIA_VIEWER_PAGE, GthMediaViewerPagePrivate))
 #define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
 #define PROGRESS_DELAY 500
+#define MAX_ATTEMPTS 1024
 
 
 struct _GthMediaViewerPagePrivate {
@@ -46,8 +48,14 @@ struct _GthMediaViewerPagePrivate {
 	GtkWidget      *area;
 	GtkWidget      *area_box;
 	gboolean        playing;
+	gboolean        paused;
+	gboolean        playing_before_screenshot;
 	gdouble         last_volume;
 	gint64          duration;
+	int             video_fps_n;
+	int             video_fps_d;
+	gboolean        has_video;
+	gboolean        has_audio;
 	gulong          update_progress_id;
 	gdouble         rate;
 	GtkWidget      *mediabar;
@@ -75,26 +83,148 @@ static const char *media_viewer_ui_info =
 "</ui>";
 
 
+typedef struct {
+	GthMediaViewerPage *self;
+	GdkPixbuf          *pixbuf;
+	GthFileData        *file_data;
+} SaveData;
+
+
+static void
+save_date_free (SaveData *save_data)
+{
+	_g_object_unref (save_data->file_data);
+	_g_object_unref (save_data->pixbuf);
+	g_free (save_data);
+}
+
+
+static void
+screenshot_saved_cb (GthFileData *file_data,
+		     GError      *error,
+		     gpointer     user_data)
+{
+	SaveData           *save_data = user_data;
+	GthMediaViewerPage *self = save_data->self;
+
+	if (error != NULL)
+		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (self->priv->browser), _("Could not save the file"), &error);
+	else if (self->priv->playing_before_screenshot)
+		gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
+
+	save_date_free (save_data);
+}
+
+
+static void
+save_as_response_cb (GtkDialog  *file_sel,
+		     int         response,
+		     SaveData   *save_data)
+{
+	GFile       *file;
+	GFile       *folder;
+	char        *folder_uri;
+	const char  *mime_type;
+
+	if (response != GTK_RESPONSE_OK) {
+		GthMediaViewerPage *self = save_data->self;
+
+		if (self->priv->playing_before_screenshot)
+			gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
+		save_date_free (save_data);
+		gtk_widget_destroy (GTK_WIDGET (file_sel));
+		return;
+	}
+
+	if (! gth_file_chooser_dialog_get_file (GTH_FILE_CHOOSER_DIALOG (file_sel), &file, &mime_type))
+		return;
+
+	folder = g_file_get_parent (file);
+	folder_uri = g_file_get_uri (folder);
+	eel_gconf_set_string (PREF_GSTREAMER_SCREESHOT_LOCATION, folder_uri);
+
+	save_data->file_data = gth_file_data_new (file, NULL);
+	gth_file_data_set_mime_type (save_data->file_data, mime_type);
+	_gdk_pixbuf_save_async (save_data->pixbuf,
+				save_data->file_data,
+				mime_type,
+				screenshot_saved_cb,
+				save_data);
+
+	gtk_widget_destroy (GTK_WIDGET (file_sel));
+
+	g_free (folder_uri);
+	g_object_unref (folder);
+	g_object_unref (file);
+}
+
+
 static void
 screenshot_ready_cb (GdkPixbuf *pixbuf,
 		     gpointer   user_data)
 {
 	GthMediaViewerPage *self = user_data;
-	GtkWidget          *image;
-	GtkWidget          *window;
+	SaveData           *save_data;
+	GtkWidget          *file_sel;
 
 	if (pixbuf == NULL) {
 		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (self->priv->browser), _("Could not take a screenshot"), NULL);
 		return;
 	}
 
-	image = gtk_image_new_from_pixbuf (pixbuf);
-	gtk_widget_show (image);
-	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-	gtk_container_add (GTK_CONTAINER (window), image);
-	gtk_window_present (GTK_WINDOW (window));
+	save_data = g_new0 (SaveData, 1);
+	save_data->self = self;
+	save_data->pixbuf = pixbuf;
+
+	file_sel = gth_file_chooser_dialog_new (_("Save Image"), GTK_WINDOW (self->priv->browser), "pixbuf-saver");
 
-	g_object_unref (pixbuf);
+	{
+		char   *last_uri;
+		GFile  *last_folder;
+		char   *prefix;
+		char   *display_name;
+		int     attempt;
+
+		last_uri = eel_gconf_get_string (PREF_GSTREAMER_SCREESHOT_LOCATION, "~");
+		if ((last_uri == NULL) || (strcmp (last_uri, "~") == 0))
+			last_folder = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
+		else
+			last_folder = g_file_new_for_uri (last_uri);
+		gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (file_sel), last_folder, NULL);
+
+		prefix = _g_utf8_remove_extension (g_file_info_get_display_name (self->priv->file_data->info));
+		if (prefix == NULL)
+			prefix = g_strdup (C_("Filename", "Screenshot"));
+		display_name = NULL;
+		for (attempt = 1; attempt < MAX_ATTEMPTS; attempt++) {
+			GFile *proposed_file;
+
+			g_free (display_name);
+
+			display_name = g_strdup_printf ("%s-%02d.jpeg", prefix, attempt);
+			proposed_file = g_file_get_child_for_display_name (last_folder, display_name, NULL);
+			if ((proposed_file != NULL) && ! g_file_query_exists (proposed_file, NULL)) {
+				g_object_unref (proposed_file);
+				break;
+			}
+		}
+
+		if (display_name != NULL) {
+			gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (file_sel), display_name);
+			g_free (display_name);
+		}
+
+		g_free (prefix);
+		g_object_unref (last_folder);
+		g_free (last_uri);
+	}
+
+	g_signal_connect (GTK_DIALOG (file_sel),
+			  "response",
+			  G_CALLBACK (save_as_response_cb),
+			  save_data);
+
+	gtk_widget_show (file_sel);
 }
 
 
@@ -105,9 +235,12 @@ media_viewer_activate_action_screenshot (GtkAction          *action,
 	if (self->priv->playbin == NULL)
 		return;
 
+	self->priv->playing_before_screenshot = self->priv->playing;
+	if (self->priv->playing)
+		gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
 	_gst_playbin_get_current_frame (self->priv->playbin,
-					0 /*self->priv->video_fps_n*/,
-					0 /*self->priv->video_fps_d*/,
+					self->priv->video_fps_n,
+					self->priv->video_fps_d,
 					screenshot_ready_cb,
 					self);
 }
@@ -191,22 +324,74 @@ volume_value_changed_cb (GtkAdjustment *adjustment,
 }
 
 
+static void position_value_changed_cb (GtkAdjustment *adjustment,
+				       gpointer       user_data);
+
+
+static void
+update_current_position_bar (GthMediaViewerPage *self,
+			     gboolean            update_progressbar)
+{
+	GstFormat format;
+        gint64    current_value = 0;
+
+        format = GST_FORMAT_TIME;
+        if (gst_element_query_position (self->priv->playbin, &format, &current_value)) {
+        	char *s;
+
+        	if (self->priv->duration <= 0) {
+        		gst_element_query_duration (self->priv->playbin, &format, &self->priv->duration);
+        		s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (self->priv->duration));
+        		gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_duration")), s);
+
+        		g_free (s);
+        	}
+
+        	/*
+        	g_print ("==> %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " (%0.3g)\n" ,
+        		 current_value,
+        		 self->priv->duration,
+        		 ((double) current_value / self->priv->duration) * 100.0);
+		*/
+
+        	if (update_progressbar) {
+			g_signal_handlers_block_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
+			gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("adjustment_position")), (self->priv->duration > 0) ? ((double) current_value / self->priv->duration) * 100.0 : 0.0);
+			g_signal_handlers_unblock_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
+        	}
+
+        	s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
+        	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
+
+        	g_free (s);
+        }
+}
+
+
 static void
 position_value_changed_cb (GtkAdjustment *adjustment,
 			   gpointer       user_data)
 {
 	GthMediaViewerPage *self = user_data;
+	gint64              current_value;
+	char               *s;
 
 	if (self->priv->playbin == NULL)
 		return;
 
+	current_value = (gint64) (gtk_adjustment_get_value (adjustment) / 100.0 * self->priv->duration);
 	if (! gst_element_seek_simple (self->priv->playbin,
 				       GST_FORMAT_TIME,
 				       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
-				       (gint64) (gtk_adjustment_get_value (adjustment) / 100.0 * self->priv->duration)))
+				       current_value))
 	{
 		g_warning ("seek failed");
 	}
+
+	s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
+
+	g_free (s);
 }
 
 
@@ -252,7 +437,15 @@ button_play_clicked_cb (GtkButton *button,
 
 	if (self->priv->playbin == NULL)
 		return;
-	gst_element_set_state (self->priv->playbin, ! self->priv->playing ? GST_STATE_PLAYING : GST_STATE_PAUSED);
+	if (! self->priv->playing) {
+		if (! self->priv->paused) {
+			gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
+			gst_element_seek_simple (self->priv->playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, 0);
+		}
+		gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
+	}
+	else
+		gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
 }
 
 
@@ -320,43 +513,6 @@ update_volume_from_playbin (GthMediaViewerPage *self)
 }
 
 
-static void
-update_current_position_bar (GthMediaViewerPage *self)
-{
-	GstFormat format;
-        gint64    current_value = 0;
-
-        format = GST_FORMAT_TIME;
-        if (gst_element_query_position (self->priv->playbin, &format, &current_value)) {
-        	char *s;
-
-        	if (self->priv->duration <= 0) {
-        		gst_element_query_duration (self->priv->playbin, &format, &self->priv->duration);
-        		s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (self->priv->duration));
-        		gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_duration")), s);
-
-        		g_free (s);
-        	}
-
-        	/*
-        	g_print ("==> %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " (%0.3g)\n" ,
-        		 current_value,
-        		 self->priv->duration,
-        		 ((double) current_value / self->priv->duration) * 100.0);
-        	*/
-
-        	g_signal_handlers_block_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
-        	gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("adjustment_position")), (self->priv->duration > 0) ? ((double) current_value / self->priv->duration) * 100.0 : 0.0);
-        	g_signal_handlers_unblock_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
-
-        	s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
-        	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
-
-        	g_free (s);
-        }
-}
-
-
 static gboolean
 update_progress_cb (gpointer user_data)
 {
@@ -367,7 +523,7 @@ update_progress_cb (gpointer user_data)
                 self->priv->update_progress_id = 0;
         }
 
-        update_current_position_bar (self);
+        update_current_position_bar (self, TRUE);
 
         self->priv->update_progress_id = gdk_threads_add_timeout (PROGRESS_DELAY, update_progress_cb, self);
 
@@ -549,6 +705,63 @@ reset_player_state (GthMediaViewerPage *self)
 
 
 static void
+update_stream_info (GthMediaViewerPage *self)
+{
+	GList  *streaminfo;
+	GstPad *videopad;
+
+	streaminfo = NULL;
+	videopad = NULL;
+
+	g_object_get (self->priv->playbin, "stream-info", &streaminfo, NULL);
+	streaminfo = g_list_copy (streaminfo);
+	g_list_foreach (streaminfo, (GFunc) g_object_ref, NULL);
+
+	for (/* void */ ; streaminfo; streaminfo = streaminfo->next) {
+		GObject    *info;
+		int         type;
+		GParamSpec *pspec;
+		GEnumValue *val;
+
+		info = streaminfo->data;
+		if (info == NULL)
+			continue;
+
+                type = -1;
+
+		g_object_get (info, "type", &type, NULL);
+		pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info), "type");
+		val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
+
+		if (strcmp (val->value_nick, "audio") == 0) {
+			self->priv->has_audio = TRUE;
+		}
+		else if (strcmp (val->value_nick, "video") == 0) {
+			self->priv->has_video = TRUE;
+			if (videopad == NULL)
+				g_object_get (info, "object", &videopad, NULL);
+		}
+	}
+
+	if (videopad != NULL) {
+		GstCaps *caps;
+
+		if ((caps = gst_pad_get_negotiated_caps (videopad)) != NULL) {
+			GstStructure *structure;
+
+			structure = gst_caps_get_structure (caps, 0);
+			gst_structure_get_fraction (structure, "framerate", &self->priv->video_fps_n, &self->priv->video_fps_d);
+
+			gst_caps_unref (caps);
+		}
+	}
+
+	g_list_foreach (streaminfo, (GFunc) g_object_unref, NULL);
+	g_list_free (streaminfo);
+}
+
+
+static void
 bus_message_cb (GstBus     *bus,
                 GstMessage *message,
                 gpointer    user_data)
@@ -564,13 +777,19 @@ bus_message_cb (GstBus     *bus,
 		old_state = new_state = GST_STATE_NULL;
 		gst_message_parse_state_changed (message, &old_state, &new_state, &pending_state);
 
+		self->priv->paused = (new_state == GST_STATE_PAUSED);
+
 		if (old_state == new_state)
 			break;
 		if (GST_MESSAGE_SRC (message) != GST_OBJECT (self->priv->playbin))
 			break;
 
-		update_current_position_bar (self);
+		update_current_position_bar (self, TRUE);
 
+		if ((old_state == GST_STATE_READY) && (new_state == GST_STATE_PAUSED)) {
+			update_stream_info (self);
+			gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
+		}
 		if ((old_state == GST_STATE_READY) || (new_state == GST_STATE_PAUSED))
 			update_volume_from_playbin (self);
 		if ((old_state == GST_STATE_PLAYING) || (new_state == GST_STATE_PLAYING))
@@ -583,13 +802,12 @@ bus_message_cb (GstBus     *bus,
 
 		format = GST_FORMAT_TIME;
 		gst_message_parse_duration (message, &format, &self->priv->duration);
-		update_current_position_bar (self);
+		update_current_position_bar (self, TRUE);
 		break;
 	}
 
 	case GST_MESSAGE_EOS:
 		reset_player_state (self);
-		/*gst_element_set_state (self->priv->playbin, GST_STATE_READY);*/
 		break;
 
 	case GST_MESSAGE_BUFFERING: {
@@ -635,10 +853,9 @@ gth_media_viewer_page_real_show (GthViewerPage *base)
 
 	gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
 
-	if (self->priv->playbin != NULL) {
-		gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
-		gst_object_unref (GST_OBJECT (self->priv->playbin));
-	}
+	if (self->priv->playbin != NULL)
+		return;
+
 	self->priv->playbin = gst_element_factory_make ("playbin", "playbin");
 	g_signal_connect (self->priv->playbin, "notify::volume", G_CALLBACK (playbin_notify_volume_cb), self);
 
@@ -813,12 +1030,26 @@ gth_media_viewer_page_real_show_pointer (GthViewerPage *base,
 
 
 static void
+set_action_sensitive (GthMediaViewerPage *self,
+		      const char         *action_name,
+		      gboolean            sensitive)
+{
+	GtkAction *action;
+
+	action = gtk_action_group_get_action (self->priv->actions, action_name);
+	g_object_set (action, "sensitive", sensitive, NULL);
+}
+
+
+static void
 gth_media_viewer_page_real_update_sensitivity (GthViewerPage *base)
 {
 	GthMediaViewerPage *self = (GthMediaViewerPage *) base;
 
 	gtk_widget_set_sensitive (GET_WIDGET ("button_play_slower"), self->priv->playing);
 	gtk_widget_set_sensitive (GET_WIDGET ("button_play_faster"), self->priv->playing);
+
+	set_action_sensitive (self, "MediaViewer_Screenshot", self->priv->has_video);
 }
 
 
@@ -914,6 +1145,10 @@ gth_media_viewer_page_instance_init (GthMediaViewerPage *self)
 	self->priv = GTH_MEDIA_VIEWER_PAGE_GET_PRIVATE (self);
 	self->priv->update_progress_id = 0;
 	self->priv->video_present = FALSE;
+	self->priv->has_video = FALSE;
+	self->priv->has_audio = FALSE;
+	self->priv->video_fps_n = 0;
+	self->priv->video_fps_d = 0;
 }
 
 
diff --git a/extensions/gstreamer/preferences.h b/extensions/gstreamer/preferences.h
new file mode 100644
index 0000000..0cad7b1
--- /dev/null
+++ b/extensions/gstreamer/preferences.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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 PREFERENCES_H
+#define PREFERENCES_H
+
+#define  PREF_GSTREAMER_SCREESHOT_LOCATION "/apps/gthumb/ext/gstreamer/screenshot_location"
+
+#endif /* PREFERENCES_H */
diff --git a/extensions/image_viewer/gth-image-viewer-page.c b/extensions/image_viewer/gth-image-viewer-page.c
index 5664657..ff3b84e 100644
--- a/extensions/image_viewer/gth-image-viewer-page.c
+++ b/extensions/image_viewer/gth-image-viewer-page.c
@@ -788,20 +788,9 @@ typedef struct {
 	gpointer            user_data;
 	GthFileData        *file_data;
 	GtkWidget          *file_sel;
-	GtkWidget          *format_chooser;
 } SaveAsData;
 
 
-typedef struct {
-	const char *type;
-	const char *extensions;
-	const char *default_ext;
-} Format;
-
-
-static GList *supported_formats = NULL;
-
-
 static void
 save_as_destroy_cb (GtkWidget  *w,
 		    SaveAsData *data)
@@ -816,12 +805,10 @@ save_as_response_cb (GtkDialog  *file_sel,
 		     int         response,
 		     SaveAsData *data)
 {
-	char   *filename;
-	int     n_format;
-	Format *format;
-	GFile  *file;
+	GFile      *file;
+	const char *mime_type;
 
-	if (response != GTK_RESPONSE_ACCEPT) {
+	if (response != GTK_RESPONSE_OK) {
 		if (data->func != NULL) {
 			(*data->func) ((GthViewerPage *) data->self,
 				       data->file_data,
@@ -832,19 +819,13 @@ save_as_response_cb (GtkDialog  *file_sel,
 		return;
 	}
 
-	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_sel));
-	n_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), filename);
-	g_free (filename);
-
-	if ((n_format < 1) || (n_format > g_list_length (supported_formats)))
+	if (! gth_file_chooser_dialog_get_file (GTH_FILE_CHOOSER_DIALOG (file_sel), &file, &mime_type))
 		return;
 
-	format = g_list_nth_data (supported_formats, n_format - 1);
-	file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (file_sel));
 	gth_file_data_set_file (data->file_data, file);
 	_gth_image_viewer_page_real_save ((GthViewerPage *) data->self,
 					  file,
-					  format->type,
+					  mime_type,
 					  data->func,
 					  data->user_data);
 	gtk_widget_destroy (GTK_WIDGET (data->file_sel));
@@ -854,140 +835,29 @@ save_as_response_cb (GtkDialog  *file_sel,
 
 
 static void
-format_chooser_selection_changed_cb (EggFileFormatChooser *self,
-				     SaveAsData           *data)
-{
-	char   *filename;
-	int     n_format;
-	Format *format;
-	char   *basename;
-	char   *basename_noext;
-	char   *new_basename;
-
-	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (data->file_sel));
-	n_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), filename);
-
-	if ((n_format < 1) || (n_format > g_list_length (supported_formats))) {
-		g_free (filename);
-		return;
-	}
-
-	format = g_list_nth_data (supported_formats, n_format - 1);
-	basename = g_path_get_basename (filename);
-	basename_noext = _g_uri_remove_extension (basename);
-	new_basename = g_strconcat (basename_noext, ".", format->default_ext, NULL);
-	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->file_sel), new_basename);
-
-	g_free (new_basename);
-	g_free (basename_noext);
-	g_free (basename);
-	g_free (filename);
-}
-
-
-static char *
-get_icon_name_for_type (const char *mime_type)
-{
-	char *name = NULL;
-
-	if (mime_type != NULL) {
-		char *s;
-
-		name = g_strconcat ("gnome-mime-", mime_type, NULL);
-		for (s = name; *s; ++s)
-			if (! g_ascii_isalpha (*s))
-				*s = '-';
-	}
-
-	if ((name == NULL) || ! gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name)) {
-		g_free (name);
-		name = g_strdup ("image-x-generic");
-	}
-
-	return name;
-}
-
-
-static void
 gth_image_viewer_page_real_save_as (GthViewerPage *base,
 				    FileSavedFunc  func,
 				    gpointer       user_data)
 {
-	GthImageViewerPage   *self;
-	GtkWidget            *file_sel;
-	char                 *uri;
-	EggFileFormatChooser *format_chooser;
-	SaveAsData           *data;
-	GList                *scan;
+	GthImageViewerPage *self;
+	GtkWidget          *file_sel;
+	char               *uri;
+	SaveAsData         *data;
 
 	self = GTH_IMAGE_VIEWER_PAGE (base);
-	file_sel = gtk_file_chooser_dialog_new (_("Save Image"),
+	file_sel = gth_file_chooser_dialog_new (_("Save Image"),
 						GTK_WINDOW (self->priv->browser),
-						GTK_FILE_CHOOSER_ACTION_SAVE,
-						GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-						GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-						NULL);
-	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE);
-	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_sel), TRUE);
-	gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_ACCEPT);
+						"pixbuf-saver");
+
 	uri = g_file_get_uri (self->priv->file_data->file);
 	gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_sel), uri);
 
-	/**/
-
-	if (supported_formats == NULL) {
-		GArray *savers;
-		int     i;
-
-		savers = gth_main_get_type_set ("pixbuf-saver");
-		for (i = 0; (savers != NULL) && (i < savers->len); i++) {
-			GthPixbufSaver *pixbuf_saver;
-			Format         *format;
-
-			pixbuf_saver = g_object_new (g_array_index (savers, GType, i), NULL);
-			format = g_new (Format, 1);
-			format->type = g_strdup (gth_pixbuf_saver_get_mime_type (pixbuf_saver));
-			format->extensions = g_strdup (gth_pixbuf_saver_get_extensions (pixbuf_saver));
-			format->default_ext = g_strdup (gth_pixbuf_saver_get_default_ext (pixbuf_saver));
-			supported_formats = g_list_prepend (supported_formats, format);
-
-			g_object_unref (pixbuf_saver);
-		}
-	}
-
-	format_chooser = (EggFileFormatChooser *) egg_file_format_chooser_new ();
-	for (scan = supported_formats; scan != NULL; scan = scan->next) {
-		Format  *format = scan->data;
-		char    *icon_name;
-		char   **extensions;
-
-		icon_name = get_icon_name_for_type (format->type);
-		extensions = g_strsplit (format->extensions, " ", -1);
-		egg_file_format_chooser_add_format (format_chooser,
-						    0,
-						    g_content_type_get_description (format->type),
-						    icon_name,
-						    extensions[0],
-						    extensions[1],
-						    extensions[2],
-						    NULL);
-
-		g_strfreev (extensions);
-		g_free (icon_name);
-	}
-
-	gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_sel),
-					   GTK_WIDGET (format_chooser));
-
-	/**/
-
 	data = g_new0 (SaveAsData, 1);
 	data->self = self;
 	data->func = func;
 	data->file_data = gth_file_data_dup (self->priv->file_data);
 	data->user_data = user_data;
 	data->file_sel = file_sel;
-	data->format_chooser = (GtkWidget *) format_chooser;
 
 	g_signal_connect (GTK_DIALOG (file_sel),
 			  "response",
@@ -997,10 +867,6 @@ gth_image_viewer_page_real_save_as (GthViewerPage *base,
 			  "destroy",
 			  G_CALLBACK (save_as_destroy_cb),
 			  data);
-	g_signal_connect (G_OBJECT (format_chooser),
-			  "selection-changed",
-			  G_CALLBACK (format_chooser_selection_changed_cb),
-			  data);
 
 	gtk_window_set_transient_for (GTK_WINDOW (file_sel), GTK_WINDOW (self->priv->browser));
 	gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE);
diff --git a/extensions/image_viewer/preferences.h b/extensions/image_viewer/preferences.h
index 50c976f..c9d878d 100644
--- a/extensions/image_viewer/preferences.h
+++ b/extensions/image_viewer/preferences.h
@@ -37,4 +37,4 @@ void image_viewer__dlg_preferences_construct_cb (GtkWidget  *dialog,
 						 GthBrowser *browser,
 						 GtkBuilder *builder);
 
-#endif /* CALLBACKS_H */
+#endif /* PREFERENCES_H */
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index 388a701..9987074 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -40,6 +40,7 @@ PUBLIC_HEADER_FILES = 					\
 	gth-embedded-dialog.h				\
 	gth-empty-list.h				\
 	gth-extensions.h				\
+	gth-file-chooser-dialog.h			\
 	gth-error.h					\
 	gth-file-data.h					\
 	gth-file-list.h					\
@@ -153,6 +154,7 @@ gthumb_SOURCES = 					\
 	gth-empty-list.c				\
 	gth-error.c					\
 	gth-extensions.c				\
+	gth-file-chooser-dialog.c			\
 	gth-file-data.c					\
 	gth-file-list.c					\
 	gth-file-properties.c				\
diff --git a/gthumb/glib-utils.c b/gthumb/glib-utils.c
index 5d25efc..6b19b66 100644
--- a/gthumb/glib-utils.c
+++ b/gthumb/glib-utils.c
@@ -887,6 +887,25 @@ _g_utf8_all_spaces (const char *utf8_string)
 }
 
 
+char *
+_g_utf8_remove_extension (const char *str)
+{
+	char *p;
+	char *ext;
+	char *dest;
+
+	if ((str == NULL) || ! g_utf8_validate (str, -1, NULL))
+		return NULL;
+
+	p = (char *) str;
+	ext = g_utf8_strrchr (p, -1, g_utf8_get_char ("."));
+	dest = g_strdup (p);
+	g_utf8_strncpy (dest, p, g_utf8_strlen (p, -1) - g_utf8_strlen (ext, -1));
+
+	return dest;
+}
+
+
 GList *
 _g_list_insert_list_before (GList *list1,
 			    GList *sibling,
@@ -926,6 +945,7 @@ _g_list_insert_list_before (GList *list1,
 
 
 GHashTable *static_strings = NULL;
+static GStaticMutex static_strings_mutex = G_STATIC_MUTEX_INIT;
 
 
 const char *
@@ -936,6 +956,8 @@ get_static_string (const char *s)
 	if (s == NULL)
 		return NULL;
 
+	g_static_mutex_lock (&static_strings_mutex);
+
 	if (static_strings == NULL)
 		static_strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
@@ -946,6 +968,8 @@ get_static_string (const char *s)
 				     GINT_TO_POINTER (1));
 	}
 
+	g_static_mutex_unlock (&static_strings_mutex);
+
 	return result;
 }
 
diff --git a/gthumb/glib-utils.h b/gthumb/glib-utils.h
index d79a6e5..f0ed738 100644
--- a/gthumb/glib-utils.h
+++ b/gthumb/glib-utils.h
@@ -166,6 +166,7 @@ char **         _g_utf8_strsplit                 (const char *string,
 						  int         max_tokens);
 char *          _g_utf8_strstrip                 (const char  *str);
 gboolean        _g_utf8_all_spaces               (const char  *utf8_string);
+char *          _g_utf8_remove_extension         (const char  *str);
 GList *         _g_list_insert_list_before       (GList       *list1,
 						  GList       *sibling,
 						  GList       *list2);
diff --git a/gthumb/gth-file-chooser-dialog.c b/gthumb/gth-file-chooser-dialog.c
new file mode 100644
index 0000000..159c65a
--- /dev/null
+++ b/gthumb/gth-file-chooser-dialog.c
@@ -0,0 +1,276 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 The 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 <stdlib.h>
+#include <gtk/gtk.h>
+#include "eggfileformatchooser.h"
+#include "glib-utils.h"
+#include "gth-file-chooser-dialog.h"
+#include "gth-main.h"
+
+
+typedef struct {
+	const char *type;
+	const char *extensions;
+	const char *default_ext;
+} Format;
+
+
+static gpointer parent_class = NULL;
+
+
+struct _GthFileChooserDialogPrivate {
+	GList                *supported_formats;
+	EggFileFormatChooser *format_chooser;
+};
+
+
+static void
+gth_file_chooser_dialog_finalize (GObject *object)
+{
+	GthFileChooserDialog *self;
+
+	self = GTH_FILE_CHOOSER_DIALOG (object);
+
+	g_list_foreach (self->priv->supported_formats, (GFunc) g_free, NULL);
+	g_list_free (self->priv->supported_formats);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_file_chooser_dialog_class_init (GthFileChooserDialogClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (GthFileChooserDialogPrivate));
+
+	object_class = (GObjectClass*) class;
+	object_class->finalize = gth_file_chooser_dialog_finalize;
+}
+
+
+static void
+gth_file_chooser_dialog_init (GthFileChooserDialog *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILE_CHOOSER_DIALOG, GthFileChooserDialogPrivate);
+	self->priv->supported_formats = NULL;
+}
+
+
+GType
+gth_file_chooser_dialog_get_type (void)
+{
+        static GType type = 0;
+
+        if (! type) {
+                GTypeInfo type_info = {
+			sizeof (GthFileChooserDialogClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_file_chooser_dialog_class_init,
+			NULL,
+			NULL,
+			sizeof (GthFileChooserDialog),
+			0,
+			(GInstanceInitFunc) gth_file_chooser_dialog_init
+		};
+
+		type = g_type_register_static (GTK_TYPE_FILE_CHOOSER_DIALOG,
+					       "GthFileChooserDialog",
+					       &type_info,
+					       0);
+	}
+
+        return type;
+}
+
+
+static void
+format_chooser_selection_changed_cb (EggFileFormatChooser *format_chooser,
+				     GthFileChooserDialog *self)
+{
+	char   *filename;
+	int     n_format;
+	Format *format;
+	char   *basename;
+	char   *basename_noext;
+	char   *new_basename;
+
+	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (self));
+	if (filename == NULL)
+		return;
+
+	n_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (self->priv->format_chooser), filename);
+
+	if ((n_format < 1) || (n_format > g_list_length (self->priv->supported_formats))) {
+		g_free (filename);
+		return;
+	}
+
+	format = g_list_nth_data (self->priv->supported_formats, n_format - 1);
+	basename = g_path_get_basename (filename);
+	basename_noext = _g_uri_remove_extension (basename);
+	new_basename = g_strconcat (basename_noext, ".", format->default_ext, NULL);
+	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (self), new_basename);
+
+	g_free (new_basename);
+	g_free (basename_noext);
+	g_free (basename);
+	g_free (filename);
+}
+
+
+static char *
+get_icon_name_for_type (const char *mime_type)
+{
+	char *name = NULL;
+
+	if (mime_type != NULL) {
+		char *s;
+
+		name = g_strconcat ("gnome-mime-", mime_type, NULL);
+		for (s = name; *s; ++s)
+			if (! g_ascii_isalpha (*s))
+				*s = '-';
+	}
+
+	if ((name == NULL) || ! gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name)) {
+		g_free (name);
+		name = g_strdup ("image-x-generic");
+	}
+
+	return name;
+}
+
+
+static void
+gth_file_chooser_dialog_construct (GthFileChooserDialog *self,
+				   const char           *title,
+			           GtkWindow            *parent,
+				   const char           *allowed_savers)
+{
+	GArray *savers;
+	int     i;
+	GList  *scan;
+
+	if (title != NULL)
+    		gtk_window_set_title (GTK_WINDOW (self), title);
+  	if (parent != NULL)
+    		gtk_window_set_transient_for (GTK_WINDOW (self), parent);
+
+  	gtk_file_chooser_set_action (GTK_FILE_CHOOSER (self), GTK_FILE_CHOOSER_ACTION_SAVE);
+  	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (self), FALSE);
+	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (self), TRUE);
+	gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
+
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_SAVE, GTK_RESPONSE_OK);
+
+	/**/
+
+	savers = gth_main_get_type_set (allowed_savers /*"pixbuf-saver"*/);
+	for (i = 0; (savers != NULL) && (i < savers->len); i++) {
+		GthPixbufSaver *saver;
+		Format         *format;
+
+		saver = g_object_new (g_array_index (savers, GType, i), NULL);
+		format = g_new (Format, 1);
+		format->type = get_static_string (gth_pixbuf_saver_get_mime_type (saver));
+		format->extensions = get_static_string (gth_pixbuf_saver_get_extensions (saver));
+		format->default_ext = get_static_string (gth_pixbuf_saver_get_default_ext (saver));
+		self->priv->supported_formats = g_list_prepend (self->priv->supported_formats, format);
+
+		g_object_unref (saver);
+	}
+
+	self->priv->format_chooser = (EggFileFormatChooser *) egg_file_format_chooser_new ();
+	for (scan = self->priv->supported_formats; scan != NULL; scan = scan->next) {
+		Format  *format = scan->data;
+		char    *icon_name;
+		char   **extensions;
+
+		icon_name = get_icon_name_for_type (format->type);
+		extensions = g_strsplit (format->extensions, " ", -1);
+		egg_file_format_chooser_add_format (self->priv->format_chooser,
+						    0,
+						    g_content_type_get_description (format->type),
+						    icon_name,
+						    extensions[0],
+						    extensions[1],
+						    extensions[2],
+						    NULL);
+
+		g_strfreev (extensions);
+		g_free (icon_name);
+	}
+
+	gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (self), GTK_WIDGET (self->priv->format_chooser));
+
+	g_signal_connect (G_OBJECT (self->priv->format_chooser),
+			  "selection-changed",
+			  G_CALLBACK (format_chooser_selection_changed_cb),
+			  self);
+}
+
+
+GtkWidget *
+gth_file_chooser_dialog_new (const char *title,
+			     GtkWindow  *parent,
+			     const char *allowed_savers)
+{
+	GthFileChooserDialog *self;
+
+	self = g_object_new (GTH_TYPE_FILE_CHOOSER_DIALOG, NULL);
+	gth_file_chooser_dialog_construct (self, title, parent, allowed_savers);
+
+	return (GtkWidget *) self;
+}
+
+
+gboolean
+gth_file_chooser_dialog_get_file (GthFileChooserDialog  *self,
+				  GFile                **file,
+				  const char           **mime_type)
+{
+	char   *filename;
+	int     n_format;
+	Format *format;
+
+	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (self));
+	n_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (self->priv->format_chooser), filename);
+	g_free (filename);
+
+	if ((n_format < 1) || (n_format > g_list_length (self->priv->supported_formats)))
+		return FALSE;
+
+	format = g_list_nth_data (self->priv->supported_formats, n_format - 1);
+	if (file != NULL)
+		*file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (self));
+	if (mime_type != NULL)
+		*mime_type = format->type;
+
+	return TRUE;
+}
diff --git a/gthumb/gth-file-chooser-dialog.h b/gthumb/gth-file-chooser-dialog.h
new file mode 100644
index 0000000..8a2e971
--- /dev/null
+++ b/gthumb/gth-file-chooser-dialog.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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_FILE_CHOOSER_DIALOG_H
+#define GTH_FILE_CHOOSER_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_FILE_CHOOSER_DIALOG         (gth_file_chooser_dialog_get_type ())
+#define GTH_FILE_CHOOSER_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_FILE_CHOOSER_DIALOG, GthFileChooserDialog))
+#define GTH_FILE_CHOOSER_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTH_TYPE_FILE_CHOOSER_DIALOG, GthFileChooserDialogClass))
+#define GTH_IS_FILE_CHOOSER_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTH_TYPE_FILE_CHOOSER_DIALOG))
+#define GTH_IS_FILE_CHOOSER_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTH_TYPE_FILE_CHOOSER_DIALOG))
+#define GTH_FILE_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTH_TYPE_FILE_CHOOSER_DIALOG, GthFileChooserDialogClass))
+
+typedef struct _GthFileChooserDialog         GthFileChooserDialog;
+typedef struct _GthFileChooserDialogPrivate  GthFileChooserDialogPrivate;
+typedef struct _GthFileChooserDialogClass    GthFileChooserDialogClass;
+
+struct _GthFileChooserDialog {
+	GtkFileChooserDialog __parent;
+	GthFileChooserDialogPrivate *priv;
+};
+
+struct _GthFileChooserDialogClass {
+	GtkFileChooserDialogClass __parent_class;
+};
+
+GType         gth_file_chooser_dialog_get_type  (void) G_GNUC_CONST;
+GtkWidget *   gth_file_chooser_dialog_new       (const char            *title,
+						 GtkWindow             *parent,
+						 const char            *allowed_savers);
+gboolean      gth_file_chooser_dialog_get_file  (GthFileChooserDialog  *self,
+						 GFile                **file,
+						 const char           **mime_type);
+
+G_END_DECLS
+
+#endif /* GTH_FILE_CHOOSER_DIALOG_H */



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