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 | |
22 | static GtkWidget *window = NULL; |
23 | static GdkPixbufLoader *pixbuf_loader = NULL; |
24 | static guint load_timeout = 0; |
25 | static GInputStream * image_stream = NULL; |
26 | |
27 | static void |
28 | progressive_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 | |
46 | static void |
47 | progressive_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 | |
64 | static int |
65 | progressive_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 | |
257 | static void |
258 | start_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 | |
271 | static void |
272 | cleanup_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 | |
297 | static void |
298 | toggle_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 | |
314 | GtkWidget * |
315 | do_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 | |