]> Pileus Git - ~andy/gtk/blob - gtk/gtkimagemenuitem.c
use gtk_widget_get_child_requisition() instead of accessing
[~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
35 typedef struct _GtkImageMenuItemPrivate GtkImageMenuItemPrivate;
36
37 struct _GtkImageMenuItemPrivate 
38 {
39   GtkSettings *settings;
40   guint        show_image_connection;
41 };
42
43 #define GTK_IMAGE_MENU_ITEM_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_IMAGE_MENU_ITEM, GtkImageMenuItemPrivate))
44
45 static void gtk_image_menu_item_class_init           (GtkImageMenuItemClass *klass);
46 static void gtk_image_menu_item_init                 (GtkImageMenuItem      *image_menu_item);
47 static void gtk_image_menu_item_size_request         (GtkWidget        *widget,
48                                                       GtkRequisition   *requisition);
49 static void gtk_image_menu_item_size_allocate        (GtkWidget        *widget,
50                                                       GtkAllocation    *allocation);
51 static void gtk_image_menu_item_remove               (GtkContainer          *container,
52                                                       GtkWidget             *child);
53 static void gtk_image_menu_item_toggle_size_request  (GtkMenuItem           *menu_item,
54                                                       gint                  *requisition);
55
56 static void gtk_image_menu_item_forall     (GtkContainer   *container,
57                                             gboolean        include_internals,
58                                             GtkCallback     callback,
59                                             gpointer        callback_data);
60
61 static void gtk_image_menu_item_set_property (GObject         *object,
62                                               guint            prop_id,
63                                               const GValue    *value,
64                                               GParamSpec      *pspec);
65 static void gtk_image_menu_item_get_property (GObject         *object,
66                                               guint            prop_id,
67                                               GValue          *value,
68                                               GParamSpec      *pspec);
69 static void gtk_image_menu_item_screen_changed (GtkWidget        *widget,
70                                                 GdkScreen        *previous_screen);
71
72
73 enum {
74   PROP_ZERO,
75   PROP_IMAGE
76 };
77
78 static GtkMenuItemClass *parent_class = NULL;
79
80 GType
81 gtk_image_menu_item_get_type (void)
82 {
83   static GType image_menu_item_type = 0;
84
85   if (!image_menu_item_type)
86     {
87       static const GTypeInfo image_menu_item_info =
88       {
89         sizeof (GtkImageMenuItemClass),
90         NULL,           /* base_init */
91         NULL,           /* base_finalize */
92         (GClassInitFunc) gtk_image_menu_item_class_init,
93         NULL,           /* class_finalize */
94         NULL,           /* class_data */
95         sizeof (GtkImageMenuItem),
96         0,              /* n_preallocs */
97         (GInstanceInitFunc) gtk_image_menu_item_init,
98       };
99
100       image_menu_item_type =
101         g_type_register_static (GTK_TYPE_MENU_ITEM, "GtkImageMenuItem",
102                                 &image_menu_item_info, 0);
103     }
104
105   return image_menu_item_type;
106 }
107
108 static void
109 gtk_image_menu_item_class_init (GtkImageMenuItemClass *klass)
110 {
111   GObjectClass *gobject_class;
112   GtkWidgetClass *widget_class;
113   GtkMenuItemClass *menu_item_class;
114   GtkContainerClass *container_class;
115
116   gobject_class = (GObjectClass*) klass;
117   widget_class = (GtkWidgetClass*) klass;
118   menu_item_class = (GtkMenuItemClass*) klass;
119   container_class = (GtkContainerClass*) klass;
120   
121   parent_class = g_type_class_peek_parent (klass);
122   
123   widget_class->screen_changed = gtk_image_menu_item_screen_changed;
124   widget_class->size_request = gtk_image_menu_item_size_request;
125   widget_class->size_allocate = gtk_image_menu_item_size_allocate;
126
127   container_class->forall = gtk_image_menu_item_forall;
128   container_class->remove = gtk_image_menu_item_remove;
129   
130   menu_item_class->toggle_size_request = gtk_image_menu_item_toggle_size_request;
131
132   gobject_class->set_property = gtk_image_menu_item_set_property;
133   gobject_class->get_property = gtk_image_menu_item_get_property;
134   
135   g_object_class_install_property (gobject_class,
136                                    PROP_IMAGE,
137                                    g_param_spec_object ("image",
138                                                         P_("Image widget"),
139                                                         P_("Child widget to appear next to the menu text"),
140                                                         GTK_TYPE_WIDGET,
141                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
142
143   gtk_settings_install_property (g_param_spec_boolean ("gtk-menu-images",
144                                                        P_("Show menu images"),
145                                                        P_("Whether images should be shown in menus"),
146                                                        TRUE,
147                                                        G_PARAM_READWRITE));
148
149   g_type_class_add_private (gobject_class, sizeof (GtkImageMenuItemPrivate));  
150 }
151
152 static void
153 gtk_image_menu_item_init (GtkImageMenuItem *image_menu_item)
154 {
155   image_menu_item->image = NULL;
156 }
157
158 static void
159 gtk_image_menu_item_set_property (GObject         *object,
160                                   guint            prop_id,
161                                   const GValue    *value,
162                                   GParamSpec      *pspec)
163 {
164   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (object);
165   
166   switch (prop_id)
167     {
168     case PROP_IMAGE:
169       {
170         GtkWidget *image;
171
172         image = (GtkWidget*) g_value_get_object (value);
173
174         gtk_image_menu_item_set_image (image_menu_item, image);
175       }
176       break;
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179       break;
180     }
181 }
182 static void
183 gtk_image_menu_item_get_property (GObject         *object,
184                                   guint            prop_id,
185                                   GValue          *value,
186                                   GParamSpec      *pspec)
187 {
188   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (object);
189   
190   switch (prop_id)
191     {
192     case PROP_IMAGE:
193       g_value_set_object (value,
194                           (GObject*) image_menu_item->image);
195       break;
196     default:
197       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198       break;
199     }
200 }
201
202 static gboolean
203 show_image (GtkImageMenuItem *image_menu_item)
204 {
205   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (image_menu_item));
206   gboolean show;
207
208   g_object_get (settings, "gtk-menu-images", &show, NULL);
209
210   return show;
211 }
212
213 static void
214 gtk_image_menu_item_toggle_size_request (GtkMenuItem *menu_item,
215                                          gint        *requisition)
216 {
217   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (menu_item);
218
219   *requisition = 0;
220
221   if (image_menu_item->image && show_image (image_menu_item))
222     {
223       GtkRequisition image_requisition;
224
225       gtk_widget_get_child_requisition (image_menu_item->image,
226                                         &image_requisition);
227
228       if (image_requisition.width > 0)
229         {
230           guint toggle_spacing;
231           gtk_widget_style_get (GTK_WIDGET (menu_item),
232                                 "toggle_spacing", &toggle_spacing,
233                                 NULL);
234
235           *requisition = image_requisition.width + toggle_spacing;
236         }
237     }
238 }
239
240
241 static void
242 gtk_image_menu_item_size_request (GtkWidget      *widget,
243                                   GtkRequisition *requisition)
244 {
245   GtkImageMenuItem *image_menu_item;
246   gint child_height = 0;
247   
248   image_menu_item = GTK_IMAGE_MENU_ITEM (widget);
249   
250   if (image_menu_item->image && 
251       GTK_WIDGET_VISIBLE (image_menu_item->image) && 
252       show_image (image_menu_item))
253     {
254       GtkRequisition child_requisition;
255       
256       gtk_widget_size_request (image_menu_item->image,
257                                &child_requisition);
258
259       child_height = child_requisition.height;
260     }
261   
262   (* GTK_WIDGET_CLASS (parent_class)->size_request) (widget, requisition);
263
264   /* not done with height since that happens via the
265    * toggle_size_request
266    */
267   requisition->height = MAX (requisition->height, child_height);
268   
269   /* Note that GtkMenuShell always size requests before
270    * toggle_size_request, so toggle_size_request will be able to use
271    * image_menu_item->image->requisition
272    */
273 }
274
275 static void
276 gtk_image_menu_item_size_allocate (GtkWidget     *widget,
277                                    GtkAllocation *allocation)
278 {
279   GtkImageMenuItem *image_menu_item;
280   
281   image_menu_item = GTK_IMAGE_MENU_ITEM (widget);  
282   
283   (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
284
285   if (image_menu_item->image && show_image (image_menu_item))
286     {
287       gint x, y, offset;
288       GtkRequisition child_requisition;
289       GtkAllocation child_allocation;
290       guint horizontal_padding, toggle_spacing;
291
292       gtk_widget_style_get (widget,
293                             "horizontal_padding", &horizontal_padding,
294                             "toggle_spacing", &toggle_spacing,
295                             NULL);
296       
297       /* Man this is lame hardcoding action, but I can't
298        * come up with a solution that's really better.
299        */
300
301       gtk_widget_get_child_requisition (image_menu_item->image,
302                                         &child_requisition);
303
304       offset = GTK_CONTAINER (image_menu_item)->border_width +
305         widget->style->xthickness;
306
307       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
308         {
309           x = offset + horizontal_padding +
310               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
311                toggle_spacing - child_requisition.width) / 2;
312         }
313       else
314         {
315           x = widget->allocation.width - offset - horizontal_padding -
316               GTK_MENU_ITEM (image_menu_item)->toggle_size + toggle_spacing +
317               (GTK_MENU_ITEM (image_menu_item)->toggle_size -
318                toggle_spacing - child_requisition.width) / 2;
319         }
320
321       y = (widget->allocation.height - child_requisition.height) / 2;
322
323       child_allocation.width = child_requisition.width;
324       child_allocation.height = child_requisition.height;
325       child_allocation.x = widget->allocation.x + MAX (x, 0);
326       child_allocation.y = widget->allocation.y + MAX (y, 0);
327
328       gtk_widget_size_allocate (image_menu_item->image, &child_allocation);
329     }
330 }
331
332 static void
333 gtk_image_menu_item_forall (GtkContainer   *container,
334                             gboolean        include_internals,
335                             GtkCallback     callback,
336                             gpointer        callback_data)
337 {
338   GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (container);
339   
340   (* GTK_CONTAINER_CLASS (parent_class)->forall) (container,
341                                                   include_internals,
342                                                   callback,
343                                                   callback_data);
344
345   if (image_menu_item->image)
346     (* callback) (image_menu_item->image, callback_data);
347 }
348
349 /**
350  * gtk_image_menu_item_new:
351  * @returns: a new #GtkImageMenuItem.
352  *
353  * Creates a new #GtkImageMenuItem with an empty label.
354  **/
355 GtkWidget*
356 gtk_image_menu_item_new (void)
357 {
358   return g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
359 }
360
361 /**
362  * gtk_image_menu_item_new_with_label:
363  * @label: the text of the menu item.
364  * @returns: a new #GtkImageMenuItem.
365  *
366  * Creates a new #GtkImageMenuItem containing a label. 
367  **/
368 GtkWidget*
369 gtk_image_menu_item_new_with_label (const gchar *label)
370 {
371   GtkImageMenuItem *image_menu_item;
372   GtkWidget *accel_label;
373   
374   image_menu_item = g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
375
376   accel_label = gtk_accel_label_new (label);
377   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
378
379   gtk_container_add (GTK_CONTAINER (image_menu_item), accel_label);
380   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label),
381                                     GTK_WIDGET (image_menu_item));
382   gtk_widget_show (accel_label);
383
384   return GTK_WIDGET(image_menu_item);
385 }
386
387
388 /**
389  * gtk_image_menu_item_new_with_mnemonic:
390  * @label: the text of the menu item, with an underscore in front of the
391  *         mnemonic character
392  * @returns: a new #GtkImageMenuItem
393  *
394  * Creates a new #GtkImageMenuItem containing a label. The label
395  * will be created using gtk_label_new_with_mnemonic(), so underscores
396  * in @label indicate the mnemonic for the menu item.
397  **/
398 GtkWidget*
399 gtk_image_menu_item_new_with_mnemonic (const gchar *label)
400 {
401   GtkImageMenuItem *image_menu_item;
402   GtkWidget *accel_label;
403   
404   image_menu_item = g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
405
406   accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
407   gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), label);
408   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
409
410   gtk_container_add (GTK_CONTAINER (image_menu_item), accel_label);
411   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label),
412                                     GTK_WIDGET (image_menu_item));
413   gtk_widget_show (accel_label);
414
415   return GTK_WIDGET(image_menu_item);
416 }
417
418 /**
419  * gtk_image_menu_item_new_from_stock:
420  * @stock_id: the name of the stock item.
421  * @accel_group: the #GtkAccelGroup to add the menu items accelerator to,
422  *   or %NULL.
423  * @returns: a new #GtkImageMenuItem.
424  *
425  * Creates a new #GtkImageMenuItem containing the image and text from a 
426  * stock item. Some stock ids have preprocessor macros like #GTK_STOCK_OK 
427  * and #GTK_STOCK_APPLY.
428  *
429  * If you want this menu item to have changeable accelerators, then pass in
430  * %NULL for accel_group. Next call gtk_menu_item_set_accel_path() with an
431  * appropriate path for the menu item, use gtk_stock_lookup() to look up the
432  * standard accelerator for the stock item, and if one is found, call
433  * gtk_accel_map_add_entry() to register it.
434  **/
435 GtkWidget*
436 gtk_image_menu_item_new_from_stock (const gchar      *stock_id,
437                                     GtkAccelGroup    *accel_group)
438 {
439   GtkWidget *image;
440   GtkStockItem stock_item;
441   GtkWidget *item;
442
443   g_return_val_if_fail (stock_id != NULL, NULL);
444
445   image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
446
447   if (gtk_stock_lookup (stock_id, &stock_item))
448     {
449       item = gtk_image_menu_item_new_with_mnemonic (stock_item.label);
450
451       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
452       
453       if (stock_item.keyval && accel_group)
454         gtk_widget_add_accelerator (item,
455                                     "activate",
456                                     accel_group,
457                                     stock_item.keyval,
458                                     stock_item.modifier,
459                                     GTK_ACCEL_VISIBLE);
460     }
461   else
462     {
463       item = gtk_image_menu_item_new_with_mnemonic (stock_id);
464
465       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
466     }
467
468   gtk_widget_show (image);
469   return item;
470 }
471
472 /** 
473  * gtk_image_menu_item_set_image:
474  * @image_menu_item: a #GtkImageMenuItem.
475  * @image: a widget to set as the image for the menu item.
476  * 
477  * Sets the image of @image_menu_item to the given widget.
478  **/ 
479 void
480 gtk_image_menu_item_set_image (GtkImageMenuItem *image_menu_item,
481                                GtkWidget        *image)
482 {
483   g_return_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item));
484
485   if (image == image_menu_item->image)
486     return;
487
488   if (image_menu_item->image)
489     gtk_container_remove (GTK_CONTAINER (image_menu_item),
490                           image_menu_item->image);
491
492   image_menu_item->image = image;
493
494   if (image == NULL)
495     return;
496
497   gtk_widget_set_parent (image, GTK_WIDGET (image_menu_item));
498   g_object_set (image, 
499                 "visible", show_image (image_menu_item),
500                 "no_show_all", TRUE,
501                 NULL);
502
503   g_object_notify (G_OBJECT (image_menu_item), "image");
504 }
505
506 /**
507  * gtk_image_menu_item_get_image:
508  * @image_menu_item: a #GtkImageMenuItem.
509  * @returns: the widget set as image of @image_menu_item.
510  *
511  * Gets the widget that is currently set as the image of @image_menu_item.
512  * See gtk_image_menu_item_set_image().
513  **/
514 GtkWidget*
515 gtk_image_menu_item_get_image (GtkImageMenuItem *image_menu_item)
516 {
517   g_return_val_if_fail (GTK_IS_IMAGE_MENU_ITEM (image_menu_item), NULL);
518
519   return image_menu_item->image;
520 }
521
522 static void
523 gtk_image_menu_item_remove (GtkContainer *container,
524                             GtkWidget    *child)
525 {
526   GtkImageMenuItem *image_menu_item;
527
528   image_menu_item = GTK_IMAGE_MENU_ITEM (container);
529
530   if (child == image_menu_item->image)
531     {
532       gboolean widget_was_visible;
533       
534       widget_was_visible = GTK_WIDGET_VISIBLE (child);
535       
536       gtk_widget_unparent (child);
537       image_menu_item->image = NULL;
538       
539       if (GTK_WIDGET_VISIBLE (container) && widget_was_visible)
540         gtk_widget_queue_resize (GTK_WIDGET (container));
541
542       g_object_notify (G_OBJECT (image_menu_item), "image");
543     }
544   else
545     {
546       (* GTK_CONTAINER_CLASS (parent_class)->remove) (container, child);
547     }
548 }
549
550 static void 
551 show_image_change_notify (GtkImageMenuItem *image_menu_item)
552 {
553   if (image_menu_item->image) 
554     g_object_set (image_menu_item->image, "visible", show_image (image_menu_item), NULL);
555 }
556
557 static void
558 gtk_image_menu_item_screen_changed (GtkWidget *widget,
559                                     GdkScreen *previous_screen)
560 {
561   GtkImageMenuItemPrivate *priv = GTK_IMAGE_MENU_ITEM_GET_PRIVATE (widget);
562   GtkSettings *settings;
563
564   if (gtk_widget_has_screen (widget))
565     settings = gtk_widget_get_settings (widget);
566   else
567     settings = NULL;
568
569   if (settings == priv->settings)
570     return;
571
572   if (priv->settings)
573     {
574       g_signal_handler_disconnect (priv->settings, priv->show_image_connection);
575       g_object_unref (priv->settings);
576     }
577
578   if (settings)
579     {
580       priv->show_image_connection =
581         g_signal_connect_swapped (settings,
582                                   "notify::gtk-menu-images",
583                                   G_CALLBACK (show_image_change_notify),
584                                   widget);
585
586       g_object_ref (settings);
587       priv->settings = settings;
588     }
589   else
590     priv->settings = NULL;
591
592   show_image_change_notify (GTK_IMAGE_MENU_ITEM (widget));
593 }