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