1/* Images
2 *
3 * GtkImage is used to display an image; the image can be in a number of formats.
4 * Typically, you load an image into a GdkPixbuf, then display the pixbuf.
5 *
6 * This demo code shows some of the more obscure cases, in the simple
7 * case a call to gtk_image_new_from_file() is all you need.
8 *
9 * If you want to put image data in your program as a C variable,
10 * use the make-inline-pixbuf program that comes with GTK+.
11 * This way you won't need to depend on loading external files, your
12 * application binary can be self-contained.
13 */
14
15#include <gtk/gtk.h>
16#include <glib/gstdio.h>
17#include <stdio.h>
18#include <errno.h>
19
20static GtkWidget *window = NULL;
21static GdkPixbufLoader *pixbuf_loader = NULL;
22static guint load_timeout = 0;
23static GInputStream * image_stream = NULL;
24
25static void
26progressive_prepared_callback (GdkPixbufLoader *loader,
27 gpointer data)
28{
29 GdkPixbuf *pixbuf;
30 GtkWidget *image;
31
32 image = GTK_WIDGET (data);
33
34 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
35
36 /* Avoid displaying random memory contents, since the pixbuf
37 * isn't filled in yet.
38 */
39 gdk_pixbuf_fill (pixbuf, 0xaaaaaaff);
40
41 gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
42}
43
44static void
45progressive_updated_callback (GdkPixbufLoader *loader,
46 gint x,
47 gint y,
48 gint width,
49 gint height,
50 gpointer data)
51{
52 GtkWidget *image;
53 GdkPixbuf *pixbuf;
54
55 image = GTK_WIDGET (data);
56
57 /* We know the pixbuf inside the GtkImage has changed, but the image
58 * itself doesn't know this; so give it a hint by setting the pixbuf
59 * again. Queuing a redraw used to be sufficient, but nowadays GtkImage
60 * uses GtkIconHelper which caches the pixbuf state and will just redraw
61 * from the cache.
62 */
63
64 pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (image));
65 g_object_ref (pixbuf);
66 gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
67 g_object_unref (pixbuf);
68}
69
70static gint
71progressive_timeout (gpointer data)
72{
73 GtkWidget *image;
74
75 image = GTK_WIDGET (data);
76
77 /* This shows off fully-paranoid error handling, so looks scary.
78 * You could factor out the error handling code into a nice separate
79 * function to make things nicer.
80 */
81
82 if (image_stream)
83 {
84 gssize bytes_read;
85 guchar buf[256];
86 GError *error = NULL;
87
88 bytes_read = g_input_stream_read (image_stream, buf, 256, NULL, &error);
89
90 if (bytes_read < 0)
91 {
92 GtkWidget *dialog;
93
94 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
95 GTK_DIALOG_DESTROY_WITH_PARENT,
96 GTK_MESSAGE_ERROR,
97 GTK_BUTTONS_CLOSE,
98 "Failure reading image file 'alphatest.png': %s",
99 error->message);
100 g_error_free (error);
101
102 g_signal_connect (dialog, "response",
103 G_CALLBACK (gtk_widget_destroy), NULL);
104
105 g_object_unref (image_stream);
106 image_stream = NULL;
107
108 gtk_widget_show (dialog);
109
110 load_timeout = 0;
111
112 return FALSE; /* uninstall the timeout */
113 }
114
115 if (!gdk_pixbuf_loader_write (pixbuf_loader,
116 buf, bytes_read,
117 &error))
118 {
119 GtkWidget *dialog;
120
121 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
122 GTK_DIALOG_DESTROY_WITH_PARENT,
123 GTK_MESSAGE_ERROR,
124 GTK_BUTTONS_CLOSE,
125 "Failed to load image: %s",
126 error->message);
127
128 g_error_free (error);
129
130 g_signal_connect (dialog, "response",
131 G_CALLBACK (gtk_widget_destroy), NULL);
132
133 g_object_unref (image_stream);
134 image_stream = NULL;
135
136 gtk_widget_show (dialog);
137
138 load_timeout = 0;
139
140 return FALSE; /* uninstall the timeout */
141 }
142
143 if (bytes_read == 0)
144 {
145 /* Errors can happen on close, e.g. if the image
146 * file was truncated we'll know on close that
147 * it was incomplete.
148 */
149 error = NULL;
150 if (!g_input_stream_close (image_stream, NULL, &error))
151 {
152 GtkWidget *dialog;
153
154 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
155 GTK_DIALOG_DESTROY_WITH_PARENT,
156 GTK_MESSAGE_ERROR,
157 GTK_BUTTONS_CLOSE,
158 "Failed to load image: %s",
159 error->message);
160
161 g_error_free (error);
162
163 g_signal_connect (dialog, "response",
164 G_CALLBACK (gtk_widget_destroy), NULL);
165
166 gtk_widget_show (dialog);
167
168 g_object_unref (image_stream);
169 image_stream = NULL;
170 g_object_unref (pixbuf_loader);
171 pixbuf_loader = NULL;
172
173 load_timeout = 0;
174
175 return FALSE; /* uninstall the timeout */
176 }
177
178 g_object_unref (image_stream);
179 image_stream = NULL;
180
181 /* Errors can happen on close, e.g. if the image
182 * file was truncated we'll know on close that
183 * it was incomplete.
184 */
185 error = NULL;
186 if (!gdk_pixbuf_loader_close (pixbuf_loader,
187 &error))
188 {
189 GtkWidget *dialog;
190
191 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
192 GTK_DIALOG_DESTROY_WITH_PARENT,
193 GTK_MESSAGE_ERROR,
194 GTK_BUTTONS_CLOSE,
195 "Failed to load image: %s",
196 error->message);
197
198 g_error_free (error);
199
200 g_signal_connect (dialog, "response",
201 G_CALLBACK (gtk_widget_destroy), NULL);
202
203 gtk_widget_show (dialog);
204
205 g_object_unref (pixbuf_loader);
206 pixbuf_loader = NULL;
207
208 load_timeout = 0;
209
210 return FALSE; /* uninstall the timeout */
211 }
212
213 g_object_unref (pixbuf_loader);
214 pixbuf_loader = NULL;
215 }
216 }
217 else
218 {
219 GError *error = NULL;
220
221 image_stream = g_resources_open_stream ("/images/alphatest.png", 0, &error);
222
223 if (image_stream == NULL)
224 {
225 GtkWidget *dialog;
226
227 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
228 GTK_DIALOG_DESTROY_WITH_PARENT,
229 GTK_MESSAGE_ERROR,
230 GTK_BUTTONS_CLOSE,
231 "%s", error->message);
232 g_error_free (error);
233
234 g_signal_connect (dialog, "response",
235 G_CALLBACK (gtk_widget_destroy), NULL);
236
237 gtk_widget_show (dialog);
238
239 load_timeout = 0;
240
241 return FALSE; /* uninstall the timeout */
242 }
243
244 if (pixbuf_loader)
245 {
246 gdk_pixbuf_loader_close (pixbuf_loader, NULL);
247 g_object_unref (pixbuf_loader);
248 }
249
250 pixbuf_loader = gdk_pixbuf_loader_new ();
251
252 g_signal_connect (pixbuf_loader, "area-prepared",
253 G_CALLBACK (progressive_prepared_callback), image);
254
255 g_signal_connect (pixbuf_loader, "area-updated",
256 G_CALLBACK (progressive_updated_callback), image);
257 }
258
259 /* leave timeout installed */
260 return TRUE;
261}
262
263static void
264start_progressive_loading (GtkWidget *image)
265{
266 /* This is obviously totally contrived (we slow down loading
267 * on purpose to show how incremental loading works).
268 * The real purpose of incremental loading is the case where
269 * you are reading data from a slow source such as the network.
270 * The timeout simply simulates a slow data source by inserting
271 * pauses in the reading process.
272 */
273 load_timeout = gdk_threads_add_timeout (150,
274 progressive_timeout,
275 image);
276 g_source_set_name_by_id (load_timeout, "[gtk+] progressive_timeout");
277}
278
279static void
280cleanup_callback (GObject *object,
281 gpointer data)
282{
283 if (load_timeout)
284 {
285 g_source_remove (load_timeout);
286 load_timeout = 0;
287 }
288
289 if (pixbuf_loader)
290 {
291 gdk_pixbuf_loader_close (pixbuf_loader, NULL);
292 g_object_unref (pixbuf_loader);
293 pixbuf_loader = NULL;
294 }
295
296 if (image_stream)
297 {
298 g_object_unref (image_stream);
299 image_stream = NULL;
300 }
301}
302
303static void
304toggle_sensitivity_callback (GtkWidget *togglebutton,
305 gpointer user_data)
306{
307 GtkContainer *container = user_data;
308 GList *list;
309 GList *tmp;
310
311 list = gtk_container_get_children (container);
312
313 tmp = list;
314 while (tmp != NULL)
315 {
316 /* don't disable our toggle */
317 if (GTK_WIDGET (tmp->data) != togglebutton)
318 gtk_widget_set_sensitive (GTK_WIDGET (tmp->data),
319 !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton)));
320
321 tmp = tmp->next;
322 }
323
324 g_list_free (list);
325}
326
327
328GtkWidget *
329do_images (GtkWidget *do_widget)
330{
331 GtkWidget *frame;
332 GtkWidget *vbox;
333 GtkWidget *image;
334 GtkWidget *label;
335 GtkWidget *button;
336 GIcon *gicon;
337
338 if (!window)
339 {
340 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
341 gtk_window_set_screen (GTK_WINDOW (window),
342 gtk_widget_get_screen (do_widget));
343 gtk_window_set_title (GTK_WINDOW (window), "Images");
344
345 g_signal_connect (window, "destroy",
346 G_CALLBACK (gtk_widget_destroyed), &window);
347 g_signal_connect (window, "destroy",
348 G_CALLBACK (cleanup_callback), NULL);
349
350 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
351
352 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
353 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
354 gtk_container_add (GTK_CONTAINER (window), vbox);
355
356 label = gtk_label_new (NULL);
357 gtk_label_set_markup (GTK_LABEL (label),
358 "<u>Image loaded from a file</u>");
359 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
360
361 frame = gtk_frame_new (NULL);
362 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
363 gtk_widget_set_halign (frame, GTK_ALIGN_CENTER);
364 gtk_widget_set_valign (frame, GTK_ALIGN_CENTER);
365 gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
366
367 image = gtk_image_new_from_icon_name ("gtk3-demo", GTK_ICON_SIZE_DIALOG);
368
369 gtk_container_add (GTK_CONTAINER (frame), image);
370
371
372 /* Animation */
373
374 label = gtk_label_new (NULL);
375 gtk_label_set_markup (GTK_LABEL (label),
376 "<u>Animation loaded from a file</u>");
377 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
378
379 frame = gtk_frame_new (NULL);
380 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
381 gtk_widget_set_halign (frame, GTK_ALIGN_CENTER);
382 gtk_widget_set_valign (frame, GTK_ALIGN_CENTER);
383 gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
384
385 image = gtk_image_new_from_resource ("/images/floppybuddy.gif");
386
387 gtk_container_add (GTK_CONTAINER (frame), image);
388
389 /* Symbolic icon */
390
391 label = gtk_label_new (NULL);
392 gtk_label_set_markup (GTK_LABEL (label),
393 "<u>Symbolic themed icon</u>");
394 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
395
396 frame = gtk_frame_new (NULL);
397 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
398 gtk_widget_set_halign (frame, GTK_ALIGN_CENTER);
399 gtk_widget_set_valign (frame, GTK_ALIGN_CENTER);
400 gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
401
402 gicon = g_themed_icon_new_with_default_fallbacks ("battery-caution-charging-symbolic");
403 image = gtk_image_new_from_gicon (gicon, GTK_ICON_SIZE_DIALOG);
404
405 gtk_container_add (GTK_CONTAINER (frame), image);
406
407
408 /* Progressive */
409
410 label = gtk_label_new (NULL);
411 gtk_label_set_markup (GTK_LABEL (label),
412 "<u>Progressive image loading</u>");
413 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
414
415 frame = gtk_frame_new (NULL);
416 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
417 gtk_widget_set_halign (frame, GTK_ALIGN_CENTER);
418 gtk_widget_set_valign (frame, GTK_ALIGN_CENTER);
419 gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
420
421 /* Create an empty image for now; the progressive loader
422 * will create the pixbuf and fill it in.
423 */
424 image = gtk_image_new_from_pixbuf (NULL);
425 gtk_container_add (GTK_CONTAINER (frame), image);
426
427 start_progressive_loading (image);
428
429 /* Sensitivity control */
430 button = gtk_toggle_button_new_with_mnemonic ("_Insensitive");
431 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
432
433 g_signal_connect (button, "toggled",
434 G_CALLBACK (toggle_sensitivity_callback),
435 vbox);
436 }
437
438 if (!gtk_widget_get_visible (window))
439 gtk_widget_show_all (window);
440 else
441 gtk_widget_destroy (window);
442
443 return window;
444}
445