]> Pileus Git - ~andy/gtk/blob - gtk/gtkimagemenuitem.c
Merge branch 'windows_list'
[~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 _GtkImageMenuItemPriv
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 (GtkImageMenuItemPriv));
198 }
199
200 static void
201 gtk_image_menu_item_init (GtkImageMenuItem *image_menu_item)
202 {
203   GtkImageMenuItemPriv *priv;
204
205   image_menu_item->priv = G_TYPE_INSTANCE_GET_PRIVATE (image_menu_item,
206                                                        GTK_TYPE_IMAGE_MENU_ITEM,
207                                                        GtkImageMenuItemPriv);
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   GtkImageMenuItemPriv *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   GtkImageMenuItemPriv *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   GtkImageMenuItemPriv *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   GtkImageMenuItemPriv *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   GtkImageMenuItemPriv *priv = image_menu_item->priv;
327   GtkPackDirection pack_dir;
328
329   if (GTK_IS_MENU_BAR (GTK_WIDGET (menu_item)->parent))
330     pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (GTK_WIDGET (menu_item)->parent));
331   else
332     pack_dir = GTK_PACK_DIRECTION_LTR;
333
334   *requisition = 0;
335
336   if (priv->image && gtk_widget_get_visible (priv->image))
337     {
338       GtkRequisition image_requisition;
339       guint toggle_spacing;
340       gtk_widget_get_child_requisition (priv->image,
341                                         &image_requisition);
342
343       gtk_widget_style_get (GTK_WIDGET (menu_item),
344                             "toggle-spacing", &toggle_spacing,
345                             NULL);
346       
347       if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL)
348         {
349           if (image_requisition.width > 0)
350             *requisition = image_requisition.width + toggle_spacing;
351         }
352       else
353         {
354           if (image_requisition.height > 0)
355             *requisition = image_requisition.height + toggle_spacing;
356         }
357     }
358 }
359
360 static void
361 gtk_image_menu_item_recalculate (GtkImageMenuItem *image_menu_item)
362 {
363   GtkImageMenuItemPriv    *priv = image_menu_item->priv;
364   GtkStockItem             stock_item;
365   GtkWidget               *image;
366   const gchar             *resolved_label = priv->label;
367
368   if (priv->use_stock && priv->label)
369     {
370
371       if (!priv->image)
372         {
373           image = gtk_image_new_from_stock (priv->label, GTK_ICON_SIZE_MENU);
374           gtk_image_menu_item_set_image (image_menu_item, image);
375         }
376
377       if (gtk_stock_lookup (priv->label, &stock_item))
378           resolved_label = stock_item.label;
379
380         gtk_menu_item_set_use_underline (GTK_MENU_ITEM (image_menu_item), TRUE);
381     }
382
383   GTK_MENU_ITEM_CLASS
384     (gtk_image_menu_item_parent_class)->set_label (GTK_MENU_ITEM (image_menu_item), resolved_label);
385
386 }
387
388 static void 
389 gtk_image_menu_item_set_label (GtkMenuItem      *menu_item,
390                                const gchar      *label)
391 {
392   GtkImageMenuItemPriv *priv = GTK_IMAGE_MENU_ITEM (menu_item)->priv;
393
394   if (priv->label != label)
395     {
396       g_free (priv->label);
397       priv->label = g_strdup (label);
398
399       gtk_image_menu_item_recalculate (GTK_IMAGE_MENU_ITEM (menu_item));
400
401       g_object_notify (G_OBJECT (menu_item), "label");
402
403     }
404 }
405
406 static G_CONST_RETURN gchar *
407 gtk_image_menu_item_get_label (GtkMenuItem *menu_item)
408 {
409   GtkImageMenuItemPriv *priv = GTK_IMAGE_MENU_ITEM (menu_item)->priv;
410
411   return priv->label;
412 }
413
414 static void
415 gtk_image_menu_item_size_request (GtkWidget      *widget,
416                                   GtkRequisition *requisition)
417 {
418   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (widget);
419   GtkImageMenuItemPriv *priv = image_menu_item->priv;
420   gint child_width = 0;
421   gint child_height = 0;
422   GtkPackDirection pack_dir;
423   
424   if (GTK_IS_MENU_BAR (widget->parent))
425     pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
426   else
427     pack_dir = GTK_PACK_DIRECTION_LTR;
428
429   if (priv->image && gtk_widget_get_visible (priv->image))
430     {
431       GtkRequisition child_requisition;
432
433       gtk_widget_size_request (priv->image,
434                                &child_requisition);
435
436       child_width = child_requisition.width;
437       child_height = child_requisition.height;
438     }
439
440   GTK_WIDGET_CLASS (gtk_image_menu_item_parent_class)->size_request (widget, requisition);
441
442   /* not done with height since that happens via the
443    * toggle_size_request
444    */
445   if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL)
446     requisition->height = MAX (requisition->height, child_height);
447   else
448     requisition->width = MAX (requisition->width, child_width);
449     
450   
451   /* Note that GtkMenuShell always size requests before
452    * toggle_size_request, so toggle_size_request will be able to use
453    * priv->image->requisition
454    */
455 }
456
457 static void
458 gtk_image_menu_item_size_allocate (GtkWidget     *widget,
459                                    GtkAllocation *allocation)
460 {
461   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (widget);
462   GtkImageMenuItemPriv *priv = image_menu_item->priv;
463   GtkPackDirection pack_dir;
464   
465   if (GTK_IS_MENU_BAR (widget->parent))
466     pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
467   else
468     pack_dir = GTK_PACK_DIRECTION_LTR;
469
470   GTK_WIDGET_CLASS (gtk_image_menu_item_parent_class)->size_allocate (widget, allocation);
471
472   if (priv->image && gtk_widget_get_visible (priv->image))
473     {
474       gint x, y, offset;
475       GtkRequisition child_requisition;
476       GtkAllocation child_allocation;
477       guint horizontal_padding, toggle_spacing;
478
479       gtk_widget_style_get (widget,
480                             "horizontal-padding", &horizontal_padding,
481                             "toggle-spacing", &toggle_spacing,
482                             NULL);
483       
484       /* Man this is lame hardcoding action, but I can't
485        * come up with a solution that's really better.
486        */
487
488       gtk_widget_get_child_requisition (priv->image,
489                                         &child_requisition);
490
491       if (pack_dir == GTK_PACK_DIRECTION_LTR ||
492           pack_dir == GTK_PACK_DIRECTION_RTL)
493         {
494           offset = gtk_container_get_border_width (GTK_CONTAINER (image_menu_item)) +
495             widget->style->xthickness;
496           
497           if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) ==
498               (pack_dir == GTK_PACK_DIRECTION_LTR))
499             x = offset + horizontal_padding +
500               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
501                toggle_spacing - child_requisition.width) / 2;
502           else
503             x = widget->allocation.width - offset - horizontal_padding -
504               GTK_MENU_ITEM (image_menu_item)->toggle_size + toggle_spacing +
505               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
506                toggle_spacing - child_requisition.width) / 2;
507           
508           y = (widget->allocation.height - child_requisition.height) / 2;
509         }
510       else
511         {
512           offset = gtk_container_get_border_width (GTK_CONTAINER (image_menu_item)) +
513             widget->style->ythickness;
514           
515           if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) ==
516               (pack_dir == GTK_PACK_DIRECTION_TTB))
517             y = offset + horizontal_padding +
518               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
519                toggle_spacing - child_requisition.height) / 2;
520           else
521             y = widget->allocation.height - offset - horizontal_padding -
522               GTK_MENU_ITEM (image_menu_item)->toggle_size + toggle_spacing +
523               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
524                toggle_spacing - child_requisition.height) / 2;
525
526           x = (widget->allocation.width - child_requisition.width) / 2;
527         }
528       
529       child_allocation.width = child_requisition.width;
530       child_allocation.height = child_requisition.height;
531       child_allocation.x = widget->allocation.x + MAX (x, 0);
532       child_allocation.y = widget->allocation.y + MAX (y, 0);
533
534       gtk_widget_size_allocate (priv->image, &child_allocation);
535     }
536 }
537
538 static void
539 gtk_image_menu_item_forall (GtkContainer   *container,
540                             gboolean        include_internals,
541                             GtkCallback     callback,
542                             gpointer        callback_data)
543 {
544   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (container);
545   GtkImageMenuItemPriv *priv = image_menu_item->priv;
546
547   GTK_CONTAINER_CLASS (gtk_image_menu_item_parent_class)->forall (container,
548                                                                   include_internals,
549                                                                   callback,
550                                                                   callback_data);
551
552   if (include_internals && priv->image)
553     (* callback) (priv->image, callback_data);
554 }
555
556
557 static void 
558 gtk_image_menu_item_activatable_interface_init (GtkActivatableIface  *iface)
559 {
560   parent_activatable_iface = g_type_interface_peek_parent (iface);
561   iface->update = gtk_image_menu_item_update;
562   iface->sync_action_properties = gtk_image_menu_item_sync_action_properties;
563 }
564
565 static gboolean
566 activatable_update_stock_id (GtkImageMenuItem *image_menu_item, GtkAction *action)
567 {
568   GtkWidget   *image;
569   const gchar *stock_id  = gtk_action_get_stock_id (action);
570
571   image = gtk_image_menu_item_get_image (image_menu_item);
572           
573   if (GTK_IS_IMAGE (image) &&
574       stock_id && gtk_icon_factory_lookup_default (stock_id))
575     {
576       gtk_image_set_from_stock (GTK_IMAGE (image), stock_id, GTK_ICON_SIZE_MENU);
577       return TRUE;
578     }
579
580   return FALSE;
581 }
582
583 static gboolean
584 activatable_update_gicon (GtkImageMenuItem *image_menu_item, GtkAction *action)
585 {
586   GtkWidget   *image;
587   GIcon       *icon = gtk_action_get_gicon (action);
588   const gchar *stock_id = gtk_action_get_stock_id (action);
589
590   image = gtk_image_menu_item_get_image (image_menu_item);
591
592   if (icon && GTK_IS_IMAGE (image) &&
593       !(stock_id && gtk_icon_factory_lookup_default (stock_id)))
594     {
595       gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU);
596       return TRUE;
597     }
598
599   return FALSE;
600 }
601
602 static void
603 activatable_update_icon_name (GtkImageMenuItem *image_menu_item, GtkAction *action)
604 {
605   GtkWidget   *image;
606   const gchar *icon_name = gtk_action_get_icon_name (action);
607
608   image = gtk_image_menu_item_get_image (image_menu_item);
609           
610   if (GTK_IS_IMAGE (image) && 
611       (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY ||
612        gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME))
613     {
614       gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_MENU);
615     }
616 }
617
618 static void
619 gtk_image_menu_item_update (GtkActivatable *activatable,
620                             GtkAction      *action,
621                             const gchar    *property_name)
622 {
623   GtkImageMenuItem *image_menu_item;
624   gboolean   use_appearance;
625
626   image_menu_item = GTK_IMAGE_MENU_ITEM (activatable);
627
628   parent_activatable_iface->update (activatable, action, property_name);
629
630   use_appearance = gtk_activatable_get_use_action_appearance (activatable);
631   if (!use_appearance)
632     return;
633
634   if (strcmp (property_name, "stock-id") == 0)
635     activatable_update_stock_id (image_menu_item, action);
636   else if (strcmp (property_name, "gicon") == 0)
637     activatable_update_gicon (image_menu_item, action);
638   else if (strcmp (property_name, "icon-name") == 0)
639     activatable_update_icon_name (image_menu_item, action);
640 }
641
642 static void 
643 gtk_image_menu_item_sync_action_properties (GtkActivatable *activatable,
644                                             GtkAction      *action)
645 {
646   GtkImageMenuItem *image_menu_item;
647   GtkWidget *image;
648   gboolean   use_appearance;
649
650   image_menu_item = GTK_IMAGE_MENU_ITEM (activatable);
651
652   parent_activatable_iface->sync_action_properties (activatable, action);
653
654   if (!action)
655     return;
656
657   use_appearance = gtk_activatable_get_use_action_appearance (activatable);
658   if (!use_appearance)
659     return;
660
661   image = gtk_image_menu_item_get_image (image_menu_item);
662   if (image && !GTK_IS_IMAGE (image))
663     {
664       gtk_image_menu_item_set_image (image_menu_item, NULL);
665       image = NULL;
666     }
667   
668   if (!image)
669     {
670       image = gtk_image_new ();
671       gtk_widget_show (image);
672       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (activatable),
673                                      image);
674     }
675   
676   if (!activatable_update_stock_id (image_menu_item, action) &&
677       !activatable_update_gicon (image_menu_item, action))
678     activatable_update_icon_name (image_menu_item, action);
679
680   gtk_image_menu_item_set_always_show_image (image_menu_item,
681                                              gtk_action_get_always_show_image (action));
682 }
683
684
685 /**
686  * gtk_image_menu_item_new:
687  * @returns: a new #GtkImageMenuItem.
688  *
689  * Creates a new #GtkImageMenuItem with an empty label.
690  **/
691 GtkWidget*
692 gtk_image_menu_item_new (void)
693 {
694   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
695 }
696
697 /**
698  * gtk_image_menu_item_new_with_label:
699  * @label: the text of the menu item.
700  * @returns: a new #GtkImageMenuItem.
701  *
702  * Creates a new #GtkImageMenuItem containing a label. 
703  **/
704 GtkWidget*
705 gtk_image_menu_item_new_with_label (const gchar *label)
706 {
707   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, 
708                        "label", label,
709                        NULL);
710 }
711
712
713 /**
714  * gtk_image_menu_item_new_with_mnemonic:
715  * @label: the text of the menu item, with an underscore in front of the
716  *         mnemonic character
717  * @returns: a new #GtkImageMenuItem
718  *
719  * Creates a new #GtkImageMenuItem containing a label. The label
720  * will be created using gtk_label_new_with_mnemonic(), so underscores
721  * in @label indicate the mnemonic for the menu item.
722  **/
723 GtkWidget*
724 gtk_image_menu_item_new_with_mnemonic (const gchar *label)
725 {
726   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, 
727                        "use-underline", TRUE,
728                        "label", label,
729                        NULL);
730 }
731
732 /**
733  * gtk_image_menu_item_new_from_stock:
734  * @stock_id: the name of the stock item.
735  * @accel_group: (allow-none): the #GtkAccelGroup to add the menu items 
736  *   accelerator to, or %NULL.
737  * @returns: a new #GtkImageMenuItem.
738  *
739  * Creates a new #GtkImageMenuItem containing the image and text from a 
740  * stock item. Some stock ids have preprocessor macros like #GTK_STOCK_OK 
741  * and #GTK_STOCK_APPLY.
742  *
743  * If you want this menu item to have changeable accelerators, then pass in
744  * %NULL for accel_group. Next call gtk_menu_item_set_accel_path() with an
745  * appropriate path for the menu item, use gtk_stock_lookup() to look up the
746  * standard accelerator for the stock item, and if one is found, call
747  * gtk_accel_map_add_entry() to register it.
748  **/
749 GtkWidget*
750 gtk_image_menu_item_new_from_stock (const gchar      *stock_id,
751                                     GtkAccelGroup    *accel_group)
752 {
753   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, 
754                        "label", stock_id,
755                        "use-stock", TRUE,
756                        "accel-group", accel_group,
757                        NULL);
758 }
759
760 /**
761  * gtk_image_menu_item_set_use_stock:
762  * @image_menu_item: a #GtkImageMenuItem
763  * @use_stock: %TRUE if the menuitem should use a stock item
764  *
765  * If %TRUE, the label set in the menuitem is used as a
766  * stock id to select the stock item for the item.
767  *
768  * Since: 2.16
769  */
770 void
771 gtk_image_menu_item_set_use_stock (GtkImageMenuItem *image_menu_item,
772                                    gboolean          use_stock)
773 {
774   GtkImageMenuItemPriv *priv;
775
776   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
777
778   priv = image_menu_item->priv;
779
780   if (priv->use_stock != use_stock)
781     {
782       priv->use_stock = use_stock;
783
784       gtk_image_menu_item_recalculate (image_menu_item);
785
786       g_object_notify (G_OBJECT (image_menu_item), "use-stock");
787     }
788 }
789
790 /**
791  * gtk_image_menu_item_get_use_stock:
792  * @image_menu_item: a #GtkImageMenuItem
793  *
794  * Checks whether the label set in the menuitem is used as a
795  * stock id to select the stock item for the item.
796  *
797  * Returns: %TRUE if the label set in the menuitem is used as a
798  *     stock id to select the stock item for the item
799  *
800  * Since: 2.16
801  */
802 gboolean
803 gtk_image_menu_item_get_use_stock (GtkImageMenuItem *image_menu_item)
804 {
805   g_return_val_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item), FALSE);
806
807   return image_menu_item->priv->use_stock;
808 }
809
810 /**
811  * gtk_image_menu_item_set_always_show_image:
812  * @image_menu_item: a #GtkImageMenuItem
813  * @always_show: %TRUE if the menuitem should always show the image
814  *
815  * If %TRUE, the menu item will ignore the #GtkSettings:gtk-menu-images 
816  * setting and always show the image, if available.
817  *
818  * Use this property if the menuitem would be useless or hard to use
819  * without the image. 
820  * 
821  * Since: 2.16
822  */
823 void
824 gtk_image_menu_item_set_always_show_image (GtkImageMenuItem *image_menu_item,
825                                            gboolean          always_show)
826 {
827   GtkImageMenuItemPriv *priv;
828
829   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
830
831   priv = image_menu_item->priv;
832
833   if (priv->always_show_image != always_show)
834     {
835       priv->always_show_image = always_show;
836
837       if (priv->image)
838         {
839           if (show_image (image_menu_item))
840             gtk_widget_show (priv->image);
841           else
842             gtk_widget_hide (priv->image);
843         }
844
845       g_object_notify (G_OBJECT (image_menu_item), "always-show-image");
846     }
847 }
848
849 /**
850  * gtk_image_menu_item_get_always_show_image:
851  * @image_menu_item: a #GtkImageMenuItem
852  *
853  * Returns whether the menu item will ignore the #GtkSettings:gtk-menu-images
854  * setting and always show the image, if available.
855  * 
856  * Returns: %TRUE if the menu item will always show the image
857  *
858  * Since: 2.16
859  */
860 gboolean
861 gtk_image_menu_item_get_always_show_image (GtkImageMenuItem *image_menu_item)
862 {
863   g_return_val_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item), FALSE);
864
865   return image_menu_item->priv->always_show_image;
866 }
867
868
869 /**
870  * gtk_image_menu_item_set_accel_group:
871  * @image_menu_item: a #GtkImageMenuItem
872  * @accel_group: the #GtkAccelGroup
873  *
874  * Specifies an @accel_group to add the menu items accelerator to
875  * (this only applies to stock items so a stock item must already
876  * be set, make sure to call gtk_image_menu_item_set_use_stock()
877  * and gtk_menu_item_set_label() with a valid stock item first).
878  *
879  * If you want this menu item to have changeable accelerators then
880  * you shouldnt need this (see gtk_image_menu_item_new_from_stock()).
881  *
882  * Since: 2.16
883  */
884 void
885 gtk_image_menu_item_set_accel_group (GtkImageMenuItem *image_menu_item, 
886                                      GtkAccelGroup    *accel_group)
887 {
888   GtkImageMenuItemPriv    *priv;
889   GtkStockItem             stock_item;
890
891   /* Silent return for the constructor */
892   if (!accel_group) 
893     return;
894   
895   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
896   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
897
898   priv = image_menu_item->priv;
899
900   if (priv->use_stock && priv->label && gtk_stock_lookup (priv->label, &stock_item))
901     if (stock_item.keyval)
902       {
903         gtk_widget_add_accelerator (GTK_WIDGET (image_menu_item),
904                                     "activate",
905                                     accel_group,
906                                     stock_item.keyval,
907                                     stock_item.modifier,
908                                     GTK_ACCEL_VISIBLE);
909         
910         g_object_notify (G_OBJECT (image_menu_item), "accel-group");
911       }
912 }
913
914 /** 
915  * gtk_image_menu_item_set_image:
916  * @image_menu_item: a #GtkImageMenuItem.
917  * @image: (allow-none): a widget to set as the image for the menu item.
918  *
919  * Sets the image of @image_menu_item to the given widget.
920  * Note that it depends on the show-menu-images setting whether
921  * the image will be displayed or not.
922  **/ 
923 void
924 gtk_image_menu_item_set_image (GtkImageMenuItem *image_menu_item,
925                                GtkWidget        *image)
926 {
927   GtkImageMenuItemPriv *priv;
928
929   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
930
931   priv = image_menu_item->priv;
932
933   if (image == priv->image)
934     return;
935
936   if (priv->image)
937     gtk_container_remove (GTK_CONTAINER (image_menu_item),
938                           priv->image);
939
940   priv->image = image;
941
942   if (image == NULL)
943     return;
944
945   gtk_widget_set_parent (image, GTK_WIDGET (image_menu_item));
946   g_object_set (image,
947                 "visible", show_image (image_menu_item),
948                 "no-show-all", TRUE,
949                 NULL);
950
951   g_object_notify (G_OBJECT (image_menu_item), "image");
952 }
953
954 /**
955  * gtk_image_menu_item_get_image:
956  * @image_menu_item: a #GtkImageMenuItem.
957  * @returns: the widget set as image of @image_menu_item.
958  *
959  * Gets the widget that is currently set as the image of @image_menu_item.
960  * See gtk_image_menu_item_set_image().
961  **/
962 GtkWidget*
963 gtk_image_menu_item_get_image (GtkImageMenuItem *image_menu_item)
964 {
965   g_return_val_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item), NULL);
966
967   return image_menu_item->priv->image;
968 }
969
970 static void
971 gtk_image_menu_item_remove (GtkContainer *container,
972                             GtkWidget    *child)
973 {
974   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (container);
975   GtkImageMenuItemPriv *priv = image_menu_item->priv;
976
977   if (child == priv->image)
978     {
979       gboolean widget_was_visible;
980       
981       widget_was_visible = gtk_widget_get_visible (child);
982       
983       gtk_widget_unparent (child);
984       priv->image = NULL;
985
986       if (widget_was_visible &&
987           gtk_widget_get_visible (GTK_WIDGET (container)))
988         gtk_widget_queue_resize (GTK_WIDGET (container));
989
990       g_object_notify (G_OBJECT (image_menu_item), "image");
991     }
992   else
993     {
994       GTK_CONTAINER_CLASS (gtk_image_menu_item_parent_class)->remove (container, child);
995     }
996 }
997
998 static void 
999 show_image_change_notify (GtkImageMenuItem *image_menu_item)
1000 {
1001   GtkImageMenuItemPriv *priv = image_menu_item->priv;
1002
1003   if (priv->image)
1004     {
1005       if (show_image (image_menu_item))
1006         gtk_widget_show (priv->image);
1007       else
1008         gtk_widget_hide (priv->image);
1009     }
1010 }
1011
1012 static void
1013 traverse_container (GtkWidget *widget,
1014                     gpointer   data)
1015 {
1016   if (GTK_IS_IMAGE_MENU_ITEM (widget))
1017     show_image_change_notify (GTK_IMAGE_MENU_ITEM (widget));
1018   else if (GTK_IS_CONTAINER (widget))
1019     gtk_container_forall (GTK_CONTAINER (widget), traverse_container, NULL);
1020 }
1021
1022 static void
1023 gtk_image_menu_item_setting_changed (GtkSettings *settings)
1024 {
1025   GList *list, *l;
1026
1027   list = gtk_window_list_toplevels ();
1028
1029   for (l = list; l; l = l->next)
1030     gtk_container_forall (GTK_CONTAINER (l->data), 
1031                           traverse_container, NULL);
1032
1033   g_list_free (list);  
1034 }
1035
1036 static void
1037 gtk_image_menu_item_screen_changed (GtkWidget *widget,
1038                                     GdkScreen *previous_screen)
1039 {
1040   GtkSettings *settings;
1041   guint show_image_connection;
1042
1043   if (!gtk_widget_has_screen (widget))
1044     return;
1045
1046   settings = gtk_widget_get_settings (widget);
1047   
1048   show_image_connection = 
1049     GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (settings), 
1050                                          "gtk-image-menu-item-connection"));
1051   
1052   if (show_image_connection)
1053     return;
1054
1055   show_image_connection =
1056     g_signal_connect (settings, "notify::gtk-menu-images",
1057                       G_CALLBACK (gtk_image_menu_item_setting_changed), NULL);
1058   g_object_set_data (G_OBJECT (settings), 
1059                      I_("gtk-image-menu-item-connection"),
1060                      GUINT_TO_POINTER (show_image_connection));
1061
1062   show_image_change_notify (GTK_IMAGE_MENU_ITEM (widget));
1063 }