diff options
Diffstat (limited to 'src/eom-file-chooser.c')
-rw-r--r-- | src/eom-file-chooser.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/src/eom-file-chooser.c b/src/eom-file-chooser.c new file mode 100644 index 0000000..83a6fe2 --- /dev/null +++ b/src/eom-file-chooser.c @@ -0,0 +1,497 @@ +/* + * 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 Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eom-file-chooser.h" +#include "eom-config-keys.h" +#include "eom-pixbuf-util.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> + +/* We must define MATE_DESKTOP_USE_UNSTABLE_API to be able + to use MateDesktopThumbnail */ +#ifndef MATE_DESKTOP_USE_UNSTABLE_API +#define MATE_DESKTOP_USE_UNSTABLE_API +#endif +#include <libmateui/mate-desktop-thumbnail.h> + +static char *last_dir[] = { NULL, NULL, NULL, NULL }; + +#define FILE_FORMAT_KEY "file-format" + +#define EOM_FILE_CHOOSER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ + EOM_TYPE_FILE_CHOOSER, \ + EomFileChooserPrivate)) + +struct _EomFileChooserPrivate +{ + MateDesktopThumbnailFactory *thumb_factory; + + GtkWidget *image; + GtkWidget *size_label; + GtkWidget *dim_label; + GtkWidget *creator_label; +}; + +G_DEFINE_TYPE(EomFileChooser, eom_file_chooser, GTK_TYPE_FILE_CHOOSER_DIALOG) + +static void +eom_file_chooser_finalize (GObject *object) +{ + EomFileChooserPrivate *priv; + + priv = EOM_FILE_CHOOSER (object)->priv; + + if (priv->thumb_factory != NULL) + g_object_unref (priv->thumb_factory); + + (* G_OBJECT_CLASS (eom_file_chooser_parent_class)->finalize) (object); +} + +static void +eom_file_chooser_class_init (EomFileChooserClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->finalize = eom_file_chooser_finalize; + + g_type_class_add_private (object_class, sizeof (EomFileChooserPrivate)); +} + +static void +eom_file_chooser_init (EomFileChooser *chooser) +{ + chooser->priv = EOM_FILE_CHOOSER_GET_PRIVATE (chooser); +} + +static void +response_cb (GtkDialog *dlg, gint id, gpointer data) +{ + char *dir; + GtkFileChooserAction action; + + if (id == GTK_RESPONSE_OK) { + dir = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg)); + action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dlg)); + + if (last_dir [action] != NULL) + g_free (last_dir [action]); + + last_dir [action] = dir; + } +} + +static void +save_response_cb (GtkDialog *dlg, gint id, gpointer data) +{ + GFile *file; + GdkPixbufFormat *format; + + if (id != GTK_RESPONSE_OK) + return; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dlg)); + format = eom_pixbuf_get_format (file); + g_object_unref (file); + + if (!format || !gdk_pixbuf_format_is_writable (format)) { + GtkWidget *msg_dialog; + + msg_dialog = gtk_message_dialog_new ( + GTK_WINDOW (dlg), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("File format is unknown or unsupported")); + + gtk_message_dialog_format_secondary_text ( + GTK_MESSAGE_DIALOG (msg_dialog), + "%s\n%s", + _("Eye of MATE could not determine a supported writable file format based on the filename."), + _("Please try a different file extension like .png or .jpg.")); + + gtk_dialog_run (GTK_DIALOG (msg_dialog)); + gtk_widget_destroy (msg_dialog); + + g_signal_stop_emission_by_name (dlg, "response"); + } else { + response_cb (dlg, id, data); + } +} + +static void +eom_file_chooser_add_filter (EomFileChooser *chooser) +{ + GSList *it; + GSList *formats; + GtkFileFilter *all_file_filter; + GtkFileFilter *all_img_filter; + GtkFileFilter *filter; + GSList *filters = NULL; + gchar **mime_types, **pattern, *tmp; + int i; + GtkFileChooserAction action; + + action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (chooser)); + + if (action != GTK_FILE_CHOOSER_ACTION_SAVE && action != GTK_FILE_CHOOSER_ACTION_OPEN) { + return; + } + + /* All Files Filter */ + all_file_filter = gtk_file_filter_new (); + gtk_file_filter_set_name (all_file_filter, _("All Files")); + gtk_file_filter_add_pattern (all_file_filter, "*"); + + /* All Image Filter */ + all_img_filter = gtk_file_filter_new (); + gtk_file_filter_set_name (all_img_filter, _("All Images")); + + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) { + formats = eom_pixbuf_get_savable_formats (); + } + else { + formats = gdk_pixbuf_get_formats (); + } + + /* Image filters */ + for (it = formats; it != NULL; it = it->next) { + char *filter_name; + char *description, *extension; + GdkPixbufFormat *format; + filter = gtk_file_filter_new (); + + format = (GdkPixbufFormat*) it->data; + description = gdk_pixbuf_format_get_description (format); + extension = gdk_pixbuf_format_get_name (format); + + /* Filter name: First description then file extension, eg. "The PNG-Format (*.png)".*/ + filter_name = g_strdup_printf (_("%s (*.%s)"), description, extension); + g_free (description); + g_free (extension); + + gtk_file_filter_set_name (filter, filter_name); + g_free (filter_name); + + mime_types = gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) it->data); + for (i = 0; mime_types[i] != NULL; i++) { + gtk_file_filter_add_mime_type (filter, mime_types[i]); + gtk_file_filter_add_mime_type (all_img_filter, mime_types[i]); + } + g_strfreev (mime_types); + + pattern = gdk_pixbuf_format_get_extensions ((GdkPixbufFormat *) it->data); + for (i = 0; pattern[i] != NULL; i++) { + tmp = g_strconcat ("*.", pattern[i], NULL); + gtk_file_filter_add_pattern (filter, tmp); + gtk_file_filter_add_pattern (all_img_filter, tmp); + g_free (tmp); + } + g_strfreev (pattern); + + /* attach GdkPixbufFormat to filter, see also + * eom_file_chooser_get_format. */ + g_object_set_data (G_OBJECT (filter), + FILE_FORMAT_KEY, + format); + + filters = g_slist_prepend (filters, filter); + } + g_slist_free (formats); + + /* Add filter to filechooser */ + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), all_file_filter); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), all_img_filter); + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser), all_img_filter); + + for (it = filters; it != NULL; it = it->next) { + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), GTK_FILE_FILTER (it->data)); + } + g_slist_free (filters); +} + +static void +set_preview_label (GtkWidget *label, const char *str) +{ + if (str == NULL) { + gtk_widget_hide (GTK_WIDGET (label)); + } + else { + gtk_label_set_text (GTK_LABEL (label), str); + gtk_widget_show (GTK_WIDGET (label)); + } +} + +/* Sets the pixbuf as preview thumbnail and tries to read and display + * further information according to the thumbnail spec. + */ +static void +set_preview_pixbuf (EomFileChooser *chooser, GdkPixbuf *pixbuf, goffset size) +{ + EomFileChooserPrivate *priv; + int bytes; + int pixels; + const char *bytes_str; + const char *width; + const char *height; + const char *creator = NULL; + char *size_str = NULL; + char *dim_str = NULL; + + g_return_if_fail (EOM_IS_FILE_CHOOSER (chooser)); + + priv = chooser->priv; + + gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf); + + if (pixbuf != NULL) { + /* try to read file size */ + bytes_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Size"); + if (bytes_str != NULL) { + bytes = atoi (bytes_str); + size_str = g_format_size_for_display (bytes); + } + else { + size_str = g_format_size_for_display (size); + } + + /* try to read image dimensions */ + width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width"); + height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height"); + + if ((width != NULL) && (height != NULL)) { + pixels = atoi (height); + /* Pixel size of image: width x height in pixel */ + dim_str = g_strdup_printf ("%s x %s %s", width, height, ngettext ("pixel", "pixels", pixels)); + } + +#if 0 + /* Not sure, if this is really useful, therefore its commented out for now. */ + + /* try to read creator of the thumbnail */ + creator = gdk_pixbuf_get_option (pixbuf, "tEXt::Software"); + + /* stupid workaround to display nicer string if the + * thumbnail is created through the mate libraries. + */ + if (g_ascii_strcasecmp (creator, "Mate::ThumbnailFactory") == 0) { + creator = "MATE Libs"; + } +#endif + } + + set_preview_label (priv->size_label, size_str); + set_preview_label (priv->dim_label, dim_str); + set_preview_label (priv->creator_label, creator); + + if (size_str != NULL) { + g_free (size_str); + } + + if (dim_str != NULL) { + g_free (dim_str); + } +} + +static void +update_preview_cb (GtkFileChooser *file_chooser, gpointer data) +{ + EomFileChooserPrivate *priv; + char *uri; + char *thumb_path = NULL; + GFile *file; + GFileInfo *file_info; + GdkPixbuf *pixbuf = NULL; + gboolean have_preview = FALSE; + + priv = EOM_FILE_CHOOSER (file_chooser)->priv; + + uri = gtk_file_chooser_get_preview_uri (file_chooser); + if (uri == NULL) { + gtk_file_chooser_set_preview_widget_active (file_chooser, FALSE); + return; + } + + file = g_file_new_for_uri (uri); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_MODIFIED "," + G_FILE_ATTRIBUTE_STANDARD_SIZE, + 0, NULL, NULL); + g_object_unref (file); + + if ((file_info != NULL) && (priv->thumb_factory != NULL)) { + guint64 mtime; + + mtime = g_file_info_get_attribute_uint64 (file_info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + thumb_path = mate_desktop_thumbnail_factory_lookup (priv->thumb_factory, uri, mtime); + if (thumb_path == NULL) { + /* read files smaller than 100kb directly */ + if (g_file_info_get_size (file_info) <= 100000) { + /* FIXME: we should then output also the image dimensions */ + thumb_path = gtk_file_chooser_get_preview_filename (file_chooser); + } + } + + if (thumb_path != NULL && g_file_test (thumb_path, G_FILE_TEST_EXISTS)) { + /* try to load and display preview thumbnail */ + pixbuf = gdk_pixbuf_new_from_file (thumb_path, NULL); + + have_preview = (pixbuf != NULL); + + set_preview_pixbuf (EOM_FILE_CHOOSER (file_chooser), pixbuf, + g_file_info_get_size (file_info)); + + if (pixbuf != NULL) { + g_object_unref (pixbuf); + } + } + } + + if (thumb_path != NULL) { + g_free (thumb_path); + } + + g_free (uri); + g_object_unref (file_info); + + gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview); +} + +static void +eom_file_chooser_add_preview (GtkWidget *widget) +{ + EomFileChooserPrivate *priv; + GtkWidget *vbox; + + priv = EOM_FILE_CHOOSER (widget)->priv; + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + + priv->image = gtk_image_new (); + /* 128x128 is maximum size of thumbnails */ + gtk_widget_set_size_request (priv->image, 128,128); + + priv->dim_label = gtk_label_new (NULL); + priv->size_label = gtk_label_new (NULL); + priv->creator_label = gtk_label_new (NULL); + + gtk_box_pack_start (GTK_BOX (vbox), priv->image, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), priv->dim_label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), priv->size_label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), priv->creator_label, FALSE, TRUE, 0); + + gtk_widget_show_all (vbox); + + gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (widget), vbox); + gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (widget), FALSE); + + priv->thumb_factory = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + + g_signal_connect (widget, "update-preview", + G_CALLBACK (update_preview_cb), NULL); +} + +GtkWidget * +eom_file_chooser_new (GtkFileChooserAction action) +{ + GtkWidget *chooser; + gchar *title = NULL; + + chooser = g_object_new (EOM_TYPE_FILE_CHOOSER, + "action", action, + "select-multiple", (action == GTK_FILE_CHOOSER_ACTION_OPEN), + "local-only", FALSE, + NULL); + + switch (action) { + case GTK_FILE_CHOOSER_ACTION_OPEN: + gtk_dialog_add_buttons (GTK_DIALOG (chooser), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + title = _("Open Image"); + break; + + case GTK_FILE_CHOOSER_ACTION_SAVE: + gtk_dialog_add_buttons (GTK_DIALOG (chooser), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + title = _("Save Image"); + break; + + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + gtk_dialog_add_buttons (GTK_DIALOG (chooser), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + title = _("Open Folder"); + break; + + default: + g_assert_not_reached (); + } + + if (action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { + eom_file_chooser_add_filter (EOM_FILE_CHOOSER (chooser)); + eom_file_chooser_add_preview (chooser); + } + + if (last_dir[action] != NULL) { + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), last_dir [action]); + } + + g_signal_connect (chooser, "response", + G_CALLBACK ((action == GTK_FILE_CHOOSER_ACTION_SAVE) ? + save_response_cb : response_cb), + NULL); + + gtk_window_set_title (GTK_WINDOW (chooser), title); + gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (chooser), TRUE); + + return chooser; +} + +GdkPixbufFormat * +eom_file_chooser_get_format (EomFileChooser *chooser) +{ + GtkFileFilter *filter; + GdkPixbufFormat* format; + + g_return_val_if_fail (EOM_IS_FILE_CHOOSER (chooser), NULL); + + filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (chooser)); + if (filter == NULL) + return NULL; + + format = g_object_get_data (G_OBJECT (filter), FILE_FORMAT_KEY); + + return format; +} |