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