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