Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--configure.ac1
-rw-r--r--cut-n-paste/Makefile.am2
-rw-r--r--cut-n-paste/fileformatchooser/Makefile.am11
-rw-r--r--cut-n-paste/fileformatchooser/egg-macros.h154
-rw-r--r--cut-n-paste/fileformatchooser/eggfileformatchooser.c1190
-rw-r--r--cut-n-paste/fileformatchooser/eggfileformatchooser.h84
-rw-r--r--shell/Makefile.am2
-rw-r--r--shell/ev-window.c34
9 files changed, 1481 insertions, 11 deletions
diff --git a/ChangeLog b/ChangeLog
index 2f91134..ebf2d24 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2007-12-30 Carl-Anton Ingmarsson <c-a.ingmarsson@oktv.se>
+
+ * configure.ac:
+ * cut-n-paste/Makefile.am:
+ * cut-n-paste/fileformatchooser/Makefile.am:
+ * cut-n-paste/fileformatchooser/egg-macros.h:
+ * cut-n-paste/fileformatchooser/eggfileformatchooser.[ch]:
+ * shell/Makefile.am:
+ * shell/ev-window.c: (image_save_dialog_response_cb),
+ (ev_view_popup_cmd_save_image_as):
+
+ Allow exporting images in any format supported by
+ GdkPixbuf. Fixes bug #500209.
+
2007-12-29 Carlos Garcia Campos <carlosgc@gnome.org>
* backend/pixbuf/pixbuf-document.c:
diff --git a/configure.ac b/configure.ac
index 906c2d1..7c50c96 100644
--- a/configure.ac
+++ b/configure.ac
@@ -463,6 +463,7 @@ cut-n-paste/gedit-message-area/Makefile
cut-n-paste/toolbar-editor/Makefile
cut-n-paste/zoom-control/Makefile
cut-n-paste/totem-screensaver/Makefile
+cut-n-paste/fileformatchooser/Makefile
data/evince.desktop.in
data/Makefile
data/icons/Makefile
diff --git a/cut-n-paste/Makefile.am b/cut-n-paste/Makefile.am
index 3c5609c..c90ce7f 100644
--- a/cut-n-paste/Makefile.am
+++ b/cut-n-paste/Makefile.am
@@ -1 +1 @@
-SUBDIRS = zoom-control toolbar-editor totem-screensaver gedit-message-area
+SUBDIRS = zoom-control toolbar-editor totem-screensaver gedit-message-area fileformatchooser
diff --git a/cut-n-paste/fileformatchooser/Makefile.am b/cut-n-paste/fileformatchooser/Makefile.am
new file mode 100644
index 0000000..ef481e6
--- /dev/null
+++ b/cut-n-paste/fileformatchooser/Makefile.am
@@ -0,0 +1,11 @@
+noinst_LTLIBRARIES = libfileformatchooser.la
+libfileformatchooser_la_SOURCES = \
+ eggfileformatchooser.c \
+ eggfileformatchooser.h \
+ egg-macros.h
+
+libfileformatchooser_la_CFLAGS = \
+ -I$(top_srcdir)/lib \
+ $(LIB_CFLAGS) \
+ $(WARNING_CFLAGS) \
+ $(DISABLE_DEPRECATED)
diff --git a/cut-n-paste/fileformatchooser/egg-macros.h b/cut-n-paste/fileformatchooser/egg-macros.h
new file mode 100644
index 0000000..2b5718e
--- /dev/null
+++ b/cut-n-paste/fileformatchooser/egg-macros.h
@@ -0,0 +1,154 @@
+/**
+ * Useful macros.
+ *
+ * Author:
+ * Darin Adler <darin@bentspoon.com>
+ *
+ * Copyright 2001 Ben Tea Spoons, Inc.
+ */
+#ifndef _EGG_MACROS_H_
+#define _EGG_MACROS_H_
+
+#include <glib/gmacros.h>
+
+G_BEGIN_DECLS
+
+/* Macros for defining classes. Ideas taken from Nautilus and GOB. */
+
+/* Define the boilerplate type stuff to reduce typos and code size. Defines
+ * the get_type method and the parent_class static variable. */
+
+#define EGG_BOILERPLATE(type, type_as_function, corba_type, \
+ parent_type, parent_type_macro, \
+ register_type_macro) \
+static void type_as_function ## _class_init (type ## Class *klass); \
+static void type_as_function ## _instance_init (type *object); \
+static parent_type ## Class *parent_class = NULL; \
+static void \
+type_as_function ## _class_init_trampoline (gpointer klass, \
+ gpointer data) \
+{ \
+ parent_class = (parent_type ## Class *)g_type_class_ref ( \
+ parent_type_macro); \
+ type_as_function ## _class_init ((type ## Class *)klass); \
+} \
+GType \
+type_as_function ## _get_type (void) \
+{ \
+ static GType object_type = 0; \
+ if (object_type == 0) { \
+ static const GTypeInfo object_info = { \
+ sizeof (type ## Class), \
+ NULL, /* base_init */ \
+ NULL, /* base_finalize */ \
+ type_as_function ## _class_init_trampoline, \
+ NULL, /* class_finalize */ \
+ NULL, /* class_data */ \
+ sizeof (type), \
+ 0, /* n_preallocs */ \
+ (GInstanceInitFunc) type_as_function ## _instance_init \
+ }; \
+ object_type = register_type_macro \
+ (type, type_as_function, corba_type, \
+ parent_type, parent_type_macro); \
+ } \
+ return object_type; \
+}
+
+/* Just call the parent handler. This assumes that there is a variable
+ * named parent_class that points to the (duh!) parent class. Note that
+ * this macro is not to be used with things that return something, use
+ * the _WITH_DEFAULT version for that */
+#define EGG_CALL_PARENT(parent_class_cast, name, args) \
+ ((parent_class_cast(parent_class)->name != NULL) ? \
+ parent_class_cast(parent_class)->name args : (void)0)
+
+/* Same as above, but in case there is no implementation, it evaluates
+ * to def_return */
+#define EGG_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \
+ name, args, def_return) \
+ ((parent_class_cast(parent_class)->name != NULL) ? \
+ parent_class_cast(parent_class)->name args : def_return)
+
+/* Call a virtual method */
+#define EGG_CALL_VIRTUAL(object, get_class_cast, method, args) \
+ (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0)
+
+/* Call a virtual method with default */
+#define EGG_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \
+ (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default)
+
+#define EGG_CLASS_BOILERPLATE(type, type_as_function, \
+ parent_type, parent_type_macro) \
+ EGG_BOILERPLATE(type, type_as_function, type, \
+ parent_type, parent_type_macro, \
+ EGG_REGISTER_TYPE)
+
+#define EGG_REGISTER_TYPE(type, type_as_function, corba_type, \
+ parent_type, parent_type_macro) \
+ g_type_register_static (parent_type_macro, #type, &object_info, 0)
+
+
+#define EGG_DEFINE_BOXED_TYPE(TN, t_n) \
+EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, {});
+
+#define EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, _C_) \
+\
+static gpointer t_n##_copy (gpointer boxed); \
+static void t_n##_free (gpointer boxed); \
+\
+EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, t_n##_copy, t_n##_free, _C_);
+
+#define EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, b_c, b_f, _C_) \
+\
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TN, t_n, b_c, b_f) {_C_;} \
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_END()
+
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TypeName, type_name, boxed_copy, boxed_free) \
+\
+GType \
+type_name##_get_type (void) \
+{ \
+ static volatile gsize g_define_type_id__volatile = 0; \
+ if (g_once_init_enter (&g_define_type_id__volatile)) \
+ { \
+ GType g_define_type_id = \
+ g_boxed_type_register_static (g_intern_static_string (#TypeName), \
+ boxed_copy, boxed_free); \
+ { /* custom code follows */
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_END() \
+ /* following custom code */ \
+ } \
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
+ } \
+ return g_define_type_id__volatile; \
+} /* closes type_name##_get_type() */
+
+#define EGG_DEFINE_QUARK(QN, q_n) \
+\
+GQuark \
+q_n##_quark (void) \
+{ \
+ static volatile gsize g_define_quark__volatile = 0; \
+ if (g_once_init_enter (&g_define_quark__volatile)) \
+ { \
+ GQuark g_define_quark = g_quark_from_string (#QN); \
+ g_once_init_leave (&g_define_quark__volatile, g_define_quark); \
+ } \
+ return g_define_quark__volatile; \
+}
+
+#define EGG_IS_POSITIVE_RESPONSE(response_id) \
+ ((response_id) == GTK_RESPONSE_ACCEPT || \
+ (response_id) == GTK_RESPONSE_OK || \
+ (response_id) == GTK_RESPONSE_YES || \
+ (response_id) == GTK_RESPONSE_APPLY)
+
+#define EGG_IS_NEGATIVE_RESPONSE(response_id) \
+ ((response_id) == GTK_RESPONSE_REJECT || \
+ (response_id) == GTK_RESPONSE_CANCEL || \
+ (response_id) == GTK_RESPONSE_NO)
+
+G_END_DECLS
+
+#endif /* _EGG_MACROS_H_ */
diff --git a/cut-n-paste/fileformatchooser/eggfileformatchooser.c b/cut-n-paste/fileformatchooser/eggfileformatchooser.c
new file mode 100644
index 0000000..5eeecdb
--- /dev/null
+++ b/cut-n-paste/fileformatchooser/eggfileformatchooser.c
@@ -0,0 +1,1190 @@
+/* EggFileFormatChooser
+ * Copyright (C) 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "eggfileformatchooser.h"
+#include "egg-macros.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct _EggFileFormatFilterInfo EggFileFormatFilterInfo;
+typedef struct _EggFileFormatSearch EggFileFormatSearch;
+
+enum
+{
+ MODEL_COLUMN_ID,
+ MODEL_COLUMN_NAME,
+ MODEL_COLUMN_ICON,
+ MODEL_COLUMN_EXTENSIONS,
+ MODEL_COLUMN_FILTER,
+ MODEL_COLUMN_DATA,
+ MODEL_COLUMN_DESTROY
+};
+
+enum
+{
+ SIGNAL_SELECTION_CHANGED,
+ SIGNAL_LAST
+};
+
+struct _EggFileFormatChooserPrivate
+{
+ GtkTreeStore *model;
+ GtkTreeSelection *selection;
+ guint idle_hack;
+ guint last_id;
+
+ GtkFileChooser *chooser;
+ GtkFileFilter *all_files;
+ GtkFileFilter *supported_files;
+};
+
+struct _EggFileFormatFilterInfo
+{
+ GHashTable *extension_set;
+ GSList *extension_list;
+ gboolean show_extensions;
+ gchar *name;
+};
+
+struct _EggFileFormatSearch
+{
+ gboolean success;
+ GtkTreeIter iter;
+
+ guint format;
+ const gchar *extension;
+};
+
+static guint signals[SIGNAL_LAST];
+
+G_DEFINE_TYPE (EggFileFormatChooser,
+ egg_file_format_chooser,
+ GTK_TYPE_EXPANDER);
+EGG_DEFINE_QUARK (EggFileFormatFilterInfo,
+ egg_file_format_filter_info);
+
+static EggFileFormatFilterInfo*
+egg_file_format_filter_info_new (const gchar *name,
+ gboolean show_extensions)
+{
+ EggFileFormatFilterInfo *self;
+
+ self = g_new0 (EggFileFormatFilterInfo, 1);
+ self->extension_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ self->show_extensions = show_extensions;
+ self->name = g_strdup (name);
+
+ return self;
+}
+
+static void
+egg_file_format_filter_info_free (gpointer boxed)
+{
+ EggFileFormatFilterInfo *self;
+
+ if (boxed)
+ {
+ self = boxed;
+
+ g_hash_table_unref (self->extension_set);
+ g_slist_foreach (self->extension_list, (GFunc) g_free, NULL);
+ g_slist_free (self->extension_list);
+ g_free (self->name);
+ g_free (self);
+ }
+}
+
+static gboolean
+egg_file_format_filter_find (gpointer key,
+ gpointer value G_GNUC_UNUSED,
+ gpointer data)
+{
+ const GtkFileFilterInfo *info = data;
+ const gchar *pattern = key;
+
+ return g_str_has_suffix (info->filename, pattern + 1);
+}
+
+static gboolean
+egg_file_format_filter_filter (const GtkFileFilterInfo *info,
+ gpointer data)
+{
+ EggFileFormatFilterInfo *self = data;
+
+ return NULL != g_hash_table_find (self->extension_set,
+ egg_file_format_filter_find,
+ (gpointer) info);
+}
+
+static GtkFileFilter*
+egg_file_format_filter_new (const gchar *name,
+ gboolean show_extensions)
+{
+ GtkFileFilter *filter;
+ EggFileFormatFilterInfo *info;
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, name);
+
+ info = egg_file_format_filter_info_new (name, show_extensions);
+
+ gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME,
+ egg_file_format_filter_filter,
+ info, NULL);
+ g_object_set_qdata_full (G_OBJECT (filter),
+ egg_file_format_filter_info_quark (),
+ info, egg_file_format_filter_info_free);
+
+ return filter;
+}
+
+static void
+egg_file_format_filter_add_extensions (GtkFileFilter *filter,
+ const gchar *extensions)
+{
+ EggFileFormatFilterInfo *info;
+ GString *filter_name;
+ const gchar *extptr;
+ gchar *pattern;
+ gsize length;
+
+ g_assert (NULL != extensions);
+
+ info = g_object_get_qdata (G_OBJECT (filter),
+ egg_file_format_filter_info_quark ());
+
+ info->extension_list = g_slist_prepend (info->extension_list,
+ g_strdup (extensions));
+
+ if (info->show_extensions)
+ {
+ filter_name = g_string_new (info->name);
+ g_string_append (filter_name, " (");
+ }
+ else
+ filter_name = NULL;
+
+ extptr = extensions;
+ while (*extptr)
+ {
+ length = strcspn (extptr, ",");
+ pattern = g_new (gchar, length + 3);
+
+ memcpy (pattern, "*.", 2);
+ memcpy (pattern + 2, extptr, length);
+ pattern[length + 2] = '\0';
+
+ if (filter_name)
+ {
+ if (extptr != extensions)
+ g_string_append (filter_name, ", ");
+
+ g_string_append (filter_name, pattern);
+ }
+
+ extptr += length;
+
+ if (*extptr)
+ extptr += 2;
+
+ g_hash_table_replace (info->extension_set, pattern, pattern);
+ }
+
+ if (filter_name)
+ {
+ g_string_append (filter_name, ")");
+ gtk_file_filter_set_name (filter, filter_name->str);
+ g_string_free (filter_name, TRUE);
+ }
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+ EggFileFormatChooser *self)
+{
+ gchar *label;
+ gchar *name;
+
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+ GtkTreeIter parent;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_NAME, &name, -1);
+
+ label = g_strdup_printf (_("File _Format: %s"), name);
+ gtk_expander_set_use_underline (GTK_EXPANDER (self), TRUE);
+ gtk_expander_set_label (GTK_EXPANDER (self), label);
+
+ g_free (name);
+ g_free (label);
+
+ if (self->priv->chooser)
+ {
+ while (gtk_tree_model_iter_parent (model, &parent, &iter))
+ iter = parent;
+
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+ gtk_file_chooser_set_filter (self->priv->chooser, filter);
+ g_object_unref (filter);
+ }
+
+ g_signal_emit (self, signals[SIGNAL_SELECTION_CHANGED], 0);
+ }
+}
+
+/* XXX This hack is needed, as gtk_expander_set_label seems
+ * not to work from egg_file_format_chooser_init */
+static gboolean
+select_default_file_format (gpointer data)
+{
+ EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (data);
+ egg_file_format_chooser_set_format (self, 0);
+ self->priv->idle_hack = 0;
+ return FALSE;
+}
+
+static gboolean
+find_by_format (GtkTreeModel *model,
+ GtkTreePath *path G_GNUC_UNUSED,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ EggFileFormatSearch *search = data;
+ guint id;
+
+ gtk_tree_model_get (model, iter, MODEL_COLUMN_ID, &id, -1);
+
+ if (id == search->format)
+ {
+ search->success = TRUE;
+ search->iter = *iter;
+ }
+
+ return search->success;
+}
+
+static gboolean
+find_in_list (gchar *list,
+ const gchar *needle)
+{
+ gchar *saveptr;
+ gchar *token;
+
+ for (token = strtok_r (list, ",", &saveptr); NULL != token;
+ token = strtok_r (NULL, ",", &saveptr))
+ {
+ token = g_strstrip (token);
+
+ if (g_str_equal (needle, token))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+accept_filename (gchar *extensions,
+ const gchar *filename)
+{
+ const gchar *extptr;
+ gchar *saveptr;
+ gchar *token;
+ gsize length;
+
+ length = strlen (filename);
+
+ for (token = strtok_r (extensions, ",", &saveptr); NULL != token;
+ token = strtok_r (NULL, ",", &saveptr))
+ {
+ token = g_strstrip (token);
+ extptr = filename + length - strlen (token) - 1;
+
+ if (extptr > filename && '.' == *extptr &&
+ !strcmp (extptr + 1, token))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+find_by_extension (GtkTreeModel *model,
+ GtkTreePath *path G_GNUC_UNUSED,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ EggFileFormatSearch *search = data;
+
+ gchar *extensions = NULL;
+ guint format = 0;
+
+ gtk_tree_model_get (model, iter,
+ MODEL_COLUMN_EXTENSIONS, &extensions,
+ MODEL_COLUMN_ID, &format,
+ -1);
+
+ if (extensions && find_in_list (extensions, search->extension))
+ {
+ search->format = format;
+ search->success = TRUE;
+ search->iter = *iter;
+ }
+
+ g_free (extensions);
+ return search->success;
+}
+
+static void
+egg_file_format_chooser_init (EggFileFormatChooser *self)
+{
+ GtkWidget *scroller;
+ GtkWidget *view;
+
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+ GtkTreeIter iter;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EGG_TYPE_FILE_FORMAT_CHOOSER,
+ EggFileFormatChooserPrivate);
+
+/* file filters */
+
+ self->priv->all_files = g_object_ref_sink (gtk_file_filter_new ());
+ gtk_file_filter_set_name (self->priv->all_files, _("All Files"));
+ self->priv->supported_files = egg_file_format_filter_new (_("All Supported Files"), FALSE);
+
+/* tree model */
+
+ self->priv->model = gtk_tree_store_new (7, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ GTK_TYPE_FILE_FILTER, G_TYPE_POINTER, G_TYPE_POINTER);
+
+ gtk_tree_store_append (self->priv->model, &iter, NULL);
+ gtk_tree_store_set (self->priv->model, &iter,
+ MODEL_COLUMN_NAME, _("By Extension"),
+ MODEL_COLUMN_FILTER, self->priv->supported_files,
+ MODEL_COLUMN_ID, 0,
+ -1);
+
+/* tree view */
+
+ view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->priv->model));
+ self->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
+
+/* file format column */
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_column_set_title (column, _("File Format"));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "icon-name", MODEL_COLUMN_ICON,
+ NULL);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", MODEL_COLUMN_NAME,
+ NULL);
+
+/* extensions column */
+
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Extension(s)"), gtk_cell_renderer_text_new (),
+ "text", MODEL_COLUMN_EXTENSIONS, NULL);
+ gtk_tree_view_column_set_expand (column, FALSE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+/* selection */
+
+ gtk_tree_selection_set_mode (self->priv->selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (self->priv->selection, "changed",
+ G_CALLBACK (selection_changed_cb), self);
+ self->priv->idle_hack = g_idle_add (select_default_file_format, self);
+
+/* scroller */
+
+ scroller = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
+ GTK_SHADOW_IN);
+ gtk_widget_set_size_request (scroller, -1, 150);
+ gtk_container_add (GTK_CONTAINER (scroller), view);
+ gtk_widget_show_all (scroller);
+
+ gtk_container_add (GTK_CONTAINER (self), scroller);
+}
+
+static void
+reset_model (EggFileFormatChooser *self)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (self->priv->model);
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ GDestroyNotify destroy = NULL;
+ gpointer data = NULL;
+
+ gtk_tree_model_get (model, &iter,
+ MODEL_COLUMN_DESTROY, &destroy,
+ MODEL_COLUMN_DATA, &data,
+ -1);
+
+ if (destroy)
+ destroy (data);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ gtk_tree_store_clear (self->priv->model);
+}
+
+static void
+egg_file_format_chooser_dispose (GObject *obj)
+{
+ EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj);
+
+ if (NULL != self)
+ {
+ if (self->priv->idle_hack)
+ {
+ g_source_remove (self->priv->idle_hack);
+ self->priv->idle_hack = 0;
+ }
+ }
+
+ G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->dispose (obj);
+}
+
+static void
+egg_file_format_chooser_finalize (GObject *obj)
+{
+ EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj);
+
+ if (NULL != self)
+ {
+ if (self->priv->model)
+ {
+ reset_model (self);
+
+ g_object_unref (self->priv->model);
+ self->priv->model = NULL;
+
+ g_object_unref (self->priv->all_files);
+ self->priv->all_files = NULL;
+ }
+ }
+
+ G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->finalize (obj);
+}
+
+static void
+filter_changed_cb (GObject *object,
+ GParamSpec *spec,
+ gpointer data)
+{
+ EggFileFormatChooser *self;
+
+ GtkFileFilter *current_filter;
+ GtkFileFilter *format_filter;
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeIter parent;
+
+ self = EGG_FILE_FORMAT_CHOOSER (data);
+
+ format_filter = NULL;
+ current_filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (object));
+ model = GTK_TREE_MODEL (self->priv->model);
+
+ if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter))
+ {
+ while (gtk_tree_model_iter_parent (model, &parent, &iter))
+ iter = parent;
+
+ gtk_tree_model_get (model, &iter,
+ MODEL_COLUMN_FILTER,
+ &format_filter, -1);
+ g_object_unref (format_filter);
+ }
+
+ if (current_filter && current_filter != format_filter &&
+ gtk_tree_model_get_iter_first (model, &iter))
+ {
+ if (current_filter == self->priv->all_files)
+ format_filter = current_filter;
+ else
+ {
+ format_filter = NULL;
+
+ do
+ {
+ gtk_tree_model_get (model, &iter,
+ MODEL_COLUMN_FILTER,
+ &format_filter, -1);
+ g_object_unref (format_filter);
+
+ if (format_filter == current_filter)
+ break;
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ if (format_filter)
+ gtk_tree_selection_select_iter (self->priv->selection, &iter);
+ }
+}
+
+/* Shows an error dialog set as transient for the specified window */
+static void
+error_message_with_parent (GtkWindow *parent,
+ const char *msg,
+ const char *detail)
+{
+ gboolean first_call = TRUE;
+ GtkWidget *dialog;
+
+ if (first_call)
+ {
+ g_warning ("%s: Merge with the code in Gtk{File,Recent}ChooserDefault.", G_STRLOC);
+ first_call = FALSE;
+ }
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ msg);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", detail);
+
+ if (parent->group)
+ gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+/* Returns a toplevel GtkWindow, or NULL if none */
+static GtkWindow *
+get_toplevel (GtkWidget *widget)
+{
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (!GTK_WIDGET_TOPLEVEL (toplevel))
+ return NULL;
+ else
+ return GTK_WINDOW (toplevel);
+}
+
+/* Shows an error dialog for the file chooser */
+static void
+error_message (EggFileFormatChooser *impl,
+ const char *msg,
+ const char *detail)
+{
+ error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
+}
+
+static void
+chooser_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer data)
+{
+ EggFileFormatChooser *self;
+ gchar *filename, *basename;
+ gchar *message;
+ guint format;
+
+ self = EGG_FILE_FORMAT_CHOOSER (data);
+
+ if (EGG_IS_POSITIVE_RESPONSE (response_id))
+ {
+ filename = gtk_file_chooser_get_filename (self->priv->chooser);
+ basename = g_filename_display_basename (filename);
+ g_free (filename);
+
+ format = egg_file_format_chooser_get_format (self, basename);
+ g_print ("%s: %s - %d\n", G_STRFUNC, basename, format);
+
+ if (0 == format)
+ {
+
+ message = g_strdup_printf (
+ _("The program was not able to find out the file format "
+ "you want to use for `%s'. Please make sure to use a "
+ "known extension for that file or manually choose a "
+ "file format from the list below."),
+ basename);
+
+ error_message (self,
+ _("File format not recognized"),
+ message);
+
+ g_free (message);
+
+ g_signal_stop_emission_by_name (dialog, "response");
+ }
+ else
+ {
+ filename = egg_file_format_chooser_append_extension (self, basename, format);
+
+ if (strcmp (filename, basename))
+ {
+ gtk_file_chooser_set_current_name (self->priv->chooser, filename);
+ g_signal_stop_emission_by_name (dialog, "response");
+ }
+
+ g_free (filename);
+ }
+
+ g_free (basename);
+ }
+
+}
+
+static void
+egg_file_format_chooser_realize (GtkWidget *widget)
+{
+ EggFileFormatChooser *self;
+ GtkWidget *parent;
+
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->realize (widget);
+
+ self = EGG_FILE_FORMAT_CHOOSER (widget);
+
+ g_return_if_fail (NULL == self->priv->chooser);
+
+ parent = gtk_widget_get_toplevel (widget);
+
+ if (!GTK_IS_FILE_CHOOSER (parent))
+ parent = gtk_widget_get_parent (widget);
+
+ while (parent && !GTK_IS_FILE_CHOOSER (parent))
+ parent = gtk_widget_get_parent (parent);
+
+ self->priv->chooser = GTK_FILE_CHOOSER (parent);
+
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (self->priv->chooser));
+ g_return_if_fail (gtk_file_chooser_get_action (self->priv->chooser) ==
+ GTK_FILE_CHOOSER_ACTION_SAVE);
+
+ g_object_ref (self->priv->chooser);
+
+ g_signal_connect (self->priv->chooser, "notify::filter",
+ G_CALLBACK (filter_changed_cb), self);
+ gtk_file_chooser_add_filter (self->priv->chooser, self->priv->all_files);
+
+ model = GTK_TREE_MODEL (self->priv->model);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+ gtk_file_chooser_add_filter (self->priv->chooser, filter);
+ g_object_unref (filter);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ gtk_file_chooser_set_filter (self->priv->chooser,
+ self->priv->supported_files);
+
+ if (GTK_IS_DIALOG (self->priv->chooser))
+ g_signal_connect (self->priv->chooser, "response",
+ G_CALLBACK (chooser_response_cb), self);
+}
+
+static void
+egg_file_format_chooser_unrealize (GtkWidget *widget)
+{
+ EggFileFormatChooser *self;
+
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->unrealize (widget);
+
+ self = EGG_FILE_FORMAT_CHOOSER (widget);
+ model = GTK_TREE_MODEL (self->priv->model);
+
+ g_signal_handlers_disconnect_by_func (self->priv->chooser,
+ filter_changed_cb, self);
+ g_signal_handlers_disconnect_by_func (self->priv->chooser,
+ chooser_response_cb, self);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+ gtk_file_chooser_remove_filter (self->priv->chooser, filter);
+ g_object_unref (filter);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ gtk_file_chooser_remove_filter (self->priv->chooser, self->priv->all_files);
+ g_object_unref (self->priv->chooser);
+}
+
+static void
+egg_file_format_chooser_class_init (EggFileFormatChooserClass *cls)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (cls);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (cls);
+
+ g_type_class_add_private (cls, sizeof (EggFileFormatChooserPrivate));
+
+ object_class->dispose = egg_file_format_chooser_dispose;
+ object_class->finalize = egg_file_format_chooser_finalize;
+
+ widget_class->realize = egg_file_format_chooser_realize;
+ widget_class->unrealize = egg_file_format_chooser_unrealize;
+
+ signals[SIGNAL_SELECTION_CHANGED] = g_signal_new (
+ "selection-changed", EGG_TYPE_FILE_FORMAT_CHOOSER, G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EggFileFormatChooserClass, selection_changed),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+GtkWidget*
+egg_file_format_chooser_new (void)
+{
+ return g_object_new (EGG_TYPE_FILE_FORMAT_CHOOSER, NULL);
+}
+
+static guint
+egg_file_format_chooser_add_format_impl (EggFileFormatChooser *self,
+ guint parent,
+ const gchar *name,
+ const gchar *icon,
+ const gchar *extensions)
+{
+ EggFileFormatSearch search;
+ GtkFileFilter *filter;
+ GtkTreeIter iter;
+
+ search.success = FALSE;
+ search.format = parent;
+ filter = NULL;
+
+ if (parent > 0)
+ {
+ gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model),
+ find_by_format, &search);
+ g_return_val_if_fail (search.success, -1);
+ }
+ else
+ filter = egg_file_format_filter_new (name, TRUE);
+
+ gtk_tree_store_append (self->priv->model, &iter,
+ parent > 0 ? &search.iter : NULL);
+
+ gtk_tree_store_set (self->priv->model, &iter,
+ MODEL_COLUMN_ID, ++self->priv->last_id,
+ MODEL_COLUMN_EXTENSIONS, extensions,
+ MODEL_COLUMN_FILTER, filter,
+ MODEL_COLUMN_NAME, name,
+ MODEL_COLUMN_ICON, icon,
+ -1);
+
+ if (extensions)
+ {
+ if (parent > 0)
+ gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model), &search.iter,
+ MODEL_COLUMN_FILTER, &filter, -1);
+
+ egg_file_format_filter_add_extensions (self->priv->supported_files, extensions);
+ egg_file_format_filter_add_extensions (filter, extensions);
+
+ if (parent > 0)
+ g_object_unref (filter);
+ }
+
+ return self->priv->last_id;
+}
+
+guint
+egg_file_format_chooser_add_format (EggFileFormatChooser *self,
+ guint parent,
+ const gchar *name,
+ const gchar *icon,
+ ...)
+{
+ GString *buffer = NULL;
+ const gchar* extptr;
+ va_list extensions;
+ guint id;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), 0);
+ g_return_val_if_fail (NULL != name, 0);
+
+ va_start (extensions, icon);
+
+ while (NULL != (extptr = va_arg (extensions, const gchar*)))
+ {
+ if (NULL == buffer)
+ buffer = g_string_new (NULL);
+ else
+ g_string_append (buffer, ", ");
+
+ g_string_append (buffer, extptr);
+ }
+
+ va_end (extensions);
+
+ id = egg_file_format_chooser_add_format_impl (self, parent, name, icon,
+ buffer ? buffer->str : NULL);
+
+ if (buffer)
+ g_string_free (buffer, TRUE);
+
+ return id;
+}
+
+static gchar*
+get_icon_name (const gchar *mime_type)
+{
+ static gboolean first_call = TRUE;
+ gchar *name = NULL;
+ gchar *s;
+
+ if (first_call)
+ {
+ g_warning ("%s: Replace by g_content_type_get_icon "
+ "when GVFS is merged into GLib.", G_STRLOC);
+ first_call = FALSE;
+ }
+
+ if (mime_type)
+ {
+ name = g_strconcat ("gnome-mime-", mime_type, NULL);
+
+ for(s = name; *s; ++s)
+ {
+ if (!isalpha (*s) || !isascii (*s))
+ *s = '-';
+ }
+ }
+
+ if (!name ||
+ !gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name))
+ {
+ g_free (name);
+ name = g_strdup ("gnome-mime-image");
+ }
+
+ return name;
+}
+
+void
+egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self,
+ guint parent G_GNUC_UNUSED,
+ guint **formats)
+{
+ GSList *pixbuf_formats = NULL;
+ GSList *iter;
+ gint i;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ pixbuf_formats = gdk_pixbuf_get_formats ();
+
+ if (formats)
+ *formats = g_new0 (guint, g_slist_length (pixbuf_formats) + 1);
+
+ for(iter = pixbuf_formats, i = 0; iter; iter = iter->next, ++i)
+ {
+ GdkPixbufFormat *format = iter->data;
+
+ gchar *description, *name, *extensions, *icon;
+ gchar **mime_types, **extension_list;
+ guint id;
+
+ if (gdk_pixbuf_format_is_disabled (format) ||
+ !gdk_pixbuf_format_is_writable (format))
+ continue;
+
+ mime_types = gdk_pixbuf_format_get_mime_types (format);
+ icon = get_icon_name (mime_types[0]);
+ g_strfreev (mime_types);
+
+ extension_list = gdk_pixbuf_format_get_extensions (format);
+ extensions = g_strjoinv (", ", extension_list);
+ g_strfreev (extension_list);
+
+ description = gdk_pixbuf_format_get_description (format);
+ name = gdk_pixbuf_format_get_name (format);
+
+ id = egg_file_format_chooser_add_format_impl (self, parent, description,
+ icon, extensions);
+
+ g_free (description);
+ g_free (extensions);
+ g_free (icon);
+
+ egg_file_format_chooser_set_format_data (self, id, name, g_free);
+
+ if (formats)
+ *formats[i] = id;
+ }
+
+ g_slist_free (pixbuf_formats);
+}
+
+void
+egg_file_format_chooser_remove_format (EggFileFormatChooser *self,
+ guint format)
+{
+ GDestroyNotify destroy = NULL;
+ gpointer data = NULL;
+
+ EggFileFormatSearch search;
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_if_fail (search.success);
+
+ gtk_tree_model_get (model, &search.iter,
+ MODEL_COLUMN_FILTER, &filter,
+ MODEL_COLUMN_DESTROY, &destroy,
+ MODEL_COLUMN_DATA, &data,
+ -1);
+
+ if (destroy)
+ destroy (data);
+
+ if (filter)
+ {
+ if (self->priv->chooser)
+ gtk_file_chooser_remove_filter (self->priv->chooser, filter);
+
+ g_object_unref (filter);
+ }
+ else
+ g_warning ("TODO: Remove extensions from parent filter");
+
+ gtk_tree_store_remove (self->priv->model, &search.iter);
+}
+
+void
+egg_file_format_chooser_set_format (EggFileFormatChooser *self,
+ guint format)
+{
+ EggFileFormatSearch search;
+
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeView *view;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_if_fail (search.success);
+
+ path = gtk_tree_model_get_path (model, &search.iter);
+ view = gtk_tree_selection_get_tree_view (self->priv->selection);
+
+ gtk_tree_view_expand_to_path (view, path);
+ gtk_tree_selection_unselect_all (self->priv->selection);
+ gtk_tree_selection_select_path (self->priv->selection, path);
+
+ gtk_tree_path_free (path);
+
+ if (self->priv->idle_hack > 0)
+ {
+ g_source_remove (self->priv->idle_hack);
+ self->priv->idle_hack = 0;
+ }
+}
+
+guint
+egg_file_format_chooser_get_format (EggFileFormatChooser *self,
+ const gchar *filename)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint format = 0;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), -1);
+
+ if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter))
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_ID, &format, -1);
+
+ if (0 == format && NULL != filename)
+ {
+ EggFileFormatSearch search;
+
+ search.extension = strrchr(filename, '.');
+ search.success = FALSE;
+
+ if (search.extension++)
+ gtk_tree_model_foreach (model, find_by_extension, &search);
+ if (search.success)
+ format = search.format;
+ }
+
+ return format;
+}
+
+void
+egg_file_format_chooser_set_format_data (EggFileFormatChooser *self,
+ guint format,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ EggFileFormatSearch search;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ search.success = FALSE;
+ search.format = format;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model),
+ find_by_format, &search);
+
+ g_return_if_fail (search.success);
+
+ gtk_tree_store_set (self->priv->model, &search.iter,
+ MODEL_COLUMN_DESTROY, destroy,
+ MODEL_COLUMN_DATA, data,
+ -1);
+}
+
+gpointer
+egg_file_format_chooser_get_format_data (EggFileFormatChooser *self,
+ guint format)
+{
+ EggFileFormatSearch search;
+ gpointer data = NULL;
+ GtkTreeModel *model;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL);
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_val_if_fail (search.success, NULL);
+
+ gtk_tree_model_get (model, &search.iter,
+ MODEL_COLUMN_DATA, &data,
+ -1);
+ return data;
+}
+
+gchar*
+egg_file_format_chooser_append_extension (EggFileFormatChooser *self,
+ const gchar *filename,
+ guint format)
+{
+ EggFileFormatSearch search;
+ GtkTreeModel *model;
+ GtkTreeIter child;
+
+ gchar *extensions;
+ gchar *result;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL);
+ g_return_val_if_fail (NULL != filename, NULL);
+
+ if (0 == format)
+ format = egg_file_format_chooser_get_format (self, NULL);
+
+ if (0 == format)
+ {
+ g_warning ("%s: No file format selected. Cannot append extension.", __FUNCTION__);
+ return NULL;
+ }
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_val_if_fail (search.success, NULL);
+
+ gtk_tree_model_get (model, &search.iter,
+ MODEL_COLUMN_EXTENSIONS, &extensions,
+ -1);
+
+ if (NULL == extensions &&
+ gtk_tree_model_iter_nth_child (model, &child, &search.iter, 0))
+ {
+ gtk_tree_model_get (model, &child,
+ MODEL_COLUMN_EXTENSIONS, &extensions,
+ -1);
+ }
+
+ if (NULL == extensions)
+ {
+ g_warning ("%s: File format %d doesn't provide file extensions. "
+ "Cannot append extension.", __FUNCTION__, format);
+ return NULL;
+ }
+
+ if (accept_filename (extensions, filename))
+ result = g_strdup (filename);
+ else
+ result = g_strconcat (filename, ".", extensions, NULL);
+
+ g_assert (NULL == strchr(extensions, ','));
+ g_free (extensions);
+ return result;
+}
+
+/* vim: set sw=2 sta et: */
diff --git a/cut-n-paste/fileformatchooser/eggfileformatchooser.h b/cut-n-paste/fileformatchooser/eggfileformatchooser.h
new file mode 100644
index 0000000..e533980
--- /dev/null
+++ b/cut-n-paste/fileformatchooser/eggfileformatchooser.h
@@ -0,0 +1,84 @@
+/* EggFileFormatChooser
+ * Copyright (C) 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __EGG_FILE_FORMAT_CHOOSER_H__
+#define __EGG_FILE_FORMAT_CHOOSER_H__
+
+#include <gtk/gtkexpander.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_FILE_FORMAT_CHOOSER (egg_file_format_chooser_get_type())
+#define EGG_FILE_FORMAT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooser))
+#define EGG_FILE_FORMAT_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass))
+#define EGG_IS_FILE_FORMAT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER))
+#define EGG_IS_FILE_FORMAT_CHOOSER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER))
+#define EGG_FILE_FORMAT_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass))
+
+typedef struct _EggFileFormatChooser EggFileFormatChooser;
+typedef struct _EggFileFormatChooserClass EggFileFormatChooserClass;
+typedef struct _EggFileFormatChooserPrivate EggFileFormatChooserPrivate;
+
+struct _EggFileFormatChooser
+{
+ GtkExpander parent;
+ EggFileFormatChooserPrivate *priv;
+};
+
+struct _EggFileFormatChooserClass
+{
+ GtkExpanderClass parent;
+
+ void (*selection_changed)(EggFileFormatChooser *self);
+};
+
+GType egg_file_format_chooser_get_type (void) G_GNUC_CONST;
+GtkWidget* egg_file_format_chooser_new (void);
+
+guint egg_file_format_chooser_add_format (EggFileFormatChooser *self,
+ guint parent,
+ const gchar *name,
+ const gchar *icon,
+ ...) G_GNUC_NULL_TERMINATED;
+void egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self,
+ guint parent,
+ guint **formats);
+void egg_file_format_chooser_remove_format (EggFileFormatChooser *self,
+ guint format);
+
+void egg_file_format_chooser_set_format (EggFileFormatChooser *self,
+ guint format);
+guint egg_file_format_chooser_get_format (EggFileFormatChooser *self,
+ const gchar *filename);
+
+void egg_file_format_chooser_set_format_data (EggFileFormatChooser *self,
+ guint format,
+ gpointer data,
+ GDestroyNotify destroy);
+gpointer egg_file_format_chooser_get_format_data (EggFileFormatChooser *self,
+ guint format);
+
+gchar* egg_file_format_chooser_append_extension (EggFileFormatChooser *self,
+ const gchar *filename,
+ guint format);
+
+G_END_DECLS
+
+#endif /* __EGG_FILE_FORMAT_CHOOSER_H__ */
+
+/* vim: set sw=2 sta et: */
diff --git a/shell/Makefile.am b/shell/Makefile.am
index a4e8d1e..8a510b4 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -5,6 +5,7 @@ INCLUDES= \
-I$(top_srcdir)/cut-n-paste/toolbar-editor/ \
-I$(top_srcdir)/cut-n-paste/totem-screensaver/ \
-I$(top_srcdir)/cut-n-paste/gedit-message-area/ \
+ -I$(top_srcdir)/cut-n-paste/fileformatchooser/ \
-I$(top_srcdir)/libdocument \
-I$(top_srcdir)/properties \
-DGNOMELOCALEDIR=\"$(datadir)/locale\" \
@@ -97,6 +98,7 @@ evince_LDADD= \
$(top_builddir)/cut-n-paste/toolbar-editor/libtoolbareditor.la \
$(top_builddir)/cut-n-paste/totem-screensaver/libtotemscrsaver.la \
$(top_builddir)/cut-n-paste/gedit-message-area/libgeditmsgarea.la \
+ $(top_builddir)/cut-n-paste/fileformatchooser/libfileformatchooser.la \
$(top_builddir)/properties/libevproperties.la \
$(top_builddir)/libdocument/libevbackend.la \
$(SHELL_LIBS) \
diff --git a/shell/ev-window.c b/shell/ev-window.c
index bbf9d46..a9d7bdc 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -93,6 +93,8 @@
#include "ev-message-area.h"
+#include "eggfileformatchooser.h"
+
#if !GLIB_CHECK_VERSION (2, 13, 3)
char *xdg_user_dir_lookup (char *type);
#endif
@@ -4974,32 +4976,43 @@ image_save_dialog_response_cb (GtkWidget *fc,
gint response_id,
EvWindow *ev_window)
{
+ GtkWidget *format_chooser;
GnomeVFSURI *target_uri;
gchar *uri;
+ gchar *uri_extension;
gchar *filename;
+ gchar *file_format;
gboolean is_local;
GError *error = NULL;
+ guint format;
if (response_id != GTK_RESPONSE_OK) {
gtk_widget_destroy (fc);
return;
}
+ format_chooser = gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (fc));
+
uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fc));
- target_uri = gnome_vfs_uri_new (uri);
+ format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (format_chooser), uri);
+ uri_extension = egg_file_format_chooser_append_extension (EGG_FILE_FORMAT_CHOOSER (format_chooser),
+ uri, format);
+ file_format = (char *)egg_file_format_chooser_get_format_data (EGG_FILE_FORMAT_CHOOSER (format_chooser),
+ format);
+ target_uri = gnome_vfs_uri_new (uri_extension);
is_local = gnome_vfs_uri_is_local (target_uri);
if (is_local) {
- filename = g_filename_from_uri (uri, NULL, NULL);
+ filename = g_filename_from_uri (uri_extension, NULL, NULL);
} else {
filename = ev_tmp_filename ("saveimage");
}
g_free (uri);
+ g_free (uri_extension);
- /* FIXME: allow saving in other image formats than png */
gdk_pixbuf_save (ev_image_get_pixbuf (ev_window->priv->image),
- filename, "png", &error, NULL);
+ filename, file_format, &error, NULL);
if (error) {
ev_window_error_message (GTK_WINDOW (ev_window),
@@ -5032,7 +5045,7 @@ image_save_dialog_response_cb (GtkWidget *fc,
static void
ev_view_popup_cmd_save_image_as (GtkAction *action, EvWindow *window)
{
- GtkWidget *fc;
+ GtkWidget *fc, *format_chooser;
GtkFileFilter *filter;
if (!window->priv->image)
@@ -5049,11 +5062,12 @@ ev_view_popup_cmd_save_image_as (GtkAction *action, EvWindow *window)
gtk_dialog_set_default_response (GTK_DIALOG (fc), GTK_RESPONSE_OK);
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fc), FALSE);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fc), TRUE);
-
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Images"));
- gtk_file_filter_add_pixbuf_formats (filter);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fc), filter);
+
+ format_chooser = egg_file_format_chooser_new ();
+ egg_file_format_chooser_add_pixbuf_formats (EGG_FILE_FORMAT_CHOOSER (format_chooser),
+ 0, NULL);
+
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (fc), format_chooser);
g_signal_connect (fc, "response",
G_CALLBACK (image_save_dialog_response_cb),