From ec8b42c7daf3f2727a6d2d517922dfa699b35bbc Mon Sep 17 00:00:00 2001 From: Carl-Anton Ingmarsson Date: Sun, 30 Dec 2007 16:27:31 +0000 Subject: [PATCH] Allow exporting images in any format supported by GdkPixbuf. Fixes bug 2007-12-30 Carl-Anton Ingmarsson * 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. svn path=/trunk/; revision=2791 --- ChangeLog | 14 + configure.ac | 1 + cut-n-paste/Makefile.am | 2 +- cut-n-paste/fileformatchooser/Makefile.am | 11 + cut-n-paste/fileformatchooser/egg-macros.h | 154 +++ .../fileformatchooser/eggfileformatchooser.c | 1190 +++++++++++++++++ .../fileformatchooser/eggfileformatchooser.h | 84 ++ shell/Makefile.am | 2 + shell/ev-window.c | 34 +- 9 files changed, 1481 insertions(+), 11 deletions(-) create mode 100644 cut-n-paste/fileformatchooser/Makefile.am create mode 100644 cut-n-paste/fileformatchooser/egg-macros.h create mode 100644 cut-n-paste/fileformatchooser/eggfileformatchooser.c create mode 100644 cut-n-paste/fileformatchooser/eggfileformatchooser.h diff --git a/ChangeLog b/ChangeLog index 2f91134f..ebf2d24f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-12-30 Carl-Anton Ingmarsson + + * 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 * backend/pixbuf/pixbuf-document.c: diff --git a/configure.ac b/configure.ac index 906c2d1e..7c50c967 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 3c5609cf..c90ce7f9 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 00000000..ef481e63 --- /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 00000000..2b5718ee --- /dev/null +++ b/cut-n-paste/fileformatchooser/egg-macros.h @@ -0,0 +1,154 @@ +/** + * Useful macros. + * + * Author: + * Darin Adler + * + * Copyright 2001 Ben Tea Spoons, Inc. + */ +#ifndef _EGG_MACROS_H_ +#define _EGG_MACROS_H_ + +#include + +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 00000000..5eeecdb2 --- /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 +#include +#include +#include + +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 00000000..e533980e --- /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 + +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 a4e8d1ef..8a510b43 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 bbf9d469..a9d7bdc0 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), -- 2.43.5