]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/images.c
Call the do_ functions with the toplevel as argument.
[~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 <stdio.h>
17 #include <errno.h>
18 #include "demo-common.h"
19
20 static GtkWidget *window = NULL;
21 static GdkPixbufLoader *pixbuf_loader = NULL;
22 static guint load_timeout = 0;
23 static FILE* 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       size_t bytes_read;
82       guchar buf[256];
83       GError *error = NULL;
84       
85       bytes_read = fread (buf, 1, 256, image_stream);
86
87       if (ferror (image_stream))
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                                            g_strerror (errno));
97
98           g_signal_connect (dialog, "response",
99                             G_CALLBACK (gtk_widget_destroy), NULL);
100
101           fclose (image_stream);
102           image_stream = NULL;
103
104           gtk_widget_show (dialog);
105           
106           load_timeout = 0;
107
108           return FALSE; /* uninstall the timeout */
109         }
110
111       if (!gdk_pixbuf_loader_write (pixbuf_loader,
112                                     buf, bytes_read,
113                                     &error))
114         {
115           GtkWidget *dialog;
116           
117           dialog = gtk_message_dialog_new (GTK_WINDOW (window),
118                                            GTK_DIALOG_DESTROY_WITH_PARENT,
119                                            GTK_MESSAGE_ERROR,
120                                            GTK_BUTTONS_CLOSE,
121                                            "Failed to load image: %s",
122                                            error->message);
123
124           g_error_free (error);
125           
126           g_signal_connect (dialog, "response",
127                             G_CALLBACK (gtk_widget_destroy), NULL);
128
129           fclose (image_stream);
130           image_stream = NULL;
131           
132           gtk_widget_show (dialog);
133
134           load_timeout = 0;
135
136           return FALSE; /* uninstall the timeout */
137         }
138
139       if (feof (image_stream))
140         {
141           fclose (image_stream);
142           image_stream = NULL;
143
144           /* Errors can happen on close, e.g. if the image
145            * file was truncated we'll know on close that
146            * it was incomplete.
147            */
148           error = NULL;
149           if (!gdk_pixbuf_loader_close (pixbuf_loader,
150                                         &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 (pixbuf_loader);
169               pixbuf_loader = NULL;
170               
171               load_timeout = 0;
172               
173               return FALSE; /* uninstall the timeout */
174             }
175           
176           g_object_unref (pixbuf_loader);
177           pixbuf_loader = NULL;
178         }
179     }
180   else
181     {
182       gchar *filename;
183       gchar *error_message = NULL;
184       GError *error = NULL; 
185
186       /* demo_find_file() looks in the the current directory first,
187        * so you can run gtk-demo without installing GTK, then looks
188        * in the location where the file is installed.
189        */
190       filename = demo_find_file ("alphatest.png", &error);
191       if (error)
192         {
193           error_message = g_strdup (error->message);
194           g_error_free (error);
195         }
196       else
197         {
198           image_stream = fopen (filename, "r");
199           g_free (filename);
200
201           if (!image_stream)
202             error_message = g_strdup_printf ("Unable to open image file 'alphatest.png': %s",
203                                              g_strerror (errno));
204         }
205
206       if (image_stream == NULL)
207         {
208           GtkWidget *dialog;
209           
210           dialog = gtk_message_dialog_new (GTK_WINDOW (window),
211                                            GTK_DIALOG_DESTROY_WITH_PARENT,
212                                            GTK_MESSAGE_ERROR,
213                                            GTK_BUTTONS_CLOSE,
214                                            "%s", error_message);
215           g_free (error_message);
216
217           g_signal_connect (dialog, "response",
218                             G_CALLBACK (gtk_widget_destroy), NULL);
219           
220           gtk_widget_show (dialog);
221
222           load_timeout = 0;
223
224           return FALSE; /* uninstall the timeout */
225         }
226
227       if (pixbuf_loader)
228         {
229           gdk_pixbuf_loader_close (pixbuf_loader, NULL);
230           g_object_unref (pixbuf_loader);
231           pixbuf_loader = NULL;
232         }
233       
234       pixbuf_loader = gdk_pixbuf_loader_new ();
235       
236       g_signal_connect (pixbuf_loader, "area_prepared",
237                         G_CALLBACK (progressive_prepared_callback), image);
238       
239       g_signal_connect (pixbuf_loader, "area_updated",
240                         G_CALLBACK (progressive_updated_callback), image);
241     }
242
243   /* leave timeout installed */
244   return TRUE;
245 }
246
247 static void
248 start_progressive_loading (GtkWidget *image)
249 {
250   /* This is obviously totally contrived (we slow down loading
251    * on purpose to show how incremental loading works).
252    * The real purpose of incremental loading is the case where
253    * you are reading data from a slow source such as the network.
254    * The timeout simply simulates a slow data source by inserting
255    * pauses in the reading process.
256    */
257   load_timeout = g_timeout_add (150,
258                                 progressive_timeout,
259                                 image);
260 }
261
262 static void
263 cleanup_callback (GtkObject *object,
264                   gpointer   data)
265 {
266   if (load_timeout)
267     {
268       g_source_remove (load_timeout);
269       load_timeout = 0;
270     }
271   
272   if (pixbuf_loader)
273     {
274       gdk_pixbuf_loader_close (pixbuf_loader, NULL);
275       g_object_unref (pixbuf_loader);
276       pixbuf_loader = NULL;
277     }
278
279   if (image_stream)
280     fclose (image_stream);
281   image_stream = NULL;
282 }
283
284 static void
285 toggle_sensitivity_callback (GtkWidget *togglebutton,
286                              gpointer   user_data)
287 {
288   GtkContainer *container = user_data;
289   GList *list;
290   GList *tmp;
291   
292   list = gtk_container_get_children (container);
293
294   tmp = list;
295   while (tmp != NULL)
296     {
297       /* don't disable our toggle */
298       if (GTK_WIDGET (tmp->data) != togglebutton)
299         gtk_widget_set_sensitive (GTK_WIDGET (tmp->data),
300                                   !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton)));
301       
302       tmp = tmp->next;
303     }
304
305   g_list_free (list);
306 }
307   
308
309 GtkWidget *
310 do_images (GtkWidget *do_widget)
311 {
312   GtkWidget *frame;
313   GtkWidget *vbox;
314   GtkWidget *image;
315   GtkWidget *label;
316   GtkWidget *align;
317   GtkWidget *button;
318   GdkPixbuf *pixbuf;
319   GError *error = NULL;
320   char *filename;
321   
322   if (!window)
323     {
324       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
325       gtk_window_set_screen (GTK_WINDOW (window),
326                              gtk_widget_get_screen (do_widget));
327       gtk_window_set_title (GTK_WINDOW (window), "Images");
328
329       g_signal_connect (window, "destroy",
330                         G_CALLBACK (gtk_widget_destroyed), &window);
331       g_signal_connect (window, "destroy",
332                         G_CALLBACK (cleanup_callback), NULL);
333
334       gtk_container_set_border_width (GTK_CONTAINER (window), 8);
335
336       vbox = gtk_vbox_new (FALSE, 8);
337       gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
338       gtk_container_add (GTK_CONTAINER (window), vbox);
339
340       label = gtk_label_new (NULL);
341       gtk_label_set_markup (GTK_LABEL (label),
342                             "<u>Image loaded from a file</u>");
343       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
344       
345       frame = gtk_frame_new (NULL);
346       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
347       /* The alignment keeps the frame from growing when users resize
348        * the window
349        */
350       align = gtk_alignment_new (0.5, 0.5, 0, 0);
351       gtk_container_add (GTK_CONTAINER (align), frame);
352       gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
353
354       /* demo_find_file() looks in the the current directory first,
355        * so you can run gtk-demo without installing GTK, then looks
356        * in the location where the file is installed.
357        */
358       pixbuf = NULL;
359       filename = demo_find_file ("gtk-logo-rgb.gif", &error);
360       if (filename)
361         {
362           pixbuf = gdk_pixbuf_new_from_file (filename, &error);
363           g_free (filename);
364         }
365
366       if (error)
367         {
368           /* This code shows off error handling. You can just use
369            * gtk_image_new_from_file() instead if you don't want to report
370            * errors to the user. If the file doesn't load when using
371            * gtk_image_new_from_file(), a "missing image" icon will
372            * be displayed instead.
373            */
374           GtkWidget *dialog;
375           
376           dialog = gtk_message_dialog_new (GTK_WINDOW (window),
377                                            GTK_DIALOG_DESTROY_WITH_PARENT,
378                                            GTK_MESSAGE_ERROR,
379                                            GTK_BUTTONS_CLOSE,
380                                            "Unable to open image file 'gtk-logo-rgb.gif': %s",
381                                            error->message);
382           g_error_free (error);
383           
384           g_signal_connect (dialog, "response",
385                             G_CALLBACK (gtk_widget_destroy), NULL);
386           
387           gtk_widget_show (dialog);
388         }
389           
390       image = gtk_image_new_from_pixbuf (pixbuf);
391
392       gtk_container_add (GTK_CONTAINER (frame), image);
393
394
395       /* Animation */
396
397       label = gtk_label_new (NULL);
398       gtk_label_set_markup (GTK_LABEL (label),
399                             "<u>Animation loaded from a file</u>");
400       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
401       
402       frame = gtk_frame_new (NULL);
403       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
404       /* The alignment keeps the frame from growing when users resize
405        * the window
406        */
407       align = gtk_alignment_new (0.5, 0.5, 0, 0);
408       gtk_container_add (GTK_CONTAINER (align), frame);
409       gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
410
411       filename = demo_find_file ("floppybuddy.gif", NULL);
412       image = gtk_image_new_from_file (filename);
413       g_free (filename);
414
415       gtk_container_add (GTK_CONTAINER (frame), image);
416       
417
418       /* Progressive */
419       
420       
421       label = gtk_label_new (NULL);
422       gtk_label_set_markup (GTK_LABEL (label),
423                             "<u>Progressive image loading</u>");
424       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
425       
426       frame = gtk_frame_new (NULL);
427       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
428       /* The alignment keeps the frame from growing when users resize
429        * the window
430        */
431       align = gtk_alignment_new (0.5, 0.5, 0, 0);
432       gtk_container_add (GTK_CONTAINER (align), frame);
433       gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
434
435       /* Create an empty image for now; the progressive loader
436        * will create the pixbuf and fill it in.
437        */
438       image = gtk_image_new_from_pixbuf (NULL);
439       gtk_container_add (GTK_CONTAINER (frame), image);
440
441       start_progressive_loading (image);
442
443       /* Sensitivity control */
444       button = gtk_toggle_button_new_with_mnemonic ("_Insensitive");
445       gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
446
447       g_signal_connect (button, "toggled",
448                         G_CALLBACK (toggle_sensitivity_callback),
449                         vbox);
450     }
451
452   if (!GTK_WIDGET_VISIBLE (window))
453     {
454       gtk_widget_show_all (window);
455     }
456   else
457     {
458       gtk_widget_destroy (window);
459       window = NULL;
460     }
461
462   return window;
463 }