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