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