]> Pileus Git - ~andy/gtk/blob - gtk/gtkimage.c
Patch from Matthias Clasen to remove remove all instances of
[~andy/gtk] / gtk / gtkimage.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtkcontainer.h"
28 #include "gtkimage.h"
29 #include "gtkiconfactory.h"
30 #include "gtkstock.h"
31 #include <string.h>
32
33 static void gtk_image_class_init   (GtkImageClass  *klass);
34 static void gtk_image_init         (GtkImage       *image);
35 static gint gtk_image_expose       (GtkWidget      *widget,
36                                     GdkEventExpose *event);
37 static void gtk_image_unmap        (GtkWidget      *widget);
38 static void gtk_image_size_request (GtkWidget      *widget,
39                                     GtkRequisition *requisition);
40 static void gtk_image_destroy      (GtkObject      *object);
41 static void gtk_image_clear        (GtkImage       *image);
42 static void gtk_image_reset        (GtkImage       *image);
43 static void gtk_image_update_size  (GtkImage       *image,
44                                     gint            image_width,
45                                     gint            image_height);
46
47 static gpointer parent_class;
48
49 GtkType
50 gtk_image_get_type (void)
51 {
52   static GtkType image_type = 0;
53
54   if (!image_type)
55     {
56       static const GtkTypeInfo image_info =
57       {
58         "GtkImage",
59         sizeof (GtkImage),
60         sizeof (GtkImageClass),
61         (GtkClassInitFunc) gtk_image_class_init,
62         (GtkObjectInitFunc) gtk_image_init,
63         /* reserved_1 */ NULL,
64         /* reserved_2 */ NULL,
65         (GtkClassInitFunc) NULL,
66       };
67
68       image_type = gtk_type_unique (GTK_TYPE_MISC, &image_info);
69     }
70
71   return image_type;
72 }
73
74 static void
75 gtk_image_class_init (GtkImageClass *class)
76 {
77   GtkObjectClass *object_class;
78   GtkWidgetClass *widget_class;
79
80   parent_class = g_type_class_peek_parent (class);
81
82   object_class = (GtkObjectClass *) class;
83   
84   object_class->destroy = gtk_image_destroy;
85
86   widget_class = (GtkWidgetClass*) class;
87
88   widget_class->expose_event = gtk_image_expose;
89   widget_class->size_request = gtk_image_size_request;
90   widget_class->unmap = gtk_image_unmap;
91 }
92
93 static void
94 gtk_image_init (GtkImage *image)
95 {
96   GTK_WIDGET_SET_FLAGS (image, GTK_NO_WINDOW);
97
98   image->storage_type = GTK_IMAGE_EMPTY;
99 }
100
101 static void
102 gtk_image_destroy (GtkObject *object)
103 {
104   GtkImage *image = GTK_IMAGE (object);
105
106   gtk_image_clear (image);
107   
108   GTK_OBJECT_CLASS (parent_class)->destroy (object);
109 }
110
111
112 /**
113  * gtk_image_new_from_pixmap:
114  * @pixmap: a #GdkPixmap, or %NULL
115  * @mask: a #GdkBitmap, or %NULL
116  * 
117  * Creates a #GtkImage widget displaying @pixmap with a @mask.
118  * A #GdkImage is a server-side image buffer in the pixel format of the
119  * current display. The #GtkImage does not assume a reference to the
120  * pixmap or mask; you still need to unref them if you own references.
121  * #GtkImage will add its own reference rather than adopting yours.
122  * 
123  * Return value: a new #GtkImage
124  **/
125 GtkWidget*
126 gtk_image_new_from_pixmap (GdkPixmap *pixmap,
127                            GdkBitmap *mask)
128 {
129   GtkImage *image;
130
131   image = gtk_type_new (GTK_TYPE_IMAGE);
132
133   gtk_image_set_from_pixmap (image, pixmap, mask);
134
135   return GTK_WIDGET (image);
136 }
137
138 /**
139  * gtk_image_new_from_image:
140  * @image: a #GdkImage, or %NULL
141  * @mask: a #GdkBitmap, or %NULL 
142  * 
143  * Creates a #GtkImage widget displaying a @image with a @mask.
144  * A #GdkImage is a client-side image buffer in the pixel format of the
145  * current display.
146  * The #GtkImage does not assume a reference to the
147  * image or mask; you still need to unref them if you own references.
148  * #GtkImage will add its own reference rather than adopting yours.
149  * 
150  * Return value: a new #GtkImage
151  **/
152 GtkWidget*
153 gtk_image_new_from_image  (GdkImage  *gdk_image,
154                            GdkBitmap *mask)
155 {
156   GtkImage *image;
157
158   image = gtk_type_new (GTK_TYPE_IMAGE);
159
160   gtk_image_set_from_image (image, gdk_image, mask);
161
162   return GTK_WIDGET (image);
163 }
164
165 /**
166  * gtk_image_new_from_file:
167  * @filename: a filename
168  * 
169  * Creates a new #GtkImage displaying the file @filename. If the file
170  * isn't found or can't be loaded, the resulting #GtkImage will
171  * display a "broken image" icon. This function never returns %NULL,
172  * it always returns a valid #GtkImage widget.
173  *
174  * If the file contains an animation, the image will contain an
175  * animation.
176  *
177  * If you need to detect failures to load the file, use
178  * gdk_pixbuf_new_from_file() to load the file yourself, then create
179  * the #GtkImage from the pixbuf. (Or for animations, use
180  * gdk_pixbuf_animation_new_from_file()).
181  *
182  * The storage type (gtk_image_get_storage_type()) of the returned
183  * image is not defined, it will be whatever is appropriate for
184  * displaying the file.
185  * 
186  * Return value: a new #GtkImage
187  **/
188 GtkWidget*
189 gtk_image_new_from_file   (const gchar *filename)
190 {
191   GtkImage *image;
192
193   image = gtk_type_new (GTK_TYPE_IMAGE);
194
195   gtk_image_set_from_file (image, filename);
196
197   return GTK_WIDGET (image);
198 }
199
200 /**
201  * gtk_image_new_from_pixbuf:
202  * @pixbuf: a #GdkPixbuf, or %NULL
203  * 
204  * Creates a new #GtkImage displaying @pixbuf.
205  * The #GtkImage does not assume a reference to the
206  * pixbuf; you still need to unref it if you own references.
207  * #GtkImage will add its own reference rather than adopting yours.
208  * 
209  * Note that this function just creates an #GtkImage from the pixbuf.  The
210  * #GtkImage created will not react to state changes.  Should you want that, you
211  * should use gtk_image_new_from_icon_set().
212  * 
213  * Return value: a new #GtkImage
214  **/
215 GtkWidget*
216 gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf)
217 {
218   GtkImage *image;
219
220   image = gtk_type_new (GTK_TYPE_IMAGE);
221
222   gtk_image_set_from_pixbuf (image, pixbuf);
223
224   return GTK_WIDGET (image);  
225 }
226
227 /**
228  * gtk_image_new_from_stock:
229  * @stock_id: a stock icon name
230  * @size: a stock icon size
231  * 
232  * Creates a #GtkImage displaying a stock icon. Sample stock icon
233  * names are #GTK_STOCK_OPEN, #GTK_STOCK_EXIT. Sample stock sizes
234  * are #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_SMALL_TOOLBAR. If the stock
235  * icon name isn't known, a "broken image" icon will be displayed instead.
236  * You can register your own stock icon names, see
237  * gtk_icon_factory_add_default() and gtk_icon_factory_add().
238  * 
239  * Return value: a new #GtkImage displaying the stock icon
240  **/
241 GtkWidget*
242 gtk_image_new_from_stock (const gchar    *stock_id,
243                           GtkIconSize     size)
244 {
245   GtkImage *image;
246
247   image = gtk_type_new (GTK_TYPE_IMAGE);
248
249   gtk_image_set_from_stock (image, stock_id, size);
250
251   return GTK_WIDGET (image);
252 }
253
254 /**
255  * gtk_image_new_from_icon_set:
256  * @icon_set: a #GtkIconSet
257  * @size: a stock icon size
258  *
259  * Creates a #GtkImage displaying an icon set. Sample stock sizes are
260  * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_SMALL_TOOLBAR. Instead of using
261  * this function, usually it's better to create a #GtkIconFactory, put
262  * your icon sets in the icon factory, add the icon factory to the
263  * list of default factories with gtk_icon_factory_add_default(), and
264  * then use gtk_image_new_from_stock(). This will allow themes to
265  * override the icon you ship with your application.
266  *
267  * The #GtkImage does not assume a reference to the
268  * icon set; you still need to unref it if you own references.
269  * #GtkImage will add its own reference rather than adopting yours.
270  * 
271  * 
272  * Return value: a new #GtkImage
273  **/
274 GtkWidget*
275 gtk_image_new_from_icon_set (GtkIconSet     *icon_set,
276                              GtkIconSize     size)
277 {
278   GtkImage *image;
279
280   image = gtk_type_new (GTK_TYPE_IMAGE);
281
282   gtk_image_set_from_icon_set (image, icon_set, size);
283
284   return GTK_WIDGET (image);
285 }
286
287 /**
288  * gtk_image_new_from_animation:
289  * @animation: an animation
290  * 
291  * Creates a #GtkImage displaying the given animation.
292  * The #GtkImage does not assume a reference to the
293  * animation; you still need to unref it if you own references.
294  * #GtkImage will add its own reference rather than adopting yours.
295  * 
296  * Return value: a new #GtkImage widget
297  **/
298 GtkWidget*
299 gtk_image_new_from_animation (GdkPixbufAnimation *animation)
300 {
301   GtkImage *image;
302
303   g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL);
304   
305   image = gtk_type_new (GTK_TYPE_IMAGE);
306
307   gtk_image_set_from_animation (image, animation);
308
309   return GTK_WIDGET (image);
310 }
311
312 /**
313  * gtk_image_set_from_pixmap:
314  * @image: a #GtkImage
315  * @pixmap: a #GdkPixmap or %NULL
316  * @mask: a #GdkBitmap or %NULL
317  *
318  * See gtk_image_new_from_pixmap() for details.
319  * 
320  **/
321 void
322 gtk_image_set_from_pixmap (GtkImage  *image,
323                            GdkPixmap *pixmap,
324                            GdkBitmap *mask)
325 {
326   g_return_if_fail (GTK_IS_IMAGE (image));
327   g_return_if_fail (pixmap == NULL ||
328                     GDK_IS_PIXMAP (pixmap));
329   g_return_if_fail (mask == NULL ||
330                     GDK_IS_PIXMAP (mask));
331   
332   if (pixmap)
333     g_object_ref (G_OBJECT (pixmap));
334
335   if (mask)
336     g_object_ref (G_OBJECT (mask));
337
338   gtk_image_reset (image);
339
340   if (pixmap)
341     {
342       int width;
343       int height;
344       
345       image->storage_type = GTK_IMAGE_PIXMAP;
346
347       image->data.pixmap.pixmap = pixmap;
348       image->data.pixmap.mask = mask;
349
350       gdk_drawable_get_size (GDK_DRAWABLE (pixmap), &width, &height);
351
352       gtk_image_update_size (image, width, height);
353     }
354   else
355     {
356       /* Clean up the mask if pixmap was NULL */
357       if (mask)
358         g_object_unref (G_OBJECT (mask));
359     }
360 }
361
362 /**
363  * gtk_image_set_from_image:
364  * @image: a #GtkImage
365  * @gdk_image: a #GdkImage or %NULL
366  * @mask: a #GdkBitmap or %NULL
367  *
368  * See gtk_image_new_from_image() for details.
369  * 
370  **/
371 void
372 gtk_image_set_from_image  (GtkImage  *image,
373                            GdkImage  *gdk_image,
374                            GdkBitmap *mask)
375 {
376   g_return_if_fail (GTK_IS_IMAGE (image));
377   g_return_if_fail (gdk_image == NULL ||
378                     GDK_IS_IMAGE (gdk_image));
379   g_return_if_fail (mask == NULL ||
380                     GDK_IS_PIXMAP (mask));
381
382   
383   if (gdk_image)
384     g_object_ref (G_OBJECT (gdk_image));
385
386   if (mask)
387     g_object_ref (G_OBJECT (mask));
388
389   gtk_image_reset (image);
390
391   if (gdk_image)
392     {
393       image->storage_type = GTK_IMAGE_IMAGE;
394
395       image->data.image.image = gdk_image;
396       image->data.image.mask = mask;
397
398       gtk_image_update_size (image, gdk_image->width, gdk_image->height);
399     }
400   else
401     {
402       /* Clean up the mask if gdk_image was NULL */
403       if (mask)
404         g_object_unref (G_OBJECT (mask));
405     }
406 }
407
408 /**
409  * gtk_image_set_from_file:
410  * @image: a #GtkImage
411  * @filename: a filename or %NULL
412  *
413  * See gtk_image_new_from_file() for details.
414  * 
415  **/
416 void
417 gtk_image_set_from_file   (GtkImage    *image,
418                            const gchar *filename)
419 {
420   GdkPixbufAnimation *anim;
421   
422   g_return_if_fail (GTK_IS_IMAGE (image));
423   g_return_if_fail (filename != NULL);
424   
425   gtk_image_reset (image);
426
427   if (filename == NULL)
428     return;
429   
430   anim = gdk_pixbuf_animation_new_from_file (filename, NULL);
431
432   if (anim == NULL)
433     {
434       gtk_image_set_from_stock (image,
435                                 GTK_STOCK_MISSING_IMAGE,
436                                 GTK_ICON_SIZE_BUTTON);
437       return;
438     }
439
440   /* We could just unconditionally set_from_animation,
441    * but it's nicer for memory if we toss the animation
442    * if it's just a single pixbuf
443    */
444
445   if (gdk_pixbuf_animation_is_static_image (anim))
446     {
447       gtk_image_set_from_pixbuf (image,
448                                  gdk_pixbuf_animation_get_static_image (anim));
449     }
450   else
451     {
452       gtk_image_set_from_animation (image, anim);
453     }
454
455   g_object_unref (G_OBJECT (anim));
456 }
457
458 /**
459  * gtk_image_set_from_pixbuf:
460  * @image: a #GtkImage
461  * @pixbuf: a #GdkPixbuf or %NULL
462  *
463  * See gtk_image_new_from_pixbuf() for details. 
464  * 
465  **/
466 void
467 gtk_image_set_from_pixbuf (GtkImage  *image,
468                            GdkPixbuf *pixbuf)
469 {
470   g_return_if_fail (GTK_IS_IMAGE (image));
471   g_return_if_fail (pixbuf == NULL ||
472                     GDK_IS_PIXBUF (pixbuf));
473   
474   if (pixbuf)
475     g_object_ref (G_OBJECT (pixbuf));
476
477   gtk_image_reset (image);
478
479   if (pixbuf != NULL)
480     {
481       image->storage_type = GTK_IMAGE_PIXBUF;
482
483       image->data.pixbuf.pixbuf = pixbuf;
484
485       gtk_image_update_size (image,
486                              gdk_pixbuf_get_width (pixbuf),
487                              gdk_pixbuf_get_height (pixbuf));
488     }
489 }
490
491 /**
492  * gtk_image_set_from_stock:
493  * @image: a #GtkImage
494  * @stock_id: a stock icon name
495  * @size: a stock icon size
496  *
497  * See gtk_image_new_from_stock for details.
498  * 
499  **/
500 void
501 gtk_image_set_from_stock  (GtkImage       *image,
502                            const gchar    *stock_id,
503                            GtkIconSize     size)
504 {
505   g_return_if_fail (GTK_IS_IMAGE (image));
506   
507   gtk_image_reset (image);
508
509   if (stock_id)
510     {      
511       image->storage_type = GTK_IMAGE_STOCK;
512       
513       image->data.stock.stock_id = g_strdup (stock_id);
514       image->data.stock.size = size;
515
516       /* Size is demand-computed in size request method
517        * if we're a stock image, since changing the
518        * style impacts the size request
519        */
520     }
521 }
522
523 /**
524  * gtk_image_set_from_icon_set:
525  * @image: a #GtkImage
526  * @icon_set: a #GtkIconSet
527  * @size: a stock icon size
528  *
529  * See gtk_image_new_from_icon_set() for details.
530  * 
531  **/
532 void
533 gtk_image_set_from_icon_set  (GtkImage       *image,
534                               GtkIconSet     *icon_set,
535                               GtkIconSize     size)
536 {
537   g_return_if_fail (GTK_IS_IMAGE (image));
538
539   if (icon_set)
540     gtk_icon_set_ref (icon_set);
541   
542   gtk_image_reset (image);
543
544   if (icon_set)
545     {      
546       image->storage_type = GTK_IMAGE_ICON_SET;
547       
548       image->data.icon_set.icon_set = icon_set;
549       image->data.icon_set.size = size;
550
551       /* Size is demand-computed in size request method
552        * if we're an icon set
553        */
554     }
555 }
556
557 /**
558  * gtk_image_set_from_animation:
559  * @image: a #GtkImage
560  * @animation: the #GdkPixbufAnimation
561  * 
562  * Causes the #GtkImage to display the given animation (or display
563  * nothing, if you set the animation to %NULL).
564  **/
565 void
566 gtk_image_set_from_animation (GtkImage           *image,
567                               GdkPixbufAnimation *animation)
568 {
569   g_return_if_fail (GTK_IS_IMAGE (image));
570   g_return_if_fail (animation == NULL ||
571                     GDK_IS_PIXBUF_ANIMATION (animation));
572   
573   if (animation)
574     g_object_ref (G_OBJECT (animation));
575
576   gtk_image_reset (image);
577
578   if (animation != NULL)
579     {
580       image->storage_type = GTK_IMAGE_ANIMATION;
581
582       image->data.anim.anim = animation;
583       image->data.anim.frame_timeout = 0;
584       image->data.anim.iter = NULL;
585       
586       gtk_image_update_size (image,
587                              gdk_pixbuf_animation_get_width (animation),
588                              gdk_pixbuf_animation_get_height (animation));
589     }
590 }
591
592 /**
593  * gtk_image_get_storage_type:
594  * @image: a #GtkImage
595  * 
596  * Gets the type of representation being used by the #GtkImage
597  * to store image data. If the #GtkImage has no image data,
598  * the return value will be %GTK_IMAGE_EMPTY.
599  * 
600  * Return value: image representation being used
601  **/
602 GtkImageType
603 gtk_image_get_storage_type (GtkImage *image)
604 {
605   g_return_val_if_fail (GTK_IS_IMAGE (image), GTK_IMAGE_EMPTY);
606
607   return image->storage_type;
608 }
609
610 /**
611  * gtk_image_get_pixmap:
612  * @image: a #GtkImage
613  * @pixmap: location to store the pixmap, or %NULL
614  * @mask: location to store the mask, or %NULL
615  *
616  * Gets the pixmap and mask being displayed by the #GtkImage.
617  * The storage type of the image must be %GTK_IMAGE_EMPTY or
618  * %GTK_IMAGE_PIXMAP (see gtk_image_get_storage_type()).
619  * The caller of this function does not own a reference to the
620  * returned pixmap and mask.
621  * 
622  **/
623 void
624 gtk_image_get_pixmap (GtkImage   *image,
625                       GdkPixmap **pixmap,
626                       GdkBitmap **mask)
627 {
628   g_return_if_fail (GTK_IS_IMAGE (image)); 
629   g_return_if_fail (image->storage_type == GTK_IMAGE_PIXMAP ||
630                     image->storage_type == GTK_IMAGE_EMPTY);
631   
632   if (pixmap)
633     *pixmap = image->data.pixmap.pixmap;
634   
635   if (mask)
636     *mask = image->data.pixmap.mask;
637 }
638
639 /**
640  * gtk_image_get_image:
641  * @image: a #GtkImage
642  * @gdk_image: return location for a #GtkImage
643  * @mask: return location for a #GdkBitmap
644  * 
645  * Gets the #GdkImage and mask being displayed by the #GtkImage.
646  * The storage type of the image must be %GTK_IMAGE_EMPTY or
647  * %GTK_IMAGE_IMAGE (see gtk_image_get_storage_type()).
648  * The caller of this function does not own a reference to the
649  * returned image and mask.
650  **/
651 void
652 gtk_image_get_image  (GtkImage   *image,
653                       GdkImage  **gdk_image,
654                       GdkBitmap **mask)
655 {
656   g_return_if_fail (GTK_IS_IMAGE (image));
657   g_return_if_fail (image->storage_type == GTK_IMAGE_IMAGE ||
658                     image->storage_type == GTK_IMAGE_EMPTY);
659
660   if (gdk_image)
661     *gdk_image = image->data.image.image;
662   
663   if (mask)
664     *mask = image->data.image.mask;
665 }
666
667 /**
668  * gtk_image_get_pixbuf:
669  * @image: a #GtkImage
670  *
671  *
672  * Gets the #GdkPixbuf being displayed by the #GtkImage.
673  * The storage type of the image must be %GTK_IMAGE_EMPTY or
674  * %GTK_IMAGE_PIXBUF (see gtk_image_get_storage_type()).
675  * The caller of this function does not own a reference to the
676  * returned pixbuf.
677  * 
678  * Return value: the displayed pixbuf, or %NULL if the image is empty
679  **/
680 GdkPixbuf*
681 gtk_image_get_pixbuf (GtkImage *image)
682 {
683   g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
684   g_return_val_if_fail (image->storage_type == GTK_IMAGE_PIXBUF ||
685                         image->storage_type == GTK_IMAGE_EMPTY, NULL);
686
687   if (image->storage_type == GTK_IMAGE_EMPTY)
688     image->data.pixbuf.pixbuf = NULL;
689   
690   return image->data.pixbuf.pixbuf;
691 }
692
693 /**
694  * gtk_image_get_stock:
695  * @image: a #GtkImage
696  * @stock_id: place to store a stock icon name
697  * @size: place to store a stock icon size
698  *
699  * Gets the stock icon name and size being displayed by the #GtkImage.
700  * The storage type of the image must be %GTK_IMAGE_EMPTY or
701  * %GTK_IMAGE_STOCK (see gtk_image_get_storage_type()).
702  * The returned string is owned by the #GtkImage and should not
703  * be freed.
704  * 
705  **/
706 void
707 gtk_image_get_stock  (GtkImage        *image,
708                       gchar          **stock_id,
709                       GtkIconSize     *size)
710 {
711   g_return_if_fail (GTK_IS_IMAGE (image));
712   g_return_if_fail (image->storage_type == GTK_IMAGE_STOCK ||
713                     image->storage_type == GTK_IMAGE_EMPTY);
714
715   if (image->storage_type == GTK_IMAGE_EMPTY)
716     image->data.stock.stock_id = NULL;
717   
718   if (stock_id)
719     *stock_id = image->data.stock.stock_id;
720
721   if (size)
722     *size = image->data.stock.size;
723 }
724
725 /**
726  * gtk_image_get_icon_set:
727  * @image: a #GtkImage
728  * @icon_set: location to store a #GtkIconSet
729  * @size: location to store a stock icon size
730  *
731  * Gets the icon set and size being displayed by the #GtkImage.
732  * The storage type of the image must be %GTK_IMAGE_EMPTY or
733  * %GTK_IMAGE_ICON_SET (see gtk_image_get_storage_type()).
734  * 
735  **/
736 void
737 gtk_image_get_icon_set  (GtkImage        *image,
738                          GtkIconSet     **icon_set,
739                          GtkIconSize     *size)
740 {
741   g_return_if_fail (GTK_IS_IMAGE (image));
742   g_return_if_fail (image->storage_type == GTK_IMAGE_ICON_SET ||
743                     image->storage_type == GTK_IMAGE_EMPTY);
744       
745   if (icon_set)    
746     *icon_set = image->data.icon_set.icon_set;
747
748   if (size)
749     *size = image->data.icon_set.size;
750 }
751
752 /**
753  * gtk_image_get_animation:
754  * @image: a #GtkImage
755  *
756  *
757  * Gets the #GdkPixbufAnimation being displayed by the #GtkImage.
758  * The storage type of the image must be %GTK_IMAGE_EMPTY or
759  * %GTK_IMAGE_ANIMATION (see gtk_image_get_storage_type()).
760  * The caller of this function does not own a reference to the
761  * returned animation.
762  * 
763  * Return value: the displayed animation, or %NULL if the image is empty
764  **/
765 GdkPixbufAnimation*
766 gtk_image_get_animation (GtkImage *image)
767 {
768   g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
769   g_return_val_if_fail (image->storage_type == GTK_IMAGE_ANIMATION ||
770                         image->storage_type == GTK_IMAGE_EMPTY,
771                         NULL);
772
773   if (image->storage_type == GTK_IMAGE_EMPTY)
774     image->data.anim.anim = NULL;
775   
776   return image->data.anim.anim;
777 }
778
779 GtkWidget*
780 gtk_image_new (GdkImage  *val,
781                GdkBitmap *mask)
782 {
783   GtkImage *image;
784
785   g_return_val_if_fail (val != NULL, NULL);
786
787   image = gtk_type_new (GTK_TYPE_IMAGE);
788
789   gtk_image_set (image, val, mask);
790
791   return GTK_WIDGET (image);
792 }
793
794 void
795 gtk_image_set (GtkImage  *image,
796                GdkImage  *val,
797                GdkBitmap *mask)
798 {
799   g_return_if_fail (GTK_IS_IMAGE (image));
800
801   gtk_image_set_from_image (image, val, mask);
802 }
803
804 void
805 gtk_image_get (GtkImage   *image,
806                GdkImage  **val,
807                GdkBitmap **mask)
808 {
809   g_return_if_fail (GTK_IS_IMAGE (image));
810
811   gtk_image_get_image (image, val, mask);
812 }
813
814 static void
815 gtk_image_unmap (GtkWidget *widget)
816 {
817   GtkImage *image;
818
819   image = GTK_IMAGE (widget);
820
821   if (image->storage_type == GTK_IMAGE_ANIMATION)
822     {
823       /* Reset the animation */
824       
825       if (image->data.anim.frame_timeout)
826         {
827           g_source_remove (image->data.anim.frame_timeout);
828           image->data.anim.frame_timeout = 0;
829         }
830
831       if (image->data.anim.iter)
832         {
833           g_object_unref (G_OBJECT (image->data.anim.iter));
834           image->data.anim.iter = NULL;
835         }
836     }
837
838   if (GTK_WIDGET_CLASS (parent_class)->unmap)
839     GTK_WIDGET_CLASS (parent_class)->unmap (widget);
840 }
841
842 gint
843 animation_timeout (gpointer data)
844 {
845   GtkImage *image;
846
847   GDK_THREADS_ENTER ();
848
849   image = GTK_IMAGE (data);
850   
851   image->data.anim.frame_timeout = 0;
852
853   gdk_pixbuf_animation_iter_advance (image->data.anim.iter, NULL);
854
855   if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0)
856     image->data.anim.frame_timeout =
857       g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter),
858                      animation_timeout,
859                      image);
860   
861   gtk_widget_queue_draw (GTK_WIDGET (image));
862
863   GDK_THREADS_LEAVE ();
864
865   return FALSE;
866 }
867
868 static gint
869 gtk_image_expose (GtkWidget      *widget,
870                   GdkEventExpose *event)
871 {
872   g_return_val_if_fail (GTK_IS_IMAGE (widget), FALSE);
873   g_return_val_if_fail (event != NULL, FALSE);
874   
875   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
876       GTK_IMAGE (widget)->storage_type != GTK_IMAGE_EMPTY)
877     {
878       GtkImage *image;
879       GtkMisc *misc;
880       GdkRectangle area, image_bound;
881       gint x, y;
882       GdkBitmap *mask = NULL;
883       GdkPixbuf *stock_pixbuf = NULL;
884       
885       image = GTK_IMAGE (widget);
886       misc = GTK_MISC (widget);
887
888       x = (widget->allocation.x * (1.0 - misc->xalign) +
889            (widget->allocation.x + widget->allocation.width
890             - (widget->requisition.width - misc->xpad * 2)) *
891            misc->xalign) + 0.5;
892       y = (widget->allocation.y * (1.0 - misc->yalign) +
893            (widget->allocation.y + widget->allocation.height
894             - (widget->requisition.height - misc->ypad * 2)) *
895            misc->yalign) + 0.5;
896
897       image_bound.x = x;
898       image_bound.y = y;      
899
900       switch (image->storage_type)
901         {
902         case GTK_IMAGE_PIXMAP:
903           mask = image->data.pixmap.mask;
904           gdk_drawable_get_size (image->data.pixmap.pixmap,
905                                  &image_bound.width,
906                                  &image_bound.height);
907           break;
908
909         case GTK_IMAGE_IMAGE:
910           mask = image->data.image.mask;
911           image_bound.width = image->data.image.image->width;
912           image_bound.height = image->data.image.image->height;
913           break;
914
915         case GTK_IMAGE_PIXBUF:
916           image_bound.width = gdk_pixbuf_get_width (image->data.pixbuf.pixbuf);
917           image_bound.height = gdk_pixbuf_get_height (image->data.pixbuf.pixbuf);
918           break;
919
920         case GTK_IMAGE_STOCK:
921           stock_pixbuf = gtk_widget_render_icon (widget,
922                                                  image->data.stock.stock_id,
923                                                  image->data.stock.size,
924                                                  NULL);
925           if (stock_pixbuf)
926             {              
927               image_bound.width = gdk_pixbuf_get_width (stock_pixbuf);
928               image_bound.height = gdk_pixbuf_get_height (stock_pixbuf);
929             }
930           break;
931
932         case GTK_IMAGE_ICON_SET:
933           stock_pixbuf =
934             gtk_icon_set_render_icon (image->data.icon_set.icon_set,
935                                       widget->style,
936                                       gtk_widget_get_direction (widget),
937                                       GTK_WIDGET_STATE (widget),
938                                       image->data.icon_set.size,
939                                       widget,
940                                       NULL);
941
942           if (stock_pixbuf)
943             {
944               image_bound.width = gdk_pixbuf_get_width (stock_pixbuf);
945               image_bound.height = gdk_pixbuf_get_height (stock_pixbuf);
946             }
947           break;
948
949         case GTK_IMAGE_ANIMATION:
950           {
951             if (image->data.anim.iter == NULL)
952               {
953                 image->data.anim.iter = gdk_pixbuf_animation_get_iter (image->data.anim.anim, NULL);
954                 
955                 if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0)
956                   image->data.anim.frame_timeout =
957                     g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter),
958                                    animation_timeout,
959                                    image);
960               }
961
962             image_bound.width = gdk_pixbuf_animation_get_width (image->data.anim.anim);
963             image_bound.height = gdk_pixbuf_animation_get_height (image->data.anim.anim);
964           }
965           break;
966           
967         default:
968           break;
969         }
970
971       if (mask)
972         {
973           gdk_gc_set_clip_mask (widget->style->black_gc, mask);
974           gdk_gc_set_clip_origin (widget->style->black_gc, x, y);
975         }
976
977       area = event->area;
978       
979       if (gdk_rectangle_intersect (&area, &widget->allocation, &area) &&
980           gdk_rectangle_intersect (&image_bound, &area, &image_bound))
981         {
982           switch (image->storage_type)
983             {
984             case GTK_IMAGE_PIXMAP:
985               gdk_draw_drawable (widget->window,
986                                  widget->style->black_gc,
987                                  image->data.pixmap.pixmap,
988                                  image_bound.x - x, image_bound.y - y,
989                                  image_bound.x, image_bound.y,
990                                  image_bound.width, image_bound.height);
991               break;
992               
993             case GTK_IMAGE_IMAGE:
994               gdk_draw_image (widget->window,
995                               widget->style->black_gc,
996                               image->data.image.image,
997                               image_bound.x - x, image_bound.y - y,
998                               image_bound.x, image_bound.y,
999                               image_bound.width, image_bound.height);
1000               break;
1001
1002             case GTK_IMAGE_PIXBUF:
1003               gdk_pixbuf_render_to_drawable_alpha (image->data.pixbuf.pixbuf,
1004                                                    widget->window,
1005                                                    image_bound.x - x,
1006                                                    image_bound.y - y,
1007                                                    image_bound.x,
1008                                                    image_bound.y,
1009                                                    image_bound.width,
1010                                                    image_bound.height,
1011                                                    GDK_PIXBUF_ALPHA_FULL,
1012                                                    128,
1013                                                    GDK_RGB_DITHER_NORMAL,
1014                                                    0, 0);
1015               break;
1016
1017             case GTK_IMAGE_STOCK: /* fall thru */
1018             case GTK_IMAGE_ICON_SET:
1019               if (stock_pixbuf)
1020                 {
1021                   gdk_pixbuf_render_to_drawable_alpha (stock_pixbuf,
1022                                                        widget->window,
1023                                                        image_bound.x - x,
1024                                                        image_bound.y - y,
1025                                                        image_bound.x,
1026                                                        image_bound.y,
1027                                                        image_bound.width,
1028                                                        image_bound.height,
1029                                                        GDK_PIXBUF_ALPHA_FULL,
1030                                                        128,
1031                                                        GDK_RGB_DITHER_NORMAL,
1032                                                        0, 0);
1033                   
1034                   g_object_unref (G_OBJECT (stock_pixbuf));
1035                 }
1036               break;
1037
1038             case GTK_IMAGE_ANIMATION:
1039               /* don't advance the anim iter here, or we could get frame changes between two
1040                * exposes of different areas.
1041                */
1042               
1043               gdk_pixbuf_render_to_drawable_alpha (gdk_pixbuf_animation_iter_get_pixbuf (image->data.anim.iter),
1044                                                    widget->window,
1045                                                    image_bound.x - x,
1046                                                    image_bound.y - y,
1047                                                    image_bound.x,
1048                                                    image_bound.y,
1049                                                    image_bound.width,
1050                                                    image_bound.height,
1051                                                    GDK_PIXBUF_ALPHA_FULL,
1052                                                    128,
1053                                                    GDK_RGB_DITHER_NORMAL,
1054                                                    0, 0);
1055               break;
1056               
1057             default:
1058               break;
1059             }
1060         } /* if rectangle intersects */      
1061       if (mask)
1062         {
1063           gdk_gc_set_clip_mask (widget->style->black_gc, NULL);
1064           gdk_gc_set_clip_origin (widget->style->black_gc, 0, 0);
1065         }
1066     } /* if widget is drawable */
1067
1068   return FALSE;
1069 }
1070
1071 static void
1072 gtk_image_clear (GtkImage *image)
1073 {
1074   switch (image->storage_type)
1075     {
1076     case GTK_IMAGE_PIXMAP:
1077
1078       if (image->data.pixmap.pixmap)
1079         g_object_unref (G_OBJECT (image->data.pixmap.pixmap));
1080
1081       if (image->data.pixmap.mask)
1082         g_object_unref (G_OBJECT (image->data.pixmap.mask));
1083
1084       break;
1085
1086     case GTK_IMAGE_IMAGE:
1087
1088       if (image->data.image.image)
1089         g_object_unref (G_OBJECT (image->data.image.image));
1090
1091       if (image->data.image.mask)
1092         g_object_unref (G_OBJECT (image->data.image.mask));
1093
1094       break;
1095
1096     case GTK_IMAGE_PIXBUF:
1097
1098       if (image->data.pixbuf.pixbuf)
1099         g_object_unref (G_OBJECT (image->data.pixbuf.pixbuf));
1100
1101       break;
1102
1103     case GTK_IMAGE_STOCK:
1104
1105       g_free (image->data.stock.stock_id);
1106       
1107       break;
1108
1109     case GTK_IMAGE_ICON_SET:
1110       if (image->data.icon_set.icon_set)
1111         gtk_icon_set_unref (image->data.icon_set.icon_set);
1112       
1113       break;
1114
1115     case GTK_IMAGE_ANIMATION:
1116       if (image->data.anim.frame_timeout)
1117         g_source_remove (image->data.anim.frame_timeout);
1118
1119       if (image->data.anim.anim)
1120         g_object_unref (G_OBJECT (image->data.anim.anim));      
1121       break;
1122       
1123     case GTK_IMAGE_EMPTY:
1124     default:
1125       break;
1126       
1127     }
1128
1129   image->storage_type = GTK_IMAGE_EMPTY;
1130
1131   memset (&image->data, '\0', sizeof (image->data));
1132 }
1133
1134 static void
1135 gtk_image_reset (GtkImage *image)
1136 {
1137   gtk_image_clear (image);
1138
1139   gtk_image_update_size (image, 0, 0);
1140 }
1141
1142 static void
1143 gtk_image_size_request (GtkWidget      *widget,
1144                         GtkRequisition *requisition)
1145 {
1146   GtkImage *image;
1147   GdkPixbuf *pixbuf = NULL;
1148   
1149   image = GTK_IMAGE (widget);
1150
1151   /* We update stock/icon set on every size request, because
1152    * the theme could have affected the size; for other kinds of
1153    * image, we just update the requisition when the image data
1154    * is set.
1155    */
1156   
1157   switch (image->storage_type)
1158     {
1159     case GTK_IMAGE_STOCK:
1160       pixbuf = gtk_widget_render_icon (GTK_WIDGET (image),
1161                                        image->data.stock.stock_id,
1162                                        image->data.stock.size,
1163                                        NULL);
1164       break;
1165
1166     case GTK_IMAGE_ICON_SET:
1167       pixbuf = gtk_icon_set_render_icon (image->data.icon_set.icon_set,
1168                                          widget->style,
1169                                          gtk_widget_get_direction (widget),
1170                                          GTK_WIDGET_STATE (widget),
1171                                          image->data.icon_set.size,
1172                                          widget,
1173                                          NULL);
1174       break;
1175       
1176     default:
1177       break;
1178     }
1179
1180   if (pixbuf)
1181     {
1182       GTK_WIDGET (image)->requisition.width = gdk_pixbuf_get_width (pixbuf) + GTK_MISC (image)->xpad * 2;
1183       GTK_WIDGET (image)->requisition.height = gdk_pixbuf_get_height (pixbuf) + GTK_MISC (image)->ypad * 2;
1184
1185       g_object_unref (G_OBJECT (pixbuf));
1186     }
1187
1188   /* Chain up to default that simply reads current requisition */
1189   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
1190 }
1191
1192 static void
1193 gtk_image_update_size (GtkImage *image,
1194                        gint      image_width,
1195                        gint      image_height)
1196 {
1197   GTK_WIDGET (image)->requisition.width = image_width + GTK_MISC (image)->xpad * 2;
1198   GTK_WIDGET (image)->requisition.height = image_height + GTK_MISC (image)->ypad * 2;
1199
1200   if (GTK_WIDGET_VISIBLE (image))
1201     gtk_widget_queue_resize (GTK_WIDGET (image));
1202 }
1203
1204