]> Pileus Git - ~andy/gtk/blob - gtk/gtkimage.c
docs, fixes bug #54144
[~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   image = GTK_IMAGE (data);
848   
849   image->data.anim.frame_timeout = 0;
850
851   gdk_pixbuf_animation_iter_advance (image->data.anim.iter, NULL);
852
853   if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0)
854     image->data.anim.frame_timeout =
855       g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter),
856                      animation_timeout,
857                      image);
858   
859   gtk_widget_queue_draw (GTK_WIDGET (image));
860
861   return FALSE;
862 }
863
864 static gint
865 gtk_image_expose (GtkWidget      *widget,
866                   GdkEventExpose *event)
867 {
868   g_return_val_if_fail (widget != NULL, FALSE);
869   g_return_val_if_fail (GTK_IS_IMAGE (widget), FALSE);
870   g_return_val_if_fail (event != NULL, FALSE);
871   
872   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
873       GTK_IMAGE (widget)->storage_type != GTK_IMAGE_EMPTY)
874     {
875       GtkImage *image;
876       GtkMisc *misc;
877       GdkRectangle area, image_bound, intersection;
878       gint x, y;
879       GdkBitmap *mask = NULL;
880       GdkPixbuf *stock_pixbuf = NULL;
881       
882       image = GTK_IMAGE (widget);
883       misc = GTK_MISC (widget);
884
885       x = (widget->allocation.x * (1.0 - misc->xalign) +
886            (widget->allocation.x + widget->allocation.width
887             - (widget->requisition.width - misc->xpad * 2)) *
888            misc->xalign) + 0.5;
889       y = (widget->allocation.y * (1.0 - misc->yalign) +
890            (widget->allocation.y + widget->allocation.height
891             - (widget->requisition.height - misc->ypad * 2)) *
892            misc->yalign) + 0.5;
893
894       image_bound.x = x;
895       image_bound.y = y;      
896
897       switch (image->storage_type)
898         {
899         case GTK_IMAGE_PIXMAP:
900           mask = image->data.pixmap.mask;
901           gdk_drawable_get_size (image->data.pixmap.pixmap,
902                                  &image_bound.width,
903                                  &image_bound.height);
904           break;
905
906         case GTK_IMAGE_IMAGE:
907           mask = image->data.image.mask;
908           image_bound.width = image->data.image.image->width;
909           image_bound.height = image->data.image.image->height;
910           break;
911
912         case GTK_IMAGE_PIXBUF:
913           image_bound.width = gdk_pixbuf_get_width (image->data.pixbuf.pixbuf);
914           image_bound.height = gdk_pixbuf_get_height (image->data.pixbuf.pixbuf);
915           break;
916
917         case GTK_IMAGE_STOCK:
918           stock_pixbuf = gtk_widget_render_icon (widget,
919                                                  image->data.stock.stock_id,
920                                                  image->data.stock.size,
921                                                  NULL);
922           if (stock_pixbuf)
923             {              
924               image_bound.width = gdk_pixbuf_get_width (stock_pixbuf);
925               image_bound.height = gdk_pixbuf_get_height (stock_pixbuf);
926             }
927           break;
928
929         case GTK_IMAGE_ICON_SET:
930           stock_pixbuf =
931             gtk_icon_set_render_icon (image->data.icon_set.icon_set,
932                                       widget->style,
933                                       gtk_widget_get_direction (widget),
934                                       GTK_WIDGET_STATE (widget),
935                                       image->data.icon_set.size,
936                                       widget,
937                                       NULL);
938
939           if (stock_pixbuf)
940             {
941               image_bound.width = gdk_pixbuf_get_width (stock_pixbuf);
942               image_bound.height = gdk_pixbuf_get_height (stock_pixbuf);
943             }
944           break;
945
946         case GTK_IMAGE_ANIMATION:
947           {
948             if (image->data.anim.iter == NULL)
949               {
950                 image->data.anim.iter = gdk_pixbuf_animation_get_iter (image->data.anim.anim, NULL);
951                 
952                 if (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter) >= 0)
953                   image->data.anim.frame_timeout =
954                     g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (image->data.anim.iter),
955                                    animation_timeout,
956                                    image);
957               }
958
959             image_bound.width = gdk_pixbuf_animation_get_width (image->data.anim.anim);
960             image_bound.height = gdk_pixbuf_animation_get_height (image->data.anim.anim);
961           }
962           break;
963           
964         default:
965           break;
966         }
967
968       if (mask)
969         {
970           gdk_gc_set_clip_mask (widget->style->black_gc, mask);
971           gdk_gc_set_clip_origin (widget->style->black_gc, x, y);
972         }
973
974       area = event->area;
975       
976       if (gdk_rectangle_intersect (&area, &widget->allocation, &area) &&
977           gdk_rectangle_intersect (&image_bound, &area, &image_bound))
978         {
979           switch (image->storage_type)
980             {
981             case GTK_IMAGE_PIXMAP:
982               gdk_draw_drawable (widget->window,
983                                  widget->style->black_gc,
984                                  image->data.pixmap.pixmap,
985                                  image_bound.x - x, image_bound.y - y,
986                                  image_bound.x, image_bound.y,
987                                  image_bound.width, image_bound.height);
988               break;
989               
990             case GTK_IMAGE_IMAGE:
991               gdk_draw_image (widget->window,
992                               widget->style->black_gc,
993                               image->data.image.image,
994                               image_bound.x - x, image_bound.y - y,
995                               image_bound.x, image_bound.y,
996                               image_bound.width, image_bound.height);
997               break;
998
999             case GTK_IMAGE_PIXBUF:
1000               gdk_pixbuf_render_to_drawable_alpha (image->data.pixbuf.pixbuf,
1001                                                    widget->window,
1002                                                    image_bound.x - x,
1003                                                    image_bound.y - y,
1004                                                    image_bound.x,
1005                                                    image_bound.y,
1006                                                    image_bound.width,
1007                                                    image_bound.height,
1008                                                    GDK_PIXBUF_ALPHA_FULL,
1009                                                    128,
1010                                                    GDK_RGB_DITHER_NORMAL,
1011                                                    0, 0);
1012               break;
1013
1014             case GTK_IMAGE_STOCK: /* fall thru */
1015             case GTK_IMAGE_ICON_SET:
1016               if (stock_pixbuf)
1017                 {
1018                   gdk_pixbuf_render_to_drawable_alpha (stock_pixbuf,
1019                                                        widget->window,
1020                                                        image_bound.x - x,
1021                                                        image_bound.y - y,
1022                                                        image_bound.x,
1023                                                        image_bound.y,
1024                                                        image_bound.width,
1025                                                        image_bound.height,
1026                                                        GDK_PIXBUF_ALPHA_FULL,
1027                                                        128,
1028                                                        GDK_RGB_DITHER_NORMAL,
1029                                                        0, 0);
1030                   
1031                   g_object_unref (G_OBJECT (stock_pixbuf));
1032                 }
1033               break;
1034
1035             case GTK_IMAGE_ANIMATION:
1036               /* don't advance the anim iter here, or we could get frame changes between two
1037                * exposes of different areas.
1038                */
1039               
1040               gdk_pixbuf_render_to_drawable_alpha (gdk_pixbuf_animation_iter_get_pixbuf (image->data.anim.iter),
1041                                                    widget->window,
1042                                                    image_bound.x - x,
1043                                                    image_bound.y - y,
1044                                                    image_bound.x,
1045                                                    image_bound.y,
1046                                                    image_bound.width,
1047                                                    image_bound.height,
1048                                                    GDK_PIXBUF_ALPHA_FULL,
1049                                                    128,
1050                                                    GDK_RGB_DITHER_NORMAL,
1051                                                    0, 0);
1052               break;
1053               
1054             default:
1055               break;
1056             }
1057         } /* if rectangle intersects */      
1058       if (mask)
1059         {
1060           gdk_gc_set_clip_mask (widget->style->black_gc, NULL);
1061           gdk_gc_set_clip_origin (widget->style->black_gc, 0, 0);
1062         }
1063     } /* if widget is drawable */
1064
1065   return FALSE;
1066 }
1067
1068 static void
1069 gtk_image_clear (GtkImage *image)
1070 {
1071   switch (image->storage_type)
1072     {
1073     case GTK_IMAGE_PIXMAP:
1074
1075       if (image->data.pixmap.pixmap)
1076         g_object_unref (G_OBJECT (image->data.pixmap.pixmap));
1077
1078       if (image->data.pixmap.mask)
1079         g_object_unref (G_OBJECT (image->data.pixmap.mask));
1080
1081       break;
1082
1083     case GTK_IMAGE_IMAGE:
1084
1085       if (image->data.image.image)
1086         g_object_unref (G_OBJECT (image->data.image.image));
1087
1088       if (image->data.image.mask)
1089         g_object_unref (G_OBJECT (image->data.image.mask));
1090
1091       break;
1092
1093     case GTK_IMAGE_PIXBUF:
1094
1095       if (image->data.pixbuf.pixbuf)
1096         g_object_unref (G_OBJECT (image->data.pixbuf.pixbuf));
1097
1098       break;
1099
1100     case GTK_IMAGE_STOCK:
1101
1102       g_free (image->data.stock.stock_id);
1103       
1104       break;
1105
1106     case GTK_IMAGE_ICON_SET:
1107       if (image->data.icon_set.icon_set)
1108         gtk_icon_set_unref (image->data.icon_set.icon_set);
1109       
1110       break;
1111
1112     case GTK_IMAGE_ANIMATION:
1113       if (image->data.anim.frame_timeout)
1114         g_source_remove (image->data.anim.frame_timeout);
1115
1116       if (image->data.anim.anim)
1117         g_object_unref (G_OBJECT (image->data.anim.anim));      
1118       break;
1119       
1120     case GTK_IMAGE_EMPTY:
1121     default:
1122       break;
1123       
1124     }
1125
1126   image->storage_type = GTK_IMAGE_EMPTY;
1127
1128   memset (&image->data, '\0', sizeof (image->data));
1129 }
1130
1131 static void
1132 gtk_image_reset (GtkImage *image)
1133 {
1134   gtk_image_clear (image);
1135
1136   gtk_image_update_size (image, 0, 0);
1137 }
1138
1139 static void
1140 gtk_image_size_request (GtkWidget      *widget,
1141                         GtkRequisition *requisition)
1142 {
1143   GtkImage *image;
1144   GdkPixbuf *pixbuf = NULL;
1145   
1146   image = GTK_IMAGE (widget);
1147
1148   /* We update stock/icon set on every size request, because
1149    * the theme could have affected the size; for other kinds of
1150    * image, we just update the requisition when the image data
1151    * is set.
1152    */
1153   
1154   switch (image->storage_type)
1155     {
1156     case GTK_IMAGE_STOCK:
1157       pixbuf = gtk_widget_render_icon (GTK_WIDGET (image),
1158                                        image->data.stock.stock_id,
1159                                        image->data.stock.size,
1160                                        NULL);
1161       break;
1162
1163     case GTK_IMAGE_ICON_SET:
1164       pixbuf = gtk_icon_set_render_icon (image->data.icon_set.icon_set,
1165                                          widget->style,
1166                                          gtk_widget_get_direction (widget),
1167                                          GTK_WIDGET_STATE (widget),
1168                                          image->data.icon_set.size,
1169                                          widget,
1170                                          NULL);
1171       break;
1172       
1173     default:
1174       break;
1175     }
1176
1177   if (pixbuf)
1178     {
1179       GTK_WIDGET (image)->requisition.width = gdk_pixbuf_get_width (pixbuf) + GTK_MISC (image)->xpad * 2;
1180       GTK_WIDGET (image)->requisition.height = gdk_pixbuf_get_height (pixbuf) + GTK_MISC (image)->ypad * 2;
1181
1182       g_object_unref (G_OBJECT (pixbuf));
1183     }
1184
1185   /* Chain up to default that simply reads current requisition */
1186   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
1187 }
1188
1189 static void
1190 gtk_image_update_size (GtkImage *image,
1191                        gint      image_width,
1192                        gint      image_height)
1193 {
1194   GTK_WIDGET (image)->requisition.width = image_width + GTK_MISC (image)->xpad * 2;
1195   GTK_WIDGET (image)->requisition.height = image_height + GTK_MISC (image)->ypad * 2;
1196
1197   if (GTK_WIDGET_VISIBLE (image))
1198     gtk_widget_queue_resize (GTK_WIDGET (image));
1199 }
1200
1201