/* * e-image-chooser-dialog.c * * Copyright (C) 2012 Dan Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * 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 Lesser 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 #endif #include #include "e-image-chooser-dialog.h" #define E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_IMAGE_CHOOSER_DIALOG, EImageChooserDialogPrivate)) #define PREVIEW_WIDTH 256 #define PREVIEW_HEIGHT 256 typedef struct _Context Context; struct _EImageChooserDialogPrivate { GCancellable *cancellable; }; struct _Context { GtkFileChooser *file_chooser; GCancellable *cancellable; }; G_DEFINE_TYPE ( EImageChooserDialog, e_image_chooser_dialog, GTK_TYPE_FILE_CHOOSER_DIALOG) static void context_free (Context *context) { g_object_unref (context->file_chooser); g_object_unref (context->cancellable); g_slice_free (Context, context); } static void image_chooser_dialog_read_cb (GFile *preview_file, GAsyncResult *result, Context *context) { GdkPixbuf *pixbuf; GtkWidget *preview_widget; GFileInputStream *input_stream; input_stream = g_file_read_finish (preview_file, result, NULL); /* FIXME Handle errors better, but remember * to ignore G_IO_ERROR_CANCELLED. */ if (input_stream == NULL) goto exit; /* XXX This blocks, but GDK-PixBuf offers no asynchronous * alternative and I don't want to deal with making GDK * calls from threads and all the crap that goes with it. */ pixbuf = gdk_pixbuf_new_from_stream_at_scale ( G_INPUT_STREAM (input_stream), PREVIEW_WIDTH, PREVIEW_HEIGHT, TRUE, context->cancellable, NULL); preview_widget = gtk_file_chooser_get_preview_widget ( context->file_chooser); gtk_file_chooser_set_preview_widget_active ( context->file_chooser, pixbuf != NULL); gtk_image_set_from_pixbuf (GTK_IMAGE (preview_widget), pixbuf); if (pixbuf != NULL) g_object_unref (pixbuf); g_object_unref (input_stream); exit: context_free (context); } static void image_chooser_dialog_update_preview (GtkFileChooser *file_chooser) { EImageChooserDialogPrivate *priv; GtkWidget *preview_widget; GFile *preview_file; Context *context; priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (file_chooser); preview_file = gtk_file_chooser_get_preview_file (file_chooser); preview_widget = gtk_file_chooser_get_preview_widget (file_chooser); if (priv->cancellable != NULL) { g_cancellable_cancel (priv->cancellable); g_object_unref (priv->cancellable); priv->cancellable = NULL; } gtk_image_clear (GTK_IMAGE (preview_widget)); gtk_file_chooser_set_preview_widget_active (file_chooser, FALSE); if (preview_file == NULL) return; priv->cancellable = g_cancellable_new (); context = g_slice_new0 (Context); context->file_chooser = g_object_ref (file_chooser); context->cancellable = g_object_ref (priv->cancellable); g_file_read_async ( preview_file, G_PRIORITY_LOW, priv->cancellable, (GAsyncReadyCallback) image_chooser_dialog_read_cb, context); g_object_unref (preview_file); } static void image_chooser_dialog_dispose (GObject *object) { EImageChooserDialogPrivate *priv; priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (object); if (priv->cancellable != NULL) { g_cancellable_cancel (priv->cancellable); g_object_unref (priv->cancellable); priv->cancellable = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_image_chooser_dialog_parent_class)->dispose (object); } static void image_chooser_dialog_constructed (GObject *object) { GtkFileChooser *file_chooser; GtkFileFilter *file_filter; file_chooser = GTK_FILE_CHOOSER (object); gtk_file_chooser_set_local_only (file_chooser, FALSE); gtk_dialog_add_button ( GTK_DIALOG (file_chooser), _("_Cancel"), GTK_RESPONSE_CANCEL); gtk_dialog_add_button ( GTK_DIALOG (file_chooser), _("_Open"), GTK_RESPONSE_ACCEPT); gtk_dialog_set_default_response ( GTK_DIALOG (file_chooser), GTK_RESPONSE_ACCEPT); file_filter = gtk_file_filter_new (); gtk_file_filter_add_pixbuf_formats (file_filter); gtk_file_chooser_set_filter (file_chooser, file_filter); gtk_file_chooser_set_preview_widget (file_chooser, gtk_image_new ()); } static void e_image_chooser_dialog_class_init (EImageChooserDialogClass *class) { GObjectClass *object_class; g_type_class_add_private ( class, sizeof (EImageChooserDialogPrivate)); object_class = G_OBJECT_CLASS (class); object_class->dispose = image_chooser_dialog_dispose; object_class->constructed = image_chooser_dialog_constructed; } static void e_image_chooser_dialog_init (EImageChooserDialog *dialog) { dialog->priv = E_IMAGE_CHOOSER_DIALOG_GET_PRIVATE (dialog); g_signal_connect ( dialog, "update-preview", G_CALLBACK (image_chooser_dialog_update_preview), NULL); } GtkWidget * e_image_chooser_dialog_new (const gchar *title, GtkWindow *parent) { return g_object_new ( E_TYPE_IMAGE_CHOOSER_DIALOG, "action", GTK_FILE_CHOOSER_ACTION_OPEN, "title", title, "transient-for", parent, NULL); } GFile * e_image_chooser_dialog_run (EImageChooserDialog *dialog) { GtkFileChooser *file_chooser; g_return_val_if_fail (E_IS_IMAGE_CHOOSER_DIALOG (dialog), NULL); file_chooser = GTK_FILE_CHOOSER (dialog); if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT) return NULL; return gtk_file_chooser_get_file (file_chooser); }