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