1/* Images
2 * #Keywords: GdkPaintable, GtkWidgetPaintable
3 *
4 * GtkImage and GtkPicture are used to display an image; the image can be
5 * in a number of formats.
6 *
7 * GtkImage is the widget used to display icons or images that should be
8 * sized and styled like an icon, while GtkPicture is used for images
9 * that should be displayed as-is.
10 *
11 * This demo code shows some of the more obscure cases, in the simple
12 * case a call to gtk_picture_new_for_file() or
13 * gtk_image_new_from_icon_name() is all you need.
14 */
15
16#include <gtk/gtk.h>
17#include <glib/gstdio.h>
18#include <stdio.h>
19#include <errno.h>
20#include "pixbufpaintable.h"
21
22static GtkWidget *window = NULL;
23static GdkPixbufLoader *pixbuf_loader = NULL;
24static guint load_timeout = 0;
25static GInputStream * image_stream = NULL;
26
27static void
28progressive_prepared_callback (GdkPixbufLoader *loader,
29 gpointer data)
30{
31 GdkPixbuf *pixbuf;
32 GtkWidget *picture;
33
34 picture = GTK_WIDGET (data);
35
36 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
37
38 /* Avoid displaying random memory contents, since the pixbuf
39 * isn't filled in yet.
40 */
41 gdk_pixbuf_fill (pixbuf, pixel: 0xaaaaaaff);
42
43 gtk_picture_set_pixbuf (self: GTK_PICTURE (ptr: picture), pixbuf);
44}
45
46static void
47progressive_updated_callback (GdkPixbufLoader *loader,
48 int x,
49 int y,
50 int width,
51 int height,
52 gpointer data)
53{
54 GtkWidget *picture;
55 GdkPixbuf *pixbuf;
56
57 picture = GTK_WIDGET (data);
58
59 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
60 gtk_picture_set_pixbuf (self: GTK_PICTURE (ptr: picture), NULL);
61 gtk_picture_set_pixbuf (self: GTK_PICTURE (ptr: picture), pixbuf);
62}
63
64static int
65progressive_timeout (gpointer data)
66{
67 GtkWidget *picture;
68
69 picture = GTK_WIDGET (data);
70
71 /* This shows off fully-paranoid error handling, so looks scary.
72 * You could factor out the error handling code into a nice separate
73 * function to make things nicer.
74 */
75
76 if (image_stream)
77 {
78 gssize bytes_read;
79 guchar buf[256];
80 GError *error = NULL;
81
82 bytes_read = g_input_stream_read (stream: image_stream, buffer: buf, count: 256, NULL, error: &error);
83
84 if (bytes_read < 0)
85 {
86 GtkWidget *dialog;
87
88 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
89 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
90 type: GTK_MESSAGE_ERROR,
91 buttons: GTK_BUTTONS_CLOSE,
92 message_format: "Failure reading image file 'alphatest.png': %s",
93 error->message);
94 g_error_free (error);
95
96 g_signal_connect (dialog, "response",
97 G_CALLBACK (gtk_window_destroy), NULL);
98
99 g_object_unref (object: image_stream);
100 image_stream = NULL;
101
102 gtk_widget_show (widget: dialog);
103
104 load_timeout = 0;
105
106 return FALSE; /* uninstall the timeout */
107 }
108
109 if (!gdk_pixbuf_loader_write (loader: pixbuf_loader,
110 buf, count: bytes_read,
111 error: &error))
112 {
113 GtkWidget *dialog;
114
115 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
116 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
117 type: GTK_MESSAGE_ERROR,
118 buttons: GTK_BUTTONS_CLOSE,
119 message_format: "Failed to load image: %s",
120 error->message);
121
122 g_error_free (error);
123
124 g_signal_connect (dialog, "response",
125 G_CALLBACK (gtk_window_destroy), NULL);
126
127 g_object_unref (object: image_stream);
128 image_stream = NULL;
129
130 gtk_widget_show (widget: dialog);
131
132 load_timeout = 0;
133
134 return FALSE; /* uninstall the timeout */
135 }
136
137 if (bytes_read == 0)
138 {
139 /* Errors can happen on close, e.g. if the image
140 * file was truncated we'll know on close that
141 * it was incomplete.
142 */
143 error = NULL;
144 if (!g_input_stream_close (stream: image_stream, NULL, error: &error))
145 {
146 GtkWidget *dialog;
147
148 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
149 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
150 type: GTK_MESSAGE_ERROR,
151 buttons: GTK_BUTTONS_CLOSE,
152 message_format: "Failed to load image: %s",
153 error->message);
154
155 g_error_free (error);
156
157 g_signal_connect (dialog, "response",
158 G_CALLBACK (gtk_window_destroy), NULL);
159
160 gtk_widget_show (widget: dialog);
161
162 g_object_unref (object: image_stream);
163 image_stream = NULL;
164 g_object_unref (object: pixbuf_loader);
165 pixbuf_loader = NULL;
166
167 load_timeout = 0;
168
169 return FALSE; /* uninstall the timeout */
170 }
171
172 g_object_unref (object: image_stream);
173 image_stream = NULL;
174
175 /* Errors can happen on close, e.g. if the image
176 * file was truncated we'll know on close that
177 * it was incomplete.
178 */
179 error = NULL;
180 if (!gdk_pixbuf_loader_close (loader: pixbuf_loader,
181 error: &error))
182 {
183 GtkWidget *dialog;
184
185 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
186 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
187 type: GTK_MESSAGE_ERROR,
188 buttons: GTK_BUTTONS_CLOSE,
189 message_format: "Failed to load image: %s",
190 error->message);
191
192 g_error_free (error);
193
194 g_signal_connect (dialog, "response",
195 G_CALLBACK (gtk_window_destroy), NULL);
196
197 gtk_widget_show (widget: dialog);
198
199 g_object_unref (object: pixbuf_loader);
200 pixbuf_loader = NULL;
201
202 load_timeout = 0;
203
204 return FALSE; /* uninstall the timeout */
205 }
206
207 g_object_unref (object: pixbuf_loader);
208 pixbuf_loader = NULL;
209 }
210 }
211 else
212 {
213 GError *error = NULL;
214
215 image_stream = g_resources_open_stream (path: "/images/alphatest.png", lookup_flags: 0, error: &error);
216
217 if (image_stream == NULL)
218 {
219 GtkWidget *dialog;
220
221 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
222 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
223 type: GTK_MESSAGE_ERROR,
224 buttons: GTK_BUTTONS_CLOSE,
225 message_format: "%s", error->message);
226 g_error_free (error);
227
228 g_signal_connect (dialog, "response",
229 G_CALLBACK (gtk_window_destroy), NULL);
230
231 gtk_widget_show (widget: dialog);
232
233 load_timeout = 0;
234
235 return FALSE; /* uninstall the timeout */
236 }
237
238 if (pixbuf_loader)
239 {
240 gdk_pixbuf_loader_close (loader: pixbuf_loader, NULL);
241 g_object_unref (object: pixbuf_loader);
242 }
243
244 pixbuf_loader = gdk_pixbuf_loader_new ();
245
246 g_signal_connect_object (instance: pixbuf_loader, detailed_signal: "area-prepared",
247 G_CALLBACK (progressive_prepared_callback), gobject: picture, connect_flags: 0);
248
249 g_signal_connect_object (instance: pixbuf_loader, detailed_signal: "area-updated",
250 G_CALLBACK (progressive_updated_callback), gobject: picture, connect_flags: 0);
251 }
252
253 /* leave timeout installed */
254 return TRUE;
255}
256
257static void
258start_progressive_loading (GtkWidget *picture)
259{
260 /* This is obviously totally contrived (we slow down loading
261 * on purpose to show how incremental loading works).
262 * The real purpose of incremental loading is the case where
263 * you are reading data from a slow source such as the network.
264 * The timeout simply simulates a slow data source by inserting
265 * pauses in the reading process.
266 */
267 load_timeout = g_timeout_add (interval: 300, function: progressive_timeout, data: picture);
268 g_source_set_name_by_id (tag: load_timeout, name: "[gtk] progressive_timeout");
269}
270
271static void
272cleanup_callback (gpointer data,
273 GObject *former_object)
274{
275 *(gpointer**)data = NULL;
276
277 if (load_timeout)
278 {
279 g_source_remove (tag: load_timeout);
280 load_timeout = 0;
281 }
282
283 if (pixbuf_loader)
284 {
285 gdk_pixbuf_loader_close (loader: pixbuf_loader, NULL);
286 g_object_unref (object: pixbuf_loader);
287 pixbuf_loader = NULL;
288 }
289
290 if (image_stream)
291 {
292 g_object_unref (object: image_stream);
293 image_stream = NULL;
294 }
295}
296
297static void
298toggle_sensitivity_callback (GtkWidget *togglebutton,
299 gpointer user_data)
300{
301 GtkWidget *child;
302
303 for (child = gtk_widget_get_first_child (GTK_WIDGET (user_data));
304 child != NULL;
305 child = gtk_widget_get_next_sibling (widget: child))
306 {
307 /* don't disable our toggle */
308 if (child != togglebutton)
309 gtk_widget_set_sensitive (widget: child, sensitive: !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton)));
310 }
311}
312
313
314GtkWidget *
315do_images (GtkWidget *do_widget)
316{
317 GtkWidget *video;
318 GtkWidget *frame;
319 GtkWidget *vbox;
320 GtkWidget *hbox;
321 GtkWidget *base_vbox;
322 GtkWidget *image;
323 GtkWidget *picture;
324 GtkWidget *label;
325 GtkWidget *button;
326 GdkPaintable *paintable;
327 GIcon *gicon;
328
329 if (!window)
330 {
331 window = gtk_window_new ();
332 gtk_window_set_display (GTK_WINDOW (window),
333 display: gtk_widget_get_display (widget: do_widget));
334 gtk_window_set_title (GTK_WINDOW (window), title: "Images");
335 g_object_weak_ref (G_OBJECT (window), notify: cleanup_callback, data: &window);
336
337 base_vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8);
338 gtk_widget_set_margin_start (widget: base_vbox, margin: 16);
339 gtk_widget_set_margin_end (widget: base_vbox, margin: 16);
340 gtk_widget_set_margin_top (widget: base_vbox, margin: 16);
341 gtk_widget_set_margin_bottom (widget: base_vbox, margin: 16);
342 gtk_window_set_child (GTK_WINDOW (window), child: base_vbox);
343
344 hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 16);
345 gtk_box_append (GTK_BOX (base_vbox), child: hbox);
346
347 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8);
348 gtk_box_append (GTK_BOX (hbox), child: vbox);
349
350 label = gtk_label_new (str: "Image from a resource");
351 gtk_widget_add_css_class (widget: label, css_class: "heading");
352 gtk_box_append (GTK_BOX (vbox), child: label);
353
354 frame = gtk_frame_new (NULL);
355 gtk_widget_set_halign (widget: frame, align: GTK_ALIGN_CENTER);
356 gtk_widget_set_valign (widget: frame, align: GTK_ALIGN_CENTER);
357 gtk_box_append (GTK_BOX (vbox), child: frame);
358
359 image = gtk_image_new_from_resource (resource_path: "/images/org.gtk.Demo4.svg");
360 gtk_image_set_icon_size (GTK_IMAGE (image), icon_size: GTK_ICON_SIZE_LARGE);
361
362 gtk_frame_set_child (GTK_FRAME (frame), child: image);
363
364
365 /* Animation */
366
367 label = gtk_label_new (str: "Animation from a resource");
368 gtk_widget_add_css_class (widget: label, css_class: "heading");
369 gtk_box_append (GTK_BOX (vbox), child: label);
370
371 frame = gtk_frame_new (NULL);
372 gtk_widget_set_halign (widget: frame, align: GTK_ALIGN_CENTER);
373 gtk_widget_set_valign (widget: frame, align: GTK_ALIGN_CENTER);
374 gtk_box_append (GTK_BOX (vbox), child: frame);
375
376 paintable = pixbuf_paintable_new_from_resource (path: "/images/floppybuddy.gif");
377 picture = gtk_picture_new_for_paintable (paintable);
378 g_object_unref (object: paintable);
379
380 gtk_frame_set_child (GTK_FRAME (frame), child: picture);
381
382 /* Symbolic icon */
383
384 label = gtk_label_new (str: "Symbolic themed icon");
385 gtk_widget_add_css_class (widget: label, css_class: "heading");
386 gtk_box_append (GTK_BOX (vbox), child: label);
387
388 frame = gtk_frame_new (NULL);
389 gtk_widget_set_halign (widget: frame, align: GTK_ALIGN_CENTER);
390 gtk_widget_set_valign (widget: frame, align: GTK_ALIGN_CENTER);
391 gtk_box_append (GTK_BOX (vbox), child: frame);
392
393 gicon = g_themed_icon_new_with_default_fallbacks (iconname: "battery-caution-charging-symbolic");
394 image = gtk_image_new_from_gicon (icon: gicon);
395 gtk_image_set_icon_size (GTK_IMAGE (image), icon_size: GTK_ICON_SIZE_LARGE);
396
397 gtk_frame_set_child (GTK_FRAME (frame), child: image);
398
399
400 /* Progressive */
401 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8);
402 gtk_box_append (GTK_BOX (hbox), child: vbox);
403
404 label = gtk_label_new (str: "Progressive image loading");
405 gtk_widget_add_css_class (widget: label, css_class: "heading");
406 gtk_box_append (GTK_BOX (vbox), child: label);
407
408 frame = gtk_frame_new (NULL);
409 gtk_widget_set_halign (widget: frame, align: GTK_ALIGN_CENTER);
410 gtk_widget_set_valign (widget: frame, align: GTK_ALIGN_CENTER);
411 gtk_box_append (GTK_BOX (vbox), child: frame);
412
413 /* Create an empty image for now; the progressive loader
414 * will create the pixbuf and fill it in.
415 */
416 picture = gtk_picture_new ();
417 gtk_picture_set_alternative_text (self: GTK_PICTURE (ptr: picture), alternative_text: "A slowly loading image");
418 gtk_frame_set_child (GTK_FRAME (frame), child: picture);
419
420 start_progressive_loading (picture);
421
422 /* Video */
423 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8);
424 gtk_box_append (GTK_BOX (hbox), child: vbox);
425
426 label = gtk_label_new (str: "Displaying video");
427 gtk_widget_add_css_class (widget: label, css_class: "heading");
428 gtk_box_append (GTK_BOX (vbox), child: label);
429
430 frame = gtk_frame_new (NULL);
431 gtk_widget_set_halign (widget: frame, align: GTK_ALIGN_CENTER);
432 gtk_widget_set_valign (widget: frame, align: GTK_ALIGN_CENTER);
433 gtk_box_append (GTK_BOX (vbox), child: frame);
434
435 video = gtk_video_new_for_resource (resource_path: "/images/gtk-logo.webm");
436 gtk_media_stream_set_loop (self: gtk_video_get_media_stream (self: GTK_VIDEO (ptr: video)), TRUE);
437 gtk_frame_set_child (GTK_FRAME (frame), child: video);
438
439 /* Widget paintables */
440 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8);
441 gtk_box_append (GTK_BOX (hbox), child: vbox);
442
443 label = gtk_label_new (str: "GtkWidgetPaintable");
444 gtk_widget_add_css_class (widget: label, css_class: "heading");
445 gtk_box_append (GTK_BOX (vbox), child: label);
446
447 paintable = gtk_widget_paintable_new (widget: do_widget);
448 picture = gtk_picture_new_for_paintable (paintable);
449 gtk_widget_set_size_request (widget: picture, width: 100, height: 100);
450 gtk_widget_set_valign (widget: picture, align: GTK_ALIGN_START);
451 gtk_box_append (GTK_BOX (vbox), child: picture);
452
453 /* Sensitivity control */
454 button = gtk_toggle_button_new_with_mnemonic (label: "_Insensitive");
455 gtk_box_append (GTK_BOX (base_vbox), child: button);
456
457 g_signal_connect (button, "toggled",
458 G_CALLBACK (toggle_sensitivity_callback),
459 base_vbox);
460 }
461
462 if (!gtk_widget_get_visible (widget: window))
463 gtk_widget_show (widget: window);
464 else
465 gtk_window_destroy (GTK_WINDOW (window));
466
467 return window;
468}
469

source code of gtk/demos/gtk-demo/images.c