]> Pileus Git - ~andy/gtk/blob - gtk/gtkimagemenuitem.c
Use GtkFooPrivate instead GtkFooPriv
[~andy/gtk] / gtk / gtkimagemenuitem.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2001 Red Hat, Inc.
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 "config.h"
28 #include "gtkimagemenuitem.h"
29 #include "gtkaccellabel.h"
30 #include "gtkintl.h"
31 #include "gtkstock.h"
32 #include "gtkiconfactory.h"
33 #include "gtkimage.h"
34 #include "gtkmenubar.h"
35 #include "gtkcontainer.h"
36 #include "gtkwindow.h"
37 #include "gtkactivatable.h"
38 #include "gtkprivate.h"
39
40
41
42 struct _GtkImageMenuItemPrivate
43 {
44   GtkWidget     *image;
45
46   gchar         *label;
47   guint          use_stock         : 1;
48   guint          always_show_image : 1;
49 };
50
51 enum {
52   PROP_0,
53   PROP_IMAGE,
54   PROP_USE_STOCK,
55   PROP_ACCEL_GROUP,
56   PROP_ALWAYS_SHOW_IMAGE
57 };
58
59 static GtkActivatableIface *parent_activatable_iface;
60
61 static void gtk_image_menu_item_destroy              (GtkObject        *object);
62 static void gtk_image_menu_item_size_request         (GtkWidget        *widget,
63                                                       GtkRequisition   *requisition);
64 static void gtk_image_menu_item_size_allocate        (GtkWidget        *widget,
65                                                       GtkAllocation    *allocation);
66 static void gtk_image_menu_item_map                  (GtkWidget        *widget);
67 static void gtk_image_menu_item_remove               (GtkContainer     *container,
68                                                       GtkWidget        *child);
69 static void gtk_image_menu_item_toggle_size_request  (GtkMenuItem      *menu_item,
70                                                       gint             *requisition);
71 static void gtk_image_menu_item_set_label            (GtkMenuItem      *menu_item,
72                                                       const gchar      *label);
73 static G_CONST_RETURN gchar *gtk_image_menu_item_get_label (GtkMenuItem *menu_item);
74
75 static void gtk_image_menu_item_forall               (GtkContainer    *container,
76                                                       gboolean         include_internals,
77                                                       GtkCallback      callback,
78                                                       gpointer         callback_data);
79
80 static void gtk_image_menu_item_finalize             (GObject         *object);
81 static void gtk_image_menu_item_set_property         (GObject         *object,
82                                                       guint            prop_id,
83                                                       const GValue    *value,
84                                                       GParamSpec      *pspec);
85 static void gtk_image_menu_item_get_property         (GObject         *object,
86                                                       guint            prop_id,
87                                                       GValue          *value,
88                                                       GParamSpec      *pspec);
89 static void gtk_image_menu_item_screen_changed       (GtkWidget        *widget,
90                                                       GdkScreen        *previous_screen);
91
92 static void gtk_image_menu_item_recalculate          (GtkImageMenuItem *image_menu_item);
93
94 static void gtk_image_menu_item_activatable_interface_init (GtkActivatableIface  *iface);
95 static void gtk_image_menu_item_update                     (GtkActivatable       *activatable,
96                                                             GtkAction            *action,
97                                                             const gchar          *property_name);
98 static void gtk_image_menu_item_sync_action_properties     (GtkActivatable       *activatable,
99                                                             GtkAction            *action);
100
101
102 G_DEFINE_TYPE_WITH_CODE (GtkImageMenuItem, gtk_image_menu_item, GTK_TYPE_MENU_ITEM,
103                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
104                                                 gtk_image_menu_item_activatable_interface_init))
105
106
107 static void
108 gtk_image_menu_item_class_init (GtkImageMenuItemClass *klass)
109 {
110   GObjectClass *gobject_class = (GObjectClass*) klass;
111   GtkObjectClass *object_class = (GtkObjectClass*) klass;
112   GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
113   GtkMenuItemClass *menu_item_class = (GtkMenuItemClass*) klass;
114   GtkContainerClass *container_class = (GtkContainerClass*) klass;
115
116   object_class->destroy = gtk_image_menu_item_destroy;
117
118   widget_class->screen_changed = gtk_image_menu_item_screen_changed;
119   widget_class->size_request = gtk_image_menu_item_size_request;
120   widget_class->size_allocate = gtk_image_menu_item_size_allocate;
121   widget_class->map = gtk_image_menu_item_map;
122
123   container_class->forall = gtk_image_menu_item_forall;
124   container_class->remove = gtk_image_menu_item_remove;
125   
126   menu_item_class->toggle_size_request = gtk_image_menu_item_toggle_size_request;
127   menu_item_class->set_label           = gtk_image_menu_item_set_label;
128   menu_item_class->get_label           = gtk_image_menu_item_get_label;
129
130   gobject_class->finalize     = gtk_image_menu_item_finalize;
131   gobject_class->set_property = gtk_image_menu_item_set_property;
132   gobject_class->get_property = gtk_image_menu_item_get_property;
133   
134   g_object_class_install_property (gobject_class,
135                                    PROP_IMAGE,
136                                    g_param_spec_object ("image",
137                                                         P_("Image widget"),
138                                                         P_("Child widget to appear next to the menu text"),
139                                                         GTK_TYPE_WIDGET,
140                                                         GTK_PARAM_READWRITE));
141   /**
142    * GtkImageMenuItem:use-stock:
143    *
144    * If %TRUE, the label set in the menuitem is used as a
145    * stock id to select the stock item for the item.
146    *
147    * Since: 2.16
148    **/
149   g_object_class_install_property (gobject_class,
150                                    PROP_USE_STOCK,
151                                    g_param_spec_boolean ("use-stock",
152                                                          P_("Use stock"),
153                                                          P_("Whether to use the label text to create a stock menu item"),
154                                                          FALSE,
155                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
156
157   /**
158    * GtkImageMenuItem:always-show-image:
159    *
160    * If %TRUE, the menu item will ignore the #GtkSettings:gtk-menu-images 
161    * setting and always show the image, if available.
162    *
163    * Use this property if the menuitem would be useless or hard to use
164    * without the image. 
165    *
166    * Since: 2.16
167    **/
168   g_object_class_install_property (gobject_class,
169                                    PROP_ALWAYS_SHOW_IMAGE,
170                                    g_param_spec_boolean ("always-show-image",
171                                                          P_("Always show image"),
172                                                          P_("Whether the image will always be shown"),
173                                                          FALSE,
174                                                          GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
175
176   /**
177    * GtkImageMenuItem:accel-group:
178    *
179    * The Accel Group to use for stock accelerator keys
180    *
181    * Since: 2.16
182    **/
183   g_object_class_install_property (gobject_class,
184                                    PROP_ACCEL_GROUP,
185                                    g_param_spec_object ("accel-group",
186                                                         P_("Accel Group"),
187                                                         P_("The Accel Group to use for stock accelerator keys"),
188                                                         GTK_TYPE_ACCEL_GROUP,
189                                                         GTK_PARAM_WRITABLE));
190
191   gtk_settings_install_property (g_param_spec_boolean ("gtk-menu-images",
192                                                        P_("Show menu images"),
193                                                        P_("Whether images should be shown in menus"),
194                                                        TRUE,
195                                                        GTK_PARAM_READWRITE));
196
197   g_type_class_add_private (object_class, sizeof (GtkImageMenuItemPrivate));
198 }
199
200 static void
201 gtk_image_menu_item_init (GtkImageMenuItem *image_menu_item)
202 {
203   GtkImageMenuItemPrivate *priv;
204
205   image_menu_item->priv = G_TYPE_INSTANCE_GET_PRIVATE (image_menu_item,
206                                                        GTK_TYPE_IMAGE_MENU_ITEM,
207                                                        GtkImageMenuItemPrivate);
208   priv = image_menu_item->priv;
209
210   priv->image = NULL;
211   priv->use_stock = FALSE;
212   priv->label  = NULL;
213 }
214
215 static void 
216 gtk_image_menu_item_finalize (GObject *object)
217 {
218   GtkImageMenuItemPrivate *priv = GTK_IMAGE_MENU_ITEM (object)->priv;
219
220   g_free (priv->label);
221   priv->label  = NULL;
222
223   G_OBJECT_CLASS (gtk_image_menu_item_parent_class)->finalize (object);
224 }
225
226 static void
227 gtk_image_menu_item_set_property (GObject         *object,
228                                   guint            prop_id,
229                                   const GValue    *value,
230                                   GParamSpec      *pspec)
231 {
232   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (object);
233   
234   switch (prop_id)
235     {
236     case PROP_IMAGE:
237       gtk_image_menu_item_set_image (image_menu_item, (GtkWidget *) g_value_get_object (value));
238       break;
239     case PROP_USE_STOCK:
240       gtk_image_menu_item_set_use_stock (image_menu_item, g_value_get_boolean (value));
241       break;
242     case PROP_ALWAYS_SHOW_IMAGE:
243       gtk_image_menu_item_set_always_show_image (image_menu_item, g_value_get_boolean (value));
244       break;
245     case PROP_ACCEL_GROUP:
246       gtk_image_menu_item_set_accel_group (image_menu_item, g_value_get_object (value));
247       break;
248     default:
249       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
250       break;
251     }
252 }
253
254 static void
255 gtk_image_menu_item_get_property (GObject         *object,
256                                   guint            prop_id,
257                                   GValue          *value,
258                                   GParamSpec      *pspec)
259 {
260   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (object);
261   
262   switch (prop_id)
263     {
264     case PROP_IMAGE:
265       g_value_set_object (value, gtk_image_menu_item_get_image (image_menu_item));
266       break;
267     case PROP_USE_STOCK:
268       g_value_set_boolean (value, gtk_image_menu_item_get_use_stock (image_menu_item));      
269       break;
270     case PROP_ALWAYS_SHOW_IMAGE:
271       g_value_set_boolean (value, gtk_image_menu_item_get_always_show_image (image_menu_item));
272       break;
273     default:
274       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
275       break;
276     }
277 }
278
279 static gboolean
280 show_image (GtkImageMenuItem *image_menu_item)
281 {
282   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
283   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (image_menu_item));
284   gboolean show;
285
286   if (priv->always_show_image)
287     show = TRUE;
288   else
289     g_object_get (settings, "gtk-menu-images", &show, NULL);
290
291   return show;
292 }
293
294 static void
295 gtk_image_menu_item_map (GtkWidget *widget)
296 {
297   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (widget);
298   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
299
300   GTK_WIDGET_CLASS (gtk_image_menu_item_parent_class)->map (widget);
301
302   if (priv->image)
303     g_object_set (priv->image,
304                   "visible", show_image (image_menu_item),
305                   NULL);
306 }
307
308 static void
309 gtk_image_menu_item_destroy (GtkObject *object)
310 {
311   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (object);
312   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
313
314   if (priv->image)
315     gtk_container_remove (GTK_CONTAINER (image_menu_item),
316                           priv->image);
317
318   GTK_OBJECT_CLASS (gtk_image_menu_item_parent_class)->destroy (object);
319 }
320
321 static void
322 gtk_image_menu_item_toggle_size_request (GtkMenuItem *menu_item,
323                                          gint        *requisition)
324 {
325   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (menu_item);
326   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
327   GtkPackDirection pack_dir;
328   GtkWidget *parent;
329   GtkWidget *widget = GTK_WIDGET (menu_item);
330
331   parent = gtk_widget_get_parent (widget);
332
333   if (GTK_IS_MENU_BAR (parent))
334     pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
335   else
336     pack_dir = GTK_PACK_DIRECTION_LTR;
337
338   *requisition = 0;
339
340   if (priv->image && gtk_widget_get_visible (priv->image))
341     {
342       GtkRequisition image_requisition;
343       guint toggle_spacing;
344       gtk_widget_get_child_requisition (priv->image,
345                                         &image_requisition);
346
347       gtk_widget_style_get (GTK_WIDGET (menu_item),
348                             "toggle-spacing", &toggle_spacing,
349                             NULL);
350       
351       if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL)
352         {
353           if (image_requisition.width > 0)
354             *requisition = image_requisition.width + toggle_spacing;
355         }
356       else
357         {
358           if (image_requisition.height > 0)
359             *requisition = image_requisition.height + toggle_spacing;
360         }
361     }
362 }
363
364 static void
365 gtk_image_menu_item_recalculate (GtkImageMenuItem *image_menu_item)
366 {
367   GtkImageMenuItemPrivate    *priv = image_menu_item->priv;
368   GtkStockItem             stock_item;
369   GtkWidget               *image;
370   const gchar             *resolved_label = priv->label;
371
372   if (priv->use_stock && priv->label)
373     {
374
375       if (!priv->image)
376         {
377           image = gtk_image_new_from_stock (priv->label, GTK_ICON_SIZE_MENU);
378           gtk_image_menu_item_set_image (image_menu_item, image);
379         }
380
381       if (gtk_stock_lookup (priv->label, &stock_item))
382           resolved_label = stock_item.label;
383
384         gtk_menu_item_set_use_underline (GTK_MENU_ITEM (image_menu_item), TRUE);
385     }
386
387   GTK_MENU_ITEM_CLASS
388     (gtk_image_menu_item_parent_class)->set_label (GTK_MENU_ITEM (image_menu_item), resolved_label);
389
390 }
391
392 static void 
393 gtk_image_menu_item_set_label (GtkMenuItem      *menu_item,
394                                const gchar      *label)
395 {
396   GtkImageMenuItemPrivate *priv = GTK_IMAGE_MENU_ITEM (menu_item)->priv;
397
398   if (priv->label != label)
399     {
400       g_free (priv->label);
401       priv->label = g_strdup (label);
402
403       gtk_image_menu_item_recalculate (GTK_IMAGE_MENU_ITEM (menu_item));
404
405       g_object_notify (G_OBJECT (menu_item), "label");
406
407     }
408 }
409
410 static G_CONST_RETURN gchar *
411 gtk_image_menu_item_get_label (GtkMenuItem *menu_item)
412 {
413   GtkImageMenuItemPrivate *priv = GTK_IMAGE_MENU_ITEM (menu_item)->priv;
414
415   return priv->label;
416 }
417
418 static void
419 gtk_image_menu_item_size_request (GtkWidget      *widget,
420                                   GtkRequisition *requisition)
421 {
422   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (widget);
423   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
424   gint child_width = 0;
425   gint child_height = 0;
426   GtkPackDirection pack_dir;
427   GtkWidget *parent;
428
429   parent = gtk_widget_get_parent (widget);
430
431   if (GTK_IS_MENU_BAR (parent))
432     pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
433   else
434     pack_dir = GTK_PACK_DIRECTION_LTR;
435
436   if (priv->image && gtk_widget_get_visible (priv->image))
437     {
438       GtkRequisition child_requisition;
439
440       gtk_widget_size_request (priv->image,
441                                &child_requisition);
442
443       child_width = child_requisition.width;
444       child_height = child_requisition.height;
445     }
446
447   GTK_WIDGET_CLASS (gtk_image_menu_item_parent_class)->size_request (widget, requisition);
448
449   /* not done with height since that happens via the
450    * toggle_size_request
451    */
452   if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL)
453     requisition->height = MAX (requisition->height, child_height);
454   else
455     requisition->width = MAX (requisition->width, child_width);
456     
457   
458   /* Note that GtkMenuShell always size requests before
459    * toggle_size_request, so toggle_size_request will be able to use
460    * priv->image->requisition
461    */
462 }
463
464 static void
465 gtk_image_menu_item_size_allocate (GtkWidget     *widget,
466                                    GtkAllocation *allocation)
467 {
468   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (widget);
469   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
470   GtkAllocation widget_allocation;
471   GtkPackDirection pack_dir;
472   GtkWidget *parent;
473
474   parent = gtk_widget_get_parent (widget);
475
476   if (GTK_IS_MENU_BAR (parent))
477     pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
478   else
479     pack_dir = GTK_PACK_DIRECTION_LTR;
480
481   GTK_WIDGET_CLASS (gtk_image_menu_item_parent_class)->size_allocate (widget, allocation);
482
483   if (priv->image && gtk_widget_get_visible (priv->image))
484     {
485       gint x, y, offset;
486       GtkRequisition child_requisition;
487       GtkAllocation child_allocation;
488       guint horizontal_padding, toggle_spacing;
489
490       gtk_widget_style_get (widget,
491                             "horizontal-padding", &horizontal_padding,
492                             "toggle-spacing", &toggle_spacing,
493                             NULL);
494       
495       /* Man this is lame hardcoding action, but I can't
496        * come up with a solution that's really better.
497        */
498
499       gtk_widget_get_child_requisition (priv->image,
500                                         &child_requisition);
501
502       gtk_widget_get_allocation (widget, &widget_allocation);
503
504       if (pack_dir == GTK_PACK_DIRECTION_LTR ||
505           pack_dir == GTK_PACK_DIRECTION_RTL)
506         {
507           offset = gtk_container_get_border_width (GTK_CONTAINER (image_menu_item)) +
508                    gtk_widget_get_style (widget)->xthickness;
509
510           if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) ==
511               (pack_dir == GTK_PACK_DIRECTION_LTR))
512             x = offset + horizontal_padding +
513               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
514                toggle_spacing - child_requisition.width) / 2;
515           else
516             x = widget_allocation.width - offset - horizontal_padding -
517               GTK_MENU_ITEM (image_menu_item)->toggle_size + toggle_spacing +
518               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
519                toggle_spacing - child_requisition.width) / 2;
520
521           y = (widget_allocation.height - child_requisition.height) / 2;
522         }
523       else
524         {
525           offset = gtk_container_get_border_width (GTK_CONTAINER (image_menu_item)) +
526                    gtk_widget_get_style (widget)->ythickness;
527
528           if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) ==
529               (pack_dir == GTK_PACK_DIRECTION_TTB))
530             y = offset + horizontal_padding +
531               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
532                toggle_spacing - child_requisition.height) / 2;
533           else
534             y = widget_allocation.height - offset - horizontal_padding -
535               GTK_MENU_ITEM (image_menu_item)->toggle_size + toggle_spacing +
536               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
537                toggle_spacing - child_requisition.height) / 2;
538
539           x = (widget_allocation.width - child_requisition.width) / 2;
540         }
541       
542       child_allocation.width = child_requisition.width;
543       child_allocation.height = child_requisition.height;
544       child_allocation.x = widget_allocation.x + MAX (x, 0);
545       child_allocation.y = widget_allocation.y + MAX (y, 0);
546
547       gtk_widget_size_allocate (priv->image, &child_allocation);
548     }
549 }
550
551 static void
552 gtk_image_menu_item_forall (GtkContainer   *container,
553                             gboolean        include_internals,
554                             GtkCallback     callback,
555                             gpointer        callback_data)
556 {
557   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (container);
558   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
559
560   GTK_CONTAINER_CLASS (gtk_image_menu_item_parent_class)->forall (container,
561                                                                   include_internals,
562                                                                   callback,
563                                                                   callback_data);
564
565   if (include_internals && priv->image)
566     (* callback) (priv->image, callback_data);
567 }
568
569
570 static void 
571 gtk_image_menu_item_activatable_interface_init (GtkActivatableIface  *iface)
572 {
573   parent_activatable_iface = g_type_interface_peek_parent (iface);
574   iface->update = gtk_image_menu_item_update;
575   iface->sync_action_properties = gtk_image_menu_item_sync_action_properties;
576 }
577
578 static gboolean
579 activatable_update_stock_id (GtkImageMenuItem *image_menu_item, GtkAction *action)
580 {
581   GtkWidget   *image;
582   const gchar *stock_id  = gtk_action_get_stock_id (action);
583
584   image = gtk_image_menu_item_get_image (image_menu_item);
585           
586   if (GTK_IS_IMAGE (image) &&
587       stock_id && gtk_icon_factory_lookup_default (stock_id))
588     {
589       gtk_image_set_from_stock (GTK_IMAGE (image), stock_id, GTK_ICON_SIZE_MENU);
590       return TRUE;
591     }
592
593   return FALSE;
594 }
595
596 static gboolean
597 activatable_update_gicon (GtkImageMenuItem *image_menu_item, GtkAction *action)
598 {
599   GtkWidget   *image;
600   GIcon       *icon = gtk_action_get_gicon (action);
601   const gchar *stock_id = gtk_action_get_stock_id (action);
602
603   image = gtk_image_menu_item_get_image (image_menu_item);
604
605   if (icon && GTK_IS_IMAGE (image) &&
606       !(stock_id && gtk_icon_factory_lookup_default (stock_id)))
607     {
608       gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU);
609       return TRUE;
610     }
611
612   return FALSE;
613 }
614
615 static void
616 activatable_update_icon_name (GtkImageMenuItem *image_menu_item, GtkAction *action)
617 {
618   GtkWidget   *image;
619   const gchar *icon_name = gtk_action_get_icon_name (action);
620
621   image = gtk_image_menu_item_get_image (image_menu_item);
622           
623   if (GTK_IS_IMAGE (image) && 
624       (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY ||
625        gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME))
626     {
627       gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_MENU);
628     }
629 }
630
631 static void
632 gtk_image_menu_item_update (GtkActivatable *activatable,
633                             GtkAction      *action,
634                             const gchar    *property_name)
635 {
636   GtkImageMenuItem *image_menu_item;
637   gboolean   use_appearance;
638
639   image_menu_item = GTK_IMAGE_MENU_ITEM (activatable);
640
641   parent_activatable_iface->update (activatable, action, property_name);
642
643   use_appearance = gtk_activatable_get_use_action_appearance (activatable);
644   if (!use_appearance)
645     return;
646
647   if (strcmp (property_name, "stock-id") == 0)
648     activatable_update_stock_id (image_menu_item, action);
649   else if (strcmp (property_name, "gicon") == 0)
650     activatable_update_gicon (image_menu_item, action);
651   else if (strcmp (property_name, "icon-name") == 0)
652     activatable_update_icon_name (image_menu_item, action);
653 }
654
655 static void 
656 gtk_image_menu_item_sync_action_properties (GtkActivatable *activatable,
657                                             GtkAction      *action)
658 {
659   GtkImageMenuItem *image_menu_item;
660   GtkWidget *image;
661   gboolean   use_appearance;
662
663   image_menu_item = GTK_IMAGE_MENU_ITEM (activatable);
664
665   parent_activatable_iface->sync_action_properties (activatable, action);
666
667   if (!action)
668     return;
669
670   use_appearance = gtk_activatable_get_use_action_appearance (activatable);
671   if (!use_appearance)
672     return;
673
674   image = gtk_image_menu_item_get_image (image_menu_item);
675   if (image && !GTK_IS_IMAGE (image))
676     {
677       gtk_image_menu_item_set_image (image_menu_item, NULL);
678       image = NULL;
679     }
680   
681   if (!image)
682     {
683       image = gtk_image_new ();
684       gtk_widget_show (image);
685       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (activatable),
686                                      image);
687     }
688   
689   if (!activatable_update_stock_id (image_menu_item, action) &&
690       !activatable_update_gicon (image_menu_item, action))
691     activatable_update_icon_name (image_menu_item, action);
692
693   gtk_image_menu_item_set_always_show_image (image_menu_item,
694                                              gtk_action_get_always_show_image (action));
695 }
696
697
698 /**
699  * gtk_image_menu_item_new:
700  * @returns: a new #GtkImageMenuItem.
701  *
702  * Creates a new #GtkImageMenuItem with an empty label.
703  **/
704 GtkWidget*
705 gtk_image_menu_item_new (void)
706 {
707   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
708 }
709
710 /**
711  * gtk_image_menu_item_new_with_label:
712  * @label: the text of the menu item.
713  * @returns: a new #GtkImageMenuItem.
714  *
715  * Creates a new #GtkImageMenuItem containing a label. 
716  **/
717 GtkWidget*
718 gtk_image_menu_item_new_with_label (const gchar *label)
719 {
720   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, 
721                        "label", label,
722                        NULL);
723 }
724
725
726 /**
727  * gtk_image_menu_item_new_with_mnemonic:
728  * @label: the text of the menu item, with an underscore in front of the
729  *         mnemonic character
730  * @returns: a new #GtkImageMenuItem
731  *
732  * Creates a new #GtkImageMenuItem containing a label. The label
733  * will be created using gtk_label_new_with_mnemonic(), so underscores
734  * in @label indicate the mnemonic for the menu item.
735  **/
736 GtkWidget*
737 gtk_image_menu_item_new_with_mnemonic (const gchar *label)
738 {
739   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, 
740                        "use-underline", TRUE,
741                        "label", label,
742                        NULL);
743 }
744
745 /**
746  * gtk_image_menu_item_new_from_stock:
747  * @stock_id: the name of the stock item.
748  * @accel_group: (allow-none): the #GtkAccelGroup to add the menu items 
749  *   accelerator to, or %NULL.
750  * @returns: a new #GtkImageMenuItem.
751  *
752  * Creates a new #GtkImageMenuItem containing the image and text from a 
753  * stock item. Some stock ids have preprocessor macros like #GTK_STOCK_OK 
754  * and #GTK_STOCK_APPLY.
755  *
756  * If you want this menu item to have changeable accelerators, then pass in
757  * %NULL for accel_group. Next call gtk_menu_item_set_accel_path() with an
758  * appropriate path for the menu item, use gtk_stock_lookup() to look up the
759  * standard accelerator for the stock item, and if one is found, call
760  * gtk_accel_map_add_entry() to register it.
761  **/
762 GtkWidget*
763 gtk_image_menu_item_new_from_stock (const gchar      *stock_id,
764                                     GtkAccelGroup    *accel_group)
765 {
766   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, 
767                        "label", stock_id,
768                        "use-stock", TRUE,
769                        "accel-group", accel_group,
770                        NULL);
771 }
772
773 /**
774  * gtk_image_menu_item_set_use_stock:
775  * @image_menu_item: a #GtkImageMenuItem
776  * @use_stock: %TRUE if the menuitem should use a stock item
777  *
778  * If %TRUE, the label set in the menuitem is used as a
779  * stock id to select the stock item for the item.
780  *
781  * Since: 2.16
782  */
783 void
784 gtk_image_menu_item_set_use_stock (GtkImageMenuItem *image_menu_item,
785                                    gboolean          use_stock)
786 {
787   GtkImageMenuItemPrivate *priv;
788
789   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
790
791   priv = image_menu_item->priv;
792
793   if (priv->use_stock != use_stock)
794     {
795       priv->use_stock = use_stock;
796
797       gtk_image_menu_item_recalculate (image_menu_item);
798
799       g_object_notify (G_OBJECT (image_menu_item), "use-stock");
800     }
801 }
802
803 /**
804  * gtk_image_menu_item_get_use_stock:
805  * @image_menu_item: a #GtkImageMenuItem
806  *
807  * Checks whether the label set in the menuitem is used as a
808  * stock id to select the stock item for the item.
809  *
810  * Returns: %TRUE if the label set in the menuitem is used as a
811  *     stock id to select the stock item for the item
812  *
813  * Since: 2.16
814  */
815 gboolean
816 gtk_image_menu_item_get_use_stock (GtkImageMenuItem *image_menu_item)
817 {
818   g_return_val_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item), FALSE);
819
820   return image_menu_item->priv->use_stock;
821 }
822
823 /**
824  * gtk_image_menu_item_set_always_show_image:
825  * @image_menu_item: a #GtkImageMenuItem
826  * @always_show: %TRUE if the menuitem should always show the image
827  *
828  * If %TRUE, the menu item will ignore the #GtkSettings:gtk-menu-images 
829  * setting and always show the image, if available.
830  *
831  * Use this property if the menuitem would be useless or hard to use
832  * without the image. 
833  * 
834  * Since: 2.16
835  */
836 void
837 gtk_image_menu_item_set_always_show_image (GtkImageMenuItem *image_menu_item,
838                                            gboolean          always_show)
839 {
840   GtkImageMenuItemPrivate *priv;
841
842   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
843
844   priv = image_menu_item->priv;
845
846   if (priv->always_show_image != always_show)
847     {
848       priv->always_show_image = always_show;
849
850       if (priv->image)
851         {
852           if (show_image (image_menu_item))
853             gtk_widget_show (priv->image);
854           else
855             gtk_widget_hide (priv->image);
856         }
857
858       g_object_notify (G_OBJECT (image_menu_item), "always-show-image");
859     }
860 }
861
862 /**
863  * gtk_image_menu_item_get_always_show_image:
864  * @image_menu_item: a #GtkImageMenuItem
865  *
866  * Returns whether the menu item will ignore the #GtkSettings:gtk-menu-images
867  * setting and always show the image, if available.
868  * 
869  * Returns: %TRUE if the menu item will always show the image
870  *
871  * Since: 2.16
872  */
873 gboolean
874 gtk_image_menu_item_get_always_show_image (GtkImageMenuItem *image_menu_item)
875 {
876   g_return_val_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item), FALSE);
877
878   return image_menu_item->priv->always_show_image;
879 }
880
881
882 /**
883  * gtk_image_menu_item_set_accel_group:
884  * @image_menu_item: a #GtkImageMenuItem
885  * @accel_group: the #GtkAccelGroup
886  *
887  * Specifies an @accel_group to add the menu items accelerator to
888  * (this only applies to stock items so a stock item must already
889  * be set, make sure to call gtk_image_menu_item_set_use_stock()
890  * and gtk_menu_item_set_label() with a valid stock item first).
891  *
892  * If you want this menu item to have changeable accelerators then
893  * you shouldnt need this (see gtk_image_menu_item_new_from_stock()).
894  *
895  * Since: 2.16
896  */
897 void
898 gtk_image_menu_item_set_accel_group (GtkImageMenuItem *image_menu_item, 
899                                      GtkAccelGroup    *accel_group)
900 {
901   GtkImageMenuItemPrivate    *priv;
902   GtkStockItem             stock_item;
903
904   /* Silent return for the constructor */
905   if (!accel_group) 
906     return;
907   
908   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
909   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
910
911   priv = image_menu_item->priv;
912
913   if (priv->use_stock && priv->label && gtk_stock_lookup (priv->label, &stock_item))
914     if (stock_item.keyval)
915       {
916         gtk_widget_add_accelerator (GTK_WIDGET (image_menu_item),
917                                     "activate",
918                                     accel_group,
919                                     stock_item.keyval,
920                                     stock_item.modifier,
921                                     GTK_ACCEL_VISIBLE);
922         
923         g_object_notify (G_OBJECT (image_menu_item), "accel-group");
924       }
925 }
926
927 /** 
928  * gtk_image_menu_item_set_image:
929  * @image_menu_item: a #GtkImageMenuItem.
930  * @image: (allow-none): a widget to set as the image for the menu item.
931  *
932  * Sets the image of @image_menu_item to the given widget.
933  * Note that it depends on the show-menu-images setting whether
934  * the image will be displayed or not.
935  **/ 
936 void
937 gtk_image_menu_item_set_image (GtkImageMenuItem *image_menu_item,
938                                GtkWidget        *image)
939 {
940   GtkImageMenuItemPrivate *priv;
941
942   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
943
944   priv = image_menu_item->priv;
945
946   if (image == priv->image)
947     return;
948
949   if (priv->image)
950     gtk_container_remove (GTK_CONTAINER (image_menu_item),
951                           priv->image);
952
953   priv->image = image;
954
955   if (image == NULL)
956     return;
957
958   gtk_widget_set_parent (image, GTK_WIDGET (image_menu_item));
959   g_object_set (image,
960                 "visible", show_image (image_menu_item),
961                 "no-show-all", TRUE,
962                 NULL);
963
964   g_object_notify (G_OBJECT (image_menu_item), "image");
965 }
966
967 /**
968  * gtk_image_menu_item_get_image:
969  * @image_menu_item: a #GtkImageMenuItem.
970  * @returns: the widget set as image of @image_menu_item.
971  *
972  * Gets the widget that is currently set as the image of @image_menu_item.
973  * See gtk_image_menu_item_set_image().
974  **/
975 GtkWidget*
976 gtk_image_menu_item_get_image (GtkImageMenuItem *image_menu_item)
977 {
978   g_return_val_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item), NULL);
979
980   return image_menu_item->priv->image;
981 }
982
983 static void
984 gtk_image_menu_item_remove (GtkContainer *container,
985                             GtkWidget    *child)
986 {
987   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (container);
988   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
989
990   if (child == priv->image)
991     {
992       gboolean widget_was_visible;
993       
994       widget_was_visible = gtk_widget_get_visible (child);
995       
996       gtk_widget_unparent (child);
997       priv->image = NULL;
998
999       if (widget_was_visible &&
1000           gtk_widget_get_visible (GTK_WIDGET (container)))
1001         gtk_widget_queue_resize (GTK_WIDGET (container));
1002
1003       g_object_notify (G_OBJECT (image_menu_item), "image");
1004     }
1005   else
1006     {
1007       GTK_CONTAINER_CLASS (gtk_image_menu_item_parent_class)->remove (container, child);
1008     }
1009 }
1010
1011 static void 
1012 show_image_change_notify (GtkImageMenuItem *image_menu_item)
1013 {
1014   GtkImageMenuItemPrivate *priv = image_menu_item->priv;
1015
1016   if (priv->image)
1017     {
1018       if (show_image (image_menu_item))
1019         gtk_widget_show (priv->image);
1020       else
1021         gtk_widget_hide (priv->image);
1022     }
1023 }
1024
1025 static void
1026 traverse_container (GtkWidget *widget,
1027                     gpointer   data)
1028 {
1029   if (GTK_IS_IMAGE_MENU_ITEM (widget))
1030     show_image_change_notify (GTK_IMAGE_MENU_ITEM (widget));
1031   else if (GTK_IS_CONTAINER (widget))
1032     gtk_container_forall (GTK_CONTAINER (widget), traverse_container, NULL);
1033 }
1034
1035 static void
1036 gtk_image_menu_item_setting_changed (GtkSettings *settings)
1037 {
1038   GList *list, *l;
1039
1040   list = gtk_window_list_toplevels ();
1041
1042   for (l = list; l; l = l->next)
1043     gtk_container_forall (GTK_CONTAINER (l->data), 
1044                           traverse_container, NULL);
1045
1046   g_list_free (list);  
1047 }
1048
1049 static void
1050 gtk_image_menu_item_screen_changed (GtkWidget *widget,
1051                                     GdkScreen *previous_screen)
1052 {
1053   GtkSettings *settings;
1054   guint show_image_connection;
1055
1056   if (!gtk_widget_has_screen (widget))
1057     return;
1058
1059   settings = gtk_widget_get_settings (widget);
1060   
1061   show_image_connection = 
1062     GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (settings), 
1063                                          "gtk-image-menu-item-connection"));
1064   
1065   if (show_image_connection)
1066     return;
1067
1068   show_image_connection =
1069     g_signal_connect (settings, "notify::gtk-menu-images",
1070                       G_CALLBACK (gtk_image_menu_item_setting_changed), NULL);
1071   g_object_set_data (G_OBJECT (settings), 
1072                      I_("gtk-image-menu-item-connection"),
1073                      GUINT_TO_POINTER (show_image_connection));
1074
1075   show_image_change_notify (GTK_IMAGE_MENU_ITEM (widget));
1076 }