1 /*
2  * gtkhtml-image-chooser-dialog.c
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of version 2 of the GNU Lesser General Public
6  * License as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 02111-1307, USA.
17  */
18 
19 #include "gtkhtml-image-chooser-dialog.h"
20 
21 #define PREVIEW_WIDTH	256
22 #define PREVIEW_HEIGHT	256
23 
24 typedef struct _Context Context;
25 
26 struct _GtkhtmlImageChooserDialogPrivate {
27 	GCancellable *cancellable;
28 };
29 
30 struct _Context {
31 	GtkFileChooser *file_chooser;
32 	GCancellable *cancellable;
33 };
34 
G_DEFINE_TYPE(GtkhtmlImageChooserDialog,gtkhtml_image_chooser_dialog,GTK_TYPE_FILE_CHOOSER_DIALOG)35 G_DEFINE_TYPE (
36 	GtkhtmlImageChooserDialog,
37 	gtkhtml_image_chooser_dialog,
38 	GTK_TYPE_FILE_CHOOSER_DIALOG)
39 
40 static void
41 context_free (Context *context)
42 {
43 	g_object_unref (context->file_chooser);
44 	g_object_unref (context->cancellable);
45 
46 	g_slice_free (Context, context);
47 }
48 
49 static void
image_chooser_dialog_read_cb(GFile * preview_file,GAsyncResult * result,Context * context)50 image_chooser_dialog_read_cb (GFile *preview_file,
51                               GAsyncResult *result,
52                               Context *context)
53 {
54 	GdkPixbuf *pixbuf;
55 	GtkWidget *preview_widget;
56 	GFileInputStream *input_stream;
57 
58 	input_stream = g_file_read_finish (preview_file, result, NULL);
59 
60 	/* FIXME Handle errors better, but remember
61 	 *       to ignore G_IO_ERROR_CANCELLED. */
62 	if (input_stream == NULL)
63 		goto exit;
64 
65 	/* XXX This blocks, but GDK-PixBuf offers no asynchronous
66 	 *     alternative and I don't want to deal with making GDK
67 	 *     calls from threads and all the crap that goes with it. */
68 	pixbuf = gdk_pixbuf_new_from_stream_at_scale (
69 		G_INPUT_STREAM (input_stream),
70 		PREVIEW_WIDTH, PREVIEW_HEIGHT, TRUE,
71 		context->cancellable, NULL);
72 
73 	preview_widget = gtk_file_chooser_get_preview_widget (
74 		context->file_chooser);
75 
76 	gtk_file_chooser_set_preview_widget_active (
77 		context->file_chooser, pixbuf != NULL);
78 
79 	gtk_image_set_from_pixbuf (GTK_IMAGE (preview_widget), pixbuf);
80 
81 	if (pixbuf != NULL)
82 		g_object_unref (pixbuf);
83 
84 	g_object_unref (input_stream);
85 
86 exit:
87 	context_free (context);
88 }
89 
90 static void
image_chooser_dialog_update_preview(GtkFileChooser * file_chooser)91 image_chooser_dialog_update_preview (GtkFileChooser *file_chooser)
92 {
93 	GtkhtmlImageChooserDialogPrivate *priv;
94 	GtkWidget *preview_widget;
95 	GFile *preview_file;
96 	Context *context;
97 
98 	priv = GTKHTML_IMAGE_CHOOSER_DIALOG (file_chooser)->priv;
99 	preview_file = gtk_file_chooser_get_preview_file (file_chooser);
100 	preview_widget = gtk_file_chooser_get_preview_widget (file_chooser);
101 
102 	if (priv->cancellable != NULL) {
103 		g_cancellable_cancel (priv->cancellable);
104 		g_object_unref (priv->cancellable);
105 		priv->cancellable = NULL;
106 	}
107 
108 	gtk_image_clear (GTK_IMAGE (preview_widget));
109 	gtk_file_chooser_set_preview_widget_active (file_chooser, FALSE);
110 
111 	if (preview_file == NULL)
112 		return;
113 
114 	priv->cancellable = g_cancellable_new ();
115 
116 	context = g_slice_new0 (Context);
117 	context->file_chooser = g_object_ref (file_chooser);
118 	context->cancellable = g_object_ref (priv->cancellable);
119 
120 	g_file_read_async (
121 		preview_file, G_PRIORITY_LOW,
122 		priv->cancellable, (GAsyncReadyCallback)
123 		image_chooser_dialog_read_cb, context);
124 
125 	g_object_unref (preview_file);
126 }
127 
128 static void
image_chooser_dialog_dispose(GObject * object)129 image_chooser_dialog_dispose (GObject *object)
130 {
131 	GtkhtmlImageChooserDialogPrivate *priv;
132 
133 	priv = GTKHTML_IMAGE_CHOOSER_DIALOG (object)->priv;
134 
135 	if (priv->cancellable != NULL) {
136 		g_cancellable_cancel (priv->cancellable);
137 		g_object_unref (priv->cancellable);
138 		priv->cancellable = NULL;
139 	}
140 
141 	/* Chain up to parent's dispose() method. */
142 	G_OBJECT_CLASS (gtkhtml_image_chooser_dialog_parent_class)->
143 		dispose (object);
144 }
145 
146 static void
image_chooser_dialog_constructed(GObject * object)147 image_chooser_dialog_constructed (GObject *object)
148 {
149 	GtkFileChooser *file_chooser;
150 	GtkFileFilter *file_filter;
151 
152 	/* Chain up to parent's method. */
153 	G_OBJECT_CLASS (gtkhtml_image_chooser_dialog_parent_class)->constructed (object);
154 
155 	file_chooser = GTK_FILE_CHOOSER (object);
156 	gtk_file_chooser_set_local_only (file_chooser, FALSE);
157 
158 	gtk_dialog_add_button (
159 		GTK_DIALOG (file_chooser),
160 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
161 	gtk_dialog_add_button (
162 		GTK_DIALOG (file_chooser),
163 		GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT);
164 	gtk_dialog_set_default_response (
165 		GTK_DIALOG (file_chooser), GTK_RESPONSE_ACCEPT);
166 
167 	file_filter = gtk_file_filter_new ();
168 	gtk_file_filter_add_pixbuf_formats (file_filter);
169 	gtk_file_chooser_set_filter (file_chooser, file_filter);
170 
171 	gtk_file_chooser_set_preview_widget (file_chooser, gtk_image_new ());
172 }
173 
174 static void
gtkhtml_image_chooser_dialog_class_init(GtkhtmlImageChooserDialogClass * class)175 gtkhtml_image_chooser_dialog_class_init (GtkhtmlImageChooserDialogClass *class)
176 {
177 	GObjectClass *object_class;
178 
179 	g_type_class_add_private (
180 		class, sizeof (GtkhtmlImageChooserDialogPrivate));
181 
182 	object_class = G_OBJECT_CLASS (class);
183 	object_class->dispose = image_chooser_dialog_dispose;
184 	object_class->constructed = image_chooser_dialog_constructed;
185 }
186 
187 static void
gtkhtml_image_chooser_dialog_init(GtkhtmlImageChooserDialog * dialog)188 gtkhtml_image_chooser_dialog_init (GtkhtmlImageChooserDialog *dialog)
189 {
190 	dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (
191 		dialog, GTKHTML_TYPE_IMAGE_CHOOSER_DIALOG,
192 		GtkhtmlImageChooserDialogPrivate);
193 
194 	g_signal_connect (
195 		dialog, "update-preview",
196 		G_CALLBACK (image_chooser_dialog_update_preview), NULL);
197 }
198 
199 GtkWidget *
gtkhtml_image_chooser_dialog_new(const gchar * title,GtkWindow * parent)200 gtkhtml_image_chooser_dialog_new (const gchar *title,
201                                   GtkWindow *parent)
202 {
203 	return g_object_new (
204 		GTKHTML_TYPE_IMAGE_CHOOSER_DIALOG,
205 		"action", GTK_FILE_CHOOSER_ACTION_OPEN,
206 		"title", title,
207 		"transient-for", parent, NULL);
208 }
209 
210 GFile *
gtkhtml_image_chooser_dialog_run(GtkhtmlImageChooserDialog * dialog)211 gtkhtml_image_chooser_dialog_run (GtkhtmlImageChooserDialog *dialog)
212 {
213 	GtkFileChooser *file_chooser;
214 
215 	g_return_val_if_fail (GTKHTML_IS_IMAGE_CHOOSER_DIALOG (dialog), NULL);
216 
217 	file_chooser = GTK_FILE_CHOOSER (dialog);
218 
219 	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
220 		return NULL;
221 
222 	return gtk_file_chooser_get_file (file_chooser);
223 }
224 
225