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