]> Pileus Git - ~andy/gtk/blob - demos/testanimation.c
fix some shell typos
[~andy/gtk] / demos / testanimation.c
1
2 /* testpixbuf -- test program for gdk-pixbuf code
3  * Copyright (C) 1999 Mark Crichton, Larry Ewing
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <gtk/gtk.h>
28
29 typedef struct _LoadContext LoadContext;
30
31 struct _LoadContext
32 {
33   gchar *filename;
34   GtkWidget *window;
35   GdkPixbufLoader *pixbuf_loader;
36   guint load_timeout;
37   FILE* image_stream;
38 };
39
40 static void
41 destroy_context (gpointer data)
42 {
43   LoadContext *lc = data;
44
45   g_free (lc->filename);
46   
47   if (lc->load_timeout)
48     g_source_remove (lc->load_timeout);
49
50   if (lc->image_stream)
51     fclose (lc->image_stream);
52
53   if (lc->pixbuf_loader)
54     {
55       gdk_pixbuf_loader_close (lc->pixbuf_loader, NULL);
56       g_object_unref (G_OBJECT (lc->pixbuf_loader));
57     }
58   
59   g_free (lc);
60 }
61
62 static LoadContext*
63 get_load_context (GtkWidget *image)
64 {
65   LoadContext *lc;
66
67   lc = g_object_get_data (G_OBJECT (image), "lc");
68
69   if (lc == NULL)
70     {
71       lc = g_new0 (LoadContext, 1);
72
73       g_object_set_data_full (G_OBJECT (image),        
74                               "lc",
75                               lc,
76                               destroy_context);
77     }
78
79   return lc;
80 }
81
82 static void
83 progressive_prepared_callback (GdkPixbufLoader* loader,
84                                gpointer         data)
85 {
86   GdkPixbuf* pixbuf;
87   GtkWidget* image;
88
89   image = GTK_WIDGET (data);
90     
91   pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
92
93   /* Avoid displaying random memory contents, since the pixbuf
94    * isn't filled in yet.
95    */
96   gdk_pixbuf_fill (pixbuf, 0xaaaaaaff);
97
98   /* Could set the pixbuf instead, if we only wanted to display
99    * static images.
100    */
101   gtk_image_set_from_animation (GTK_IMAGE (image),
102                                 gdk_pixbuf_loader_get_animation (loader));
103 }
104
105 static void
106 progressive_updated_callback (GdkPixbufLoader* loader,
107                               gint x, gint y, gint width, gint height,
108                               gpointer data)
109 {
110   GtkWidget* image;
111   
112   image = GTK_WIDGET (data);
113
114   /* We know the pixbuf inside the GtkImage has changed, but the image
115    * itself doesn't know this; so queue a redraw.  If we wanted to be
116    * really efficient, we could use a drawing area or something
117    * instead of a GtkImage, so we could control the exact position of
118    * the pixbuf on the display, then we could queue a draw for only
119    * the updated area of the image.
120    */
121
122   /* We only really need to redraw if the image's animation iterator
123    * is gdk_pixbuf_animation_iter_on_currently_loading_frame(), but
124    * who cares.
125    */
126   
127   gtk_widget_queue_draw (image);
128 }
129
130 static gint
131 progressive_timeout (gpointer data)
132 {
133   GtkWidget *image;
134   LoadContext *lc;
135   
136   image = GTK_WIDGET (data);
137   lc = get_load_context (image);
138   
139   /* This shows off fully-paranoid error handling, so looks scary.
140    * You could factor out the error handling code into a nice separate
141    * function to make things nicer.
142    */
143   
144   if (lc->image_stream)
145     {
146       size_t bytes_read;
147       guchar buf[256];
148       GError *error = NULL;
149       
150       bytes_read = fread (buf, 1, 256, lc->image_stream);
151
152       if (ferror (lc->image_stream))
153         {
154           GtkWidget *dialog;
155           
156           dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
157                                            GTK_DIALOG_DESTROY_WITH_PARENT,
158                                            GTK_MESSAGE_ERROR,
159                                            GTK_BUTTONS_CLOSE,
160                                            "Failure reading image file 'alphatest.png': %s",
161                                            g_strerror (errno));
162
163           gtk_signal_connect (GTK_OBJECT (dialog),
164                               "response",
165                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
166                               NULL);
167
168           fclose (lc->image_stream);
169           lc->image_stream = NULL;
170
171           gtk_widget_show (dialog);
172           
173           lc->load_timeout = 0;
174
175           return FALSE; /* uninstall the timeout */
176         }
177
178       if (!gdk_pixbuf_loader_write (lc->pixbuf_loader,
179                                     buf, bytes_read,
180                                     &error))
181         {
182           GtkWidget *dialog;
183           
184           dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
185                                            GTK_DIALOG_DESTROY_WITH_PARENT,
186                                            GTK_MESSAGE_ERROR,
187                                            GTK_BUTTONS_CLOSE,
188                                            "Failed to load image: %s",
189                                            error->message);
190
191           g_error_free (error);
192           
193           gtk_signal_connect (GTK_OBJECT (dialog),
194                               "response",
195                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
196                               NULL);
197
198           fclose (lc->image_stream);
199           lc->image_stream = NULL;
200           
201           gtk_widget_show (dialog);
202
203           lc->load_timeout = 0;
204
205           return FALSE; /* uninstall the timeout */
206         }
207
208       if (feof (lc->image_stream))
209         {
210           fclose (lc->image_stream);
211           lc->image_stream = NULL;
212
213           /* Errors can happen on close, e.g. if the image
214            * file was truncated we'll know on close that
215            * it was incomplete.
216            */
217           error = NULL;
218           if (!gdk_pixbuf_loader_close (lc->pixbuf_loader,
219                                         &error))
220             {
221               GtkWidget *dialog;
222               
223               dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
224                                                GTK_DIALOG_DESTROY_WITH_PARENT,
225                                                GTK_MESSAGE_ERROR,
226                                                GTK_BUTTONS_CLOSE,
227                                                "Failed to load image: %s",
228                                                error->message);
229               
230               g_error_free (error);
231               
232               gtk_signal_connect (GTK_OBJECT (dialog),
233                                   "response",
234                                   GTK_SIGNAL_FUNC (gtk_widget_destroy),
235                                   NULL);
236               
237               gtk_widget_show (dialog);
238
239               g_object_unref (G_OBJECT (lc->pixbuf_loader));
240               lc->pixbuf_loader = NULL;
241               
242               lc->load_timeout = 0;
243               
244               return FALSE; /* uninstall the timeout */
245             }
246           
247           g_object_unref (G_OBJECT (lc->pixbuf_loader));
248           lc->pixbuf_loader = NULL;
249         }
250     }
251   else
252     {
253       lc->image_stream = fopen (lc->filename, "r");
254
255       if (lc->image_stream == NULL)
256         {
257           GtkWidget *dialog;
258           
259           dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
260                                            GTK_DIALOG_DESTROY_WITH_PARENT,
261                                            GTK_MESSAGE_ERROR,
262                                            GTK_BUTTONS_CLOSE,
263                                            "Unable to open image file '%s': %s",
264                                            lc->filename,
265                                            g_strerror (errno));
266
267           gtk_signal_connect (GTK_OBJECT (dialog),
268                               "response",
269                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
270                               NULL);
271           
272           gtk_widget_show (dialog);
273
274           lc->load_timeout = 0;
275
276           return FALSE; /* uninstall the timeout */
277         }
278
279       if (lc->pixbuf_loader)
280         {
281           gdk_pixbuf_loader_close (lc->pixbuf_loader, NULL);
282           g_object_unref (G_OBJECT (lc->pixbuf_loader));
283           lc->pixbuf_loader = NULL;
284         }
285       
286       lc->pixbuf_loader = gdk_pixbuf_loader_new ();
287       
288       g_signal_connect_data (G_OBJECT (lc->pixbuf_loader),
289                              "area_prepared",
290                              G_CALLBACK (progressive_prepared_callback),
291                              image,
292                              NULL, FALSE, FALSE);
293       
294       g_signal_connect_data (G_OBJECT (lc->pixbuf_loader),
295                              "area_updated",
296                              G_CALLBACK (progressive_updated_callback),
297                              image,
298                              NULL, FALSE, FALSE);
299     }
300
301   /* leave timeout installed */
302   return TRUE;
303 }
304
305 static void
306 start_progressive_loading (GtkWidget *image)
307 {
308   LoadContext *lc;
309
310   lc = get_load_context (image);
311   
312   /* This is obviously totally contrived (we slow down loading
313    * on purpose to show how incremental loading works).
314    * The real purpose of incremental loading is the case where
315    * you are reading data from a slow source such as the network.
316    * The timeout simply simulates a slow data source by inserting
317    * pauses in the reading process.
318    */
319   lc->load_timeout = g_timeout_add (100,
320                                     progressive_timeout,
321                                     image);
322 }
323
324 static GtkWidget *
325 do_image (const char *filename)
326 {
327   GtkWidget *frame;
328   GtkWidget *vbox;
329   GtkWidget *image;
330   GtkWidget *label;
331   GtkWidget *align;
332   GtkWidget *window;
333   gchar *str, *escaped;
334   LoadContext *lc;
335   
336   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
337   gtk_window_set_title (GTK_WINDOW (window), "Image Loading");
338
339   gtk_container_set_border_width (GTK_CONTAINER (window), 8);
340
341   vbox = gtk_vbox_new (FALSE, 8);
342   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
343   gtk_container_add (GTK_CONTAINER (window), vbox);
344
345   label = gtk_label_new (NULL);
346   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
347   escaped = g_markup_escape_text (filename, -1);
348   str = g_strdup_printf ("Progressively loading: <b>%s</b>", escaped);
349   gtk_label_set_markup (GTK_LABEL (label),
350                         str);
351   g_free (escaped);
352   g_free (str);
353   
354   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
355       
356   frame = gtk_frame_new (NULL);
357   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
358   /* The alignment keeps the frame from growing when users resize
359    * the window
360    */
361   align = gtk_alignment_new (0.5, 0.5, 0, 0);
362   gtk_container_add (GTK_CONTAINER (align), frame);
363   gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);      
364
365   image = gtk_image_new_from_pixbuf (NULL);
366   gtk_container_add (GTK_CONTAINER (frame), image);
367
368   lc = get_load_context (image);
369
370   lc->window = window;
371   lc->filename = g_strdup (filename);
372   
373   start_progressive_loading (image);
374
375   gtk_widget_show_all (window);
376
377   return window;
378 }
379
380 static void
381 do_nonprogressive (const gchar *filename)
382 {
383   GtkWidget *frame;
384   GtkWidget *vbox;
385   GtkWidget *image;
386   GtkWidget *label;
387   GtkWidget *align;
388   GtkWidget *window;
389   gchar *str, *escaped;
390   
391   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
392   gtk_window_set_title (GTK_WINDOW (window), "Animation");
393
394   gtk_container_set_border_width (GTK_CONTAINER (window), 8);
395
396   vbox = gtk_vbox_new (FALSE, 8);
397   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
398   gtk_container_add (GTK_CONTAINER (window), vbox);
399
400   label = gtk_label_new (NULL);
401   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
402   escaped = g_markup_escape_text (filename, -1);
403   str = g_strdup_printf ("Loaded from file: <b>%s</b>", escaped);
404   gtk_label_set_markup (GTK_LABEL (label),
405                         str);
406   g_free (escaped);
407   g_free (str);
408   
409   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
410       
411   frame = gtk_frame_new (NULL);
412   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
413   /* The alignment keeps the frame from growing when users resize
414    * the window
415    */
416   align = gtk_alignment_new (0.5, 0.5, 0, 0);
417   gtk_container_add (GTK_CONTAINER (align), frame);
418   gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);      
419
420   image = gtk_image_new_from_file (filename);
421   gtk_container_add (GTK_CONTAINER (frame), image);
422
423   gtk_widget_show_all (window);
424 }
425
426 int
427 main (int    argc,
428       char **argv)
429 {
430   gint i;
431   
432   gtk_init (&argc, &argv);
433
434   i = 1;
435   while (i < argc)
436     {
437       do_image (argv[i]);
438       do_nonprogressive (argv[i]);
439       
440       ++i;
441     }
442
443   gtk_main ();
444   
445   return 0;
446 }
447
448