]> Pileus Git - ~andy/gtk/blob - gtk/gtktoolbutton.c
Minor documentation improvements
[~andy/gtk] / gtk / gtktoolbutton.c
1 /* gtktoolbutton.c
2  *
3  * Copyright (C) 2002 Anders Carlsson <andersca@gnome.org>
4  * Copyright (C) 2002 James Henstridge <james@daa.com.au>
5  * Copyright (C) 2003 Soeren Sandmann <sandmann@daimi.au.dk>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "config.h"
24 #include "gtktoolbutton.h"
25 #include "gtkbutton.h"
26 #include "gtkhbox.h"
27 #include "gtkiconfactory.h"
28 #include "gtkimage.h"
29 #include "gtkimagemenuitem.h"
30 #include "gtklabel.h"
31 #include "gtkstock.h"
32 #include "gtkvbox.h"
33 #include "gtkintl.h"
34 #include "gtktoolbar.h"
35 #include "gtkactivatable.h"
36 #include "gtkprivate.h"
37
38 #include <string.h>
39
40 #define MENU_ID "gtk-tool-button-menu-id"
41
42 enum {
43   CLICKED,
44   LAST_SIGNAL
45 };
46
47 enum {
48   PROP_0,
49   PROP_LABEL,
50   PROP_USE_UNDERLINE,
51   PROP_LABEL_WIDGET,
52   PROP_STOCK_ID,
53   PROP_ICON_NAME,
54   PROP_ICON_WIDGET
55 };
56
57 static void gtk_tool_button_init          (GtkToolButton      *button,
58                                            GtkToolButtonClass *klass);
59 static void gtk_tool_button_class_init    (GtkToolButtonClass *klass);
60 static void gtk_tool_button_set_property  (GObject            *object,
61                                            guint               prop_id,
62                                            const GValue       *value,
63                                            GParamSpec         *pspec);
64 static void gtk_tool_button_get_property  (GObject            *object,
65                                            guint               prop_id,
66                                            GValue             *value,
67                                            GParamSpec         *pspec);
68 static void gtk_tool_button_property_notify (GObject          *object,
69                                              GParamSpec       *pspec);
70 static void gtk_tool_button_finalize      (GObject            *object);
71
72 static void gtk_tool_button_toolbar_reconfigured (GtkToolItem *tool_item);
73 static gboolean   gtk_tool_button_create_menu_proxy (GtkToolItem     *item);
74 static void       button_clicked                    (GtkWidget       *widget,
75                                                      GtkToolButton   *button);
76 static void gtk_tool_button_style_updated  (GtkWidget          *widget);
77
78 static void gtk_tool_button_construct_contents (GtkToolItem *tool_item);
79
80 static void gtk_tool_button_activatable_interface_init (GtkActivatableIface  *iface);
81 static void gtk_tool_button_update                     (GtkActivatable       *activatable,
82                                                         GtkAction            *action,
83                                                         const gchar          *property_name);
84 static void gtk_tool_button_sync_action_properties     (GtkActivatable       *activatable,
85                                                         GtkAction            *action);
86
87
88 struct _GtkToolButtonPrivate
89 {
90   GtkWidget *button;
91
92   gchar *stock_id;
93   gchar *icon_name;
94   gchar *label_text;
95   GtkWidget *label_widget;
96   GtkWidget *icon_widget;
97
98   GtkSizeGroup *text_size_group;
99
100   guint use_underline : 1;
101   guint contents_invalid : 1;
102 };
103
104 static GObjectClass        *parent_class = NULL;
105 static GtkActivatableIface *parent_activatable_iface;
106 static guint                toolbutton_signals[LAST_SIGNAL] = { 0 };
107
108
109 GType
110 gtk_tool_button_get_type (void)
111 {
112   static GType type = 0;
113   
114   if (!type)
115     {
116       const GInterfaceInfo activatable_info =
117       {
118         (GInterfaceInitFunc) gtk_tool_button_activatable_interface_init,
119         (GInterfaceFinalizeFunc) NULL,
120         NULL
121       };
122
123       type = g_type_register_static_simple (GTK_TYPE_TOOL_ITEM,
124                                             I_("GtkToolButton"),
125                                             sizeof (GtkToolButtonClass),
126                                             (GClassInitFunc) gtk_tool_button_class_init,
127                                             sizeof (GtkToolButton),
128                                             (GInstanceInitFunc) gtk_tool_button_init,
129                                             0);
130
131       g_type_add_interface_static (type, GTK_TYPE_ACTIVATABLE,
132                                    &activatable_info);
133     }
134   return type;
135 }
136
137 static void
138 gtk_tool_button_class_init (GtkToolButtonClass *klass)
139 {
140   GObjectClass *object_class;
141   GtkWidgetClass *widget_class;
142   GtkToolItemClass *tool_item_class;
143   
144   parent_class = g_type_class_peek_parent (klass);
145   
146   object_class = (GObjectClass *)klass;
147   widget_class = (GtkWidgetClass *)klass;
148   tool_item_class = (GtkToolItemClass *)klass;
149   
150   object_class->set_property = gtk_tool_button_set_property;
151   object_class->get_property = gtk_tool_button_get_property;
152   object_class->notify = gtk_tool_button_property_notify;
153   object_class->finalize = gtk_tool_button_finalize;
154
155   widget_class->style_updated = gtk_tool_button_style_updated;
156
157   tool_item_class->create_menu_proxy = gtk_tool_button_create_menu_proxy;
158   tool_item_class->toolbar_reconfigured = gtk_tool_button_toolbar_reconfigured;
159   
160   klass->button_type = GTK_TYPE_BUTTON;
161
162   /* Properties are interpreted like this:
163    *
164    *          - if the tool button has an icon_widget, then that widget
165    *            will be used as the icon. Otherwise, if the tool button
166    *            has a stock id, the corresponding stock icon will be
167    *            used. Otherwise, if the tool button has an icon name,
168    *            the corresponding icon from the theme will be used.
169    *            Otherwise, the tool button will not have an icon.
170    *
171    *          - if the tool button has a label_widget then that widget
172    *            will be used as the label. Otherwise, if the tool button
173    *            has a label text, that text will be used as label. Otherwise,
174    *            if the toolbutton has a stock id, the corresponding text
175    *            will be used as label. Otherwise, if the tool button has
176    *            an icon name, the corresponding icon name from the theme will
177    *            be used. Otherwise, the toolbutton will have an empty label.
178    *
179    *          - The use_underline property only has an effect when the label
180    *            on the toolbutton comes from the label property (ie. not from
181    *            label_widget or from stock_id).
182    *
183    *            In that case, if use_underline is set,
184    *
185    *                    - underscores are removed from the label text before
186    *                      the label is shown on the toolbutton unless the
187    *                      underscore is followed by another underscore
188    *
189    *                    - an underscore indicates that the next character when
190    *                      used in the overflow menu should be used as a
191    *                      mnemonic.
192    *
193    *            In short: use_underline = TRUE means that the label text has
194    *            the form "_Open" and the toolbar should take appropriate
195    *            action.
196    */
197
198   g_object_class_install_property (object_class,
199                                    PROP_LABEL,
200                                    g_param_spec_string ("label",
201                                                         P_("Label"),
202                                                         P_("Text to show in the item."),
203                                                         NULL,
204                                                         GTK_PARAM_READWRITE));
205   g_object_class_install_property (object_class,
206                                    PROP_USE_UNDERLINE,
207                                    g_param_spec_boolean ("use-underline",
208                                                          P_("Use underline"),
209                                                          P_("If set, an underline in the label property indicates that the next character should be used for the mnemonic accelerator key in the overflow menu"),
210                                                          FALSE,
211                                                          GTK_PARAM_READWRITE));
212   g_object_class_install_property (object_class,
213                                    PROP_LABEL_WIDGET,
214                                    g_param_spec_object ("label-widget",
215                                                         P_("Label widget"),
216                                                         P_("Widget to use as the item label"),
217                                                         GTK_TYPE_WIDGET,
218                                                         GTK_PARAM_READWRITE));
219   g_object_class_install_property (object_class,
220                                    PROP_STOCK_ID,
221                                    g_param_spec_string ("stock-id",
222                                                         P_("Stock Id"),
223                                                         P_("The stock icon displayed on the item"),
224                                                         NULL,
225                                                         GTK_PARAM_READWRITE));
226
227   /**
228    * GtkToolButton:icon-name:
229    * 
230    * The name of the themed icon displayed on the item.
231    * This property only has an effect if not overridden by "label", 
232    * "icon_widget" or "stock_id" properties.
233    *
234    * Since: 2.8 
235    */
236   g_object_class_install_property (object_class,
237                                    PROP_ICON_NAME,
238                                    g_param_spec_string ("icon-name",
239                                                         P_("Icon name"),
240                                                         P_("The name of the themed icon displayed on the item"),
241                                                         NULL,
242                                                         GTK_PARAM_READWRITE));
243   g_object_class_install_property (object_class,
244                                    PROP_ICON_WIDGET,
245                                    g_param_spec_object ("icon-widget",
246                                                         P_("Icon widget"),
247                                                         P_("Icon widget to display in the item"),
248                                                         GTK_TYPE_WIDGET,
249                                                         GTK_PARAM_READWRITE));
250
251   /**
252    * GtkButton:icon-spacing:
253    * 
254    * Spacing in pixels between the icon and label.
255    * 
256    * Since: 2.10
257    */
258   gtk_widget_class_install_style_property (widget_class,
259                                            g_param_spec_int ("icon-spacing",
260                                                              P_("Icon spacing"),
261                                                              P_("Spacing in pixels between the icon and label"),
262                                                              0,
263                                                              G_MAXINT,
264                                                              3,
265                                                              GTK_PARAM_READWRITE));
266
267 /**
268  * GtkToolButton::clicked:
269  * @toolbutton: the object that emitted the signal
270  *
271  * This signal is emitted when the tool button is clicked with the mouse
272  * or activated with the keyboard.
273  **/
274   toolbutton_signals[CLICKED] =
275     g_signal_new (I_("clicked"),
276                   G_OBJECT_CLASS_TYPE (klass),
277                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
278                   G_STRUCT_OFFSET (GtkToolButtonClass, clicked),
279                   NULL, NULL,
280                   g_cclosure_marshal_VOID__VOID,
281                   G_TYPE_NONE, 0);
282   
283   g_type_class_add_private (object_class, sizeof (GtkToolButtonPrivate));
284 }
285
286 static void
287 gtk_tool_button_init (GtkToolButton      *button,
288                       GtkToolButtonClass *klass)
289 {
290   GtkToolItem *toolitem = GTK_TOOL_ITEM (button);
291
292   button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
293                                               GTK_TYPE_TOOL_BUTTON,
294                                               GtkToolButtonPrivate);
295
296   button->priv->contents_invalid = TRUE;
297
298   gtk_tool_item_set_homogeneous (toolitem, TRUE);
299
300   /* create button */
301   button->priv->button = g_object_new (klass->button_type, NULL);
302   gtk_button_set_focus_on_click (GTK_BUTTON (button->priv->button), FALSE);
303   g_signal_connect_object (button->priv->button, "clicked",
304                            G_CALLBACK (button_clicked), button, 0);
305
306   gtk_container_add (GTK_CONTAINER (button), button->priv->button);
307   gtk_widget_show (button->priv->button);
308 }
309
310 static void
311 gtk_tool_button_construct_contents (GtkToolItem *tool_item)
312 {
313   GtkToolButton *button = GTK_TOOL_BUTTON (tool_item);
314   GtkWidget *child;
315   GtkWidget *label = NULL;
316   GtkWidget *icon = NULL;
317   GtkToolbarStyle style;
318   gboolean need_label = FALSE;
319   gboolean need_icon = FALSE;
320   GtkIconSize icon_size;
321   GtkWidget *box = NULL;
322   guint icon_spacing;
323   GtkOrientation text_orientation = GTK_ORIENTATION_HORIZONTAL;
324   GtkSizeGroup *size_group = NULL;
325   GtkWidget *parent;
326
327   button->priv->contents_invalid = FALSE;
328
329   gtk_widget_style_get (GTK_WIDGET (tool_item), 
330                         "icon-spacing", &icon_spacing,
331                         NULL);
332
333   if (button->priv->icon_widget)
334     {
335       parent = gtk_widget_get_parent (button->priv->icon_widget);
336       if (parent)
337         {
338           gtk_container_remove (GTK_CONTAINER (parent),
339                                 button->priv->icon_widget);
340         }
341     }
342
343   if (button->priv->label_widget)
344     {
345       parent = gtk_widget_get_parent (button->priv->label_widget);
346       if (parent)
347         {
348           gtk_container_remove (GTK_CONTAINER (parent),
349                                 button->priv->label_widget);
350         }
351     }
352
353   child = gtk_bin_get_child (GTK_BIN (button->priv->button));
354   if (child)
355     {
356       /* Note: we are not destroying the label_widget or icon_widget
357        * here because they were removed from their containers above
358        */
359       gtk_widget_destroy (child);
360     }
361
362   style = gtk_tool_item_get_toolbar_style (GTK_TOOL_ITEM (button));
363   
364   if (style != GTK_TOOLBAR_TEXT)
365     need_icon = TRUE;
366
367   if (style != GTK_TOOLBAR_ICONS && style != GTK_TOOLBAR_BOTH_HORIZ)
368     need_label = TRUE;
369
370   if (style == GTK_TOOLBAR_BOTH_HORIZ &&
371       (gtk_tool_item_get_is_important (GTK_TOOL_ITEM (button)) ||
372        gtk_tool_item_get_orientation (GTK_TOOL_ITEM (button)) == GTK_ORIENTATION_VERTICAL ||
373        gtk_tool_item_get_text_orientation (GTK_TOOL_ITEM (button)) == GTK_ORIENTATION_VERTICAL))
374     {
375       need_label = TRUE;
376     }
377   
378   if (style == GTK_TOOLBAR_ICONS && button->priv->icon_widget == NULL &&
379       button->priv->stock_id == NULL && button->priv->icon_name == NULL)
380     {
381       need_label = TRUE;
382       need_icon = FALSE;
383       style = GTK_TOOLBAR_TEXT;
384     }
385
386   if (style == GTK_TOOLBAR_TEXT && button->priv->label_widget == NULL &&
387       button->priv->stock_id == NULL && button->priv->label_text == NULL)
388     {
389       need_label = FALSE;
390       need_icon = TRUE;
391       style = GTK_TOOLBAR_ICONS;
392     }
393
394   if (need_label)
395     {
396       if (button->priv->label_widget)
397         {
398           label = button->priv->label_widget;
399         }
400       else
401         {
402           GtkStockItem stock_item;
403           gboolean elide;
404           gchar *label_text;
405
406           if (button->priv->label_text)
407             {
408               label_text = button->priv->label_text;
409               elide = button->priv->use_underline;
410             }
411           else if (button->priv->stock_id && gtk_stock_lookup (button->priv->stock_id, &stock_item))
412             {
413               label_text = stock_item.label;
414               elide = TRUE;
415             }
416           else
417             {
418               label_text = "";
419               elide = FALSE;
420             }
421
422           if (elide)
423             label_text = _gtk_toolbar_elide_underscores (label_text);
424           else
425             label_text = g_strdup (label_text);
426
427           label = gtk_label_new (label_text);
428
429           g_free (label_text);
430           
431           gtk_widget_show (label);
432         }
433
434       if (GTK_IS_LABEL (label))
435         {
436           gtk_label_set_ellipsize (GTK_LABEL (label),
437                                    gtk_tool_item_get_ellipsize_mode (GTK_TOOL_ITEM (button)));
438           text_orientation = gtk_tool_item_get_text_orientation (GTK_TOOL_ITEM (button));
439           if (text_orientation == GTK_ORIENTATION_HORIZONTAL)
440             {
441               gtk_label_set_angle (GTK_LABEL (label), 0);
442               gtk_misc_set_alignment (GTK_MISC (label),
443                                       gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)),
444                                       0.5);
445             }
446           else
447             {
448               gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_NONE);
449               if (gtk_widget_get_direction (GTK_WIDGET (tool_item)) == GTK_TEXT_DIR_RTL)
450                 gtk_label_set_angle (GTK_LABEL (label), -90);
451               else
452                 gtk_label_set_angle (GTK_LABEL (label), 90);
453               gtk_misc_set_alignment (GTK_MISC (label),
454                                       0.5,
455                                       1 - gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)));
456             }
457         }
458     }
459
460   icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (button));
461   if (need_icon)
462     {
463       if (button->priv->icon_widget)
464         {
465           icon = button->priv->icon_widget;
466           
467           if (GTK_IS_IMAGE (icon))
468             {
469               g_object_set (button->priv->icon_widget,
470                             "icon-size", icon_size,
471                             NULL);
472             }
473         }
474       else if (button->priv->stock_id && 
475                gtk_icon_factory_lookup_default (button->priv->stock_id))
476         {
477           icon = gtk_image_new_from_stock (button->priv->stock_id, icon_size);
478           gtk_widget_show (icon);
479         }
480       else if (button->priv->icon_name)
481         {
482           icon = gtk_image_new_from_icon_name (button->priv->icon_name, icon_size);
483           gtk_widget_show (icon);
484         }
485
486       if (GTK_IS_MISC (icon) && text_orientation == GTK_ORIENTATION_HORIZONTAL)
487         gtk_misc_set_alignment (GTK_MISC (icon),
488                                 1.0 - gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)),
489                                 0.5);
490       else if (GTK_IS_MISC (icon))
491         gtk_misc_set_alignment (GTK_MISC (icon),
492                                 0.5,
493                                 gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)));
494
495       if (icon)
496         {
497           size_group = gtk_tool_item_get_text_size_group (GTK_TOOL_ITEM (button));
498           if (size_group != NULL)
499             gtk_size_group_add_widget (size_group, icon);
500         }
501     }
502
503   switch (style)
504     {
505     case GTK_TOOLBAR_ICONS:
506       if (icon)
507         gtk_container_add (GTK_CONTAINER (button->priv->button), icon);
508       break;
509
510     case GTK_TOOLBAR_BOTH:
511       if (text_orientation == GTK_ORIENTATION_HORIZONTAL)
512         box = gtk_box_new (GTK_ORIENTATION_VERTICAL, icon_spacing);
513       else
514         box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, icon_spacing);
515       if (icon)
516         gtk_box_pack_start (GTK_BOX (box), icon, TRUE, TRUE, 0);
517       gtk_box_pack_end (GTK_BOX (box), label, FALSE, TRUE, 0);
518       gtk_container_add (GTK_CONTAINER (button->priv->button), box);
519       break;
520
521     case GTK_TOOLBAR_BOTH_HORIZ:
522       if (text_orientation == GTK_ORIENTATION_HORIZONTAL)
523         {
524           box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, icon_spacing);
525           if (icon)
526             gtk_box_pack_start (GTK_BOX (box), icon, label? FALSE : TRUE, TRUE, 0);
527           if (label)
528             gtk_box_pack_end (GTK_BOX (box), label, TRUE, TRUE, 0);
529         }
530       else
531         {
532           box = gtk_box_new (GTK_ORIENTATION_VERTICAL, icon_spacing);
533           if (icon)
534             gtk_box_pack_end (GTK_BOX (box), icon, label ? FALSE : TRUE, TRUE, 0);
535           if (label)
536             gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
537         }
538       gtk_container_add (GTK_CONTAINER (button->priv->button), box);
539       break;
540
541     case GTK_TOOLBAR_TEXT:
542       gtk_container_add (GTK_CONTAINER (button->priv->button), label);
543       break;
544     }
545
546   if (box)
547     gtk_widget_show (box);
548
549   gtk_button_set_relief (GTK_BUTTON (button->priv->button),
550                          gtk_tool_item_get_relief_style (GTK_TOOL_ITEM (button)));
551
552   gtk_tool_item_rebuild_menu (tool_item);
553   
554   gtk_widget_queue_resize (GTK_WIDGET (button));
555 }
556
557 static void
558 gtk_tool_button_set_property (GObject         *object,
559                               guint            prop_id,
560                               const GValue    *value,
561                               GParamSpec      *pspec)
562 {
563   GtkToolButton *button = GTK_TOOL_BUTTON (object);
564   
565   switch (prop_id)
566     {
567     case PROP_LABEL:
568       gtk_tool_button_set_label (button, g_value_get_string (value));
569       break;
570     case PROP_USE_UNDERLINE:
571       gtk_tool_button_set_use_underline (button, g_value_get_boolean (value));
572       break;
573     case PROP_LABEL_WIDGET:
574       gtk_tool_button_set_label_widget (button, g_value_get_object (value));
575       break;
576     case PROP_STOCK_ID:
577       gtk_tool_button_set_stock_id (button, g_value_get_string (value));
578       break;
579     case PROP_ICON_NAME:
580       gtk_tool_button_set_icon_name (button, g_value_get_string (value));
581       break;
582     case PROP_ICON_WIDGET:
583       gtk_tool_button_set_icon_widget (button, g_value_get_object (value));
584       break;
585     default:
586       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
587       break;
588     }
589 }
590
591 static void
592 gtk_tool_button_property_notify (GObject          *object,
593                                  GParamSpec       *pspec)
594 {
595   GtkToolButton *button = GTK_TOOL_BUTTON (object);
596
597   if (button->priv->contents_invalid ||
598       strcmp ("is-important", pspec->name) == 0)
599     gtk_tool_button_construct_contents (GTK_TOOL_ITEM (object));
600
601   if (parent_class->notify)
602     parent_class->notify (object, pspec);
603 }
604
605 static void
606 gtk_tool_button_get_property (GObject         *object,
607                               guint            prop_id,
608                               GValue          *value,
609                               GParamSpec      *pspec)
610 {
611   GtkToolButton *button = GTK_TOOL_BUTTON (object);
612
613   switch (prop_id)
614     {
615     case PROP_LABEL:
616       g_value_set_string (value, gtk_tool_button_get_label (button));
617       break;
618     case PROP_LABEL_WIDGET:
619       g_value_set_object (value, gtk_tool_button_get_label_widget (button));
620       break;
621     case PROP_USE_UNDERLINE:
622       g_value_set_boolean (value, gtk_tool_button_get_use_underline (button));
623       break;
624     case PROP_STOCK_ID:
625       g_value_set_string (value, button->priv->stock_id);
626       break;
627     case PROP_ICON_NAME:
628       g_value_set_string (value, button->priv->icon_name);
629       break;
630     case PROP_ICON_WIDGET:
631       g_value_set_object (value, button->priv->icon_widget);
632       break;
633     default:
634       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
635       break;
636     }
637 }
638
639 static void
640 gtk_tool_button_finalize (GObject *object)
641 {
642   GtkToolButton *button = GTK_TOOL_BUTTON (object);
643
644   g_free (button->priv->stock_id);
645   g_free (button->priv->icon_name);
646   g_free (button->priv->label_text);
647
648   if (button->priv->label_widget)
649     g_object_unref (button->priv->label_widget);
650
651   if (button->priv->icon_widget)
652     g_object_unref (button->priv->icon_widget);
653   
654   parent_class->finalize (object);
655 }
656
657 static GtkWidget *
658 clone_image_menu_size (GtkImage *image, GtkSettings *settings)
659 {
660   GtkImageType storage_type = gtk_image_get_storage_type (image);
661
662   if (storage_type == GTK_IMAGE_STOCK)
663     {
664       gchar *stock_id;
665       gtk_image_get_stock (image, &stock_id, NULL);
666       return gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
667     }
668   else if (storage_type == GTK_IMAGE_ICON_NAME)
669     {
670       const gchar *icon_name;
671       gtk_image_get_icon_name (image, &icon_name, NULL);
672       return gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
673     }
674   else if (storage_type == GTK_IMAGE_ICON_SET)
675     {
676       GtkIconSet *icon_set;
677       gtk_image_get_icon_set (image, &icon_set, NULL);
678       return gtk_image_new_from_icon_set (icon_set, GTK_ICON_SIZE_MENU);
679     }
680   else if (storage_type == GTK_IMAGE_GICON)
681     {
682       GIcon *icon;
683       gtk_image_get_gicon (image, &icon, NULL);
684       return gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
685     }
686   else if (storage_type == GTK_IMAGE_PIXBUF)
687     {
688       gint width, height;
689       
690       if (settings &&
691           gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
692                                              &width, &height))
693         {
694           GdkPixbuf *src_pixbuf, *dest_pixbuf;
695           GtkWidget *cloned_image;
696
697           src_pixbuf = gtk_image_get_pixbuf (image);
698           dest_pixbuf = gdk_pixbuf_scale_simple (src_pixbuf, width, height,
699                                                  GDK_INTERP_BILINEAR);
700
701           cloned_image = gtk_image_new_from_pixbuf (dest_pixbuf);
702           g_object_unref (dest_pixbuf);
703
704           return cloned_image;
705         }
706     }
707
708   return NULL;
709 }
710       
711 static gboolean
712 gtk_tool_button_create_menu_proxy (GtkToolItem *item)
713 {
714   GtkToolButton *button = GTK_TOOL_BUTTON (item);
715   GtkWidget *menu_item;
716   GtkWidget *menu_image = NULL;
717   GtkStockItem stock_item;
718   gboolean use_mnemonic = TRUE;
719   const char *label;
720
721   if (_gtk_tool_item_create_menu_proxy (item))
722     return TRUE;
723  
724   if (GTK_IS_LABEL (button->priv->label_widget))
725     {
726       label = gtk_label_get_label (GTK_LABEL (button->priv->label_widget));
727       use_mnemonic = gtk_label_get_use_underline (GTK_LABEL (button->priv->label_widget));
728     }
729   else if (button->priv->label_text)
730     {
731       label = button->priv->label_text;
732       use_mnemonic = button->priv->use_underline;
733     }
734   else if (button->priv->stock_id && gtk_stock_lookup (button->priv->stock_id, &stock_item))
735     {
736       label = stock_item.label;
737     }
738   else
739     {
740       label = "";
741     }
742   
743   if (use_mnemonic)
744     menu_item = gtk_image_menu_item_new_with_mnemonic (label);
745   else
746     menu_item = gtk_image_menu_item_new_with_label (label);
747
748   if (GTK_IS_IMAGE (button->priv->icon_widget))
749     {
750       menu_image = clone_image_menu_size (GTK_IMAGE (button->priv->icon_widget),
751                                           gtk_widget_get_settings (GTK_WIDGET (button)));
752     }
753   else if (button->priv->stock_id)
754     {
755       menu_image = gtk_image_new_from_stock (button->priv->stock_id, GTK_ICON_SIZE_MENU);
756     }
757
758   if (menu_image)
759     gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image);
760
761   g_signal_connect_closure_by_id (menu_item,
762                                   g_signal_lookup ("activate", G_OBJECT_TYPE (menu_item)), 0,
763                                   g_cclosure_new_object_swap (G_CALLBACK (gtk_button_clicked),
764                                                               G_OBJECT (GTK_TOOL_BUTTON (button)->priv->button)),
765                                   FALSE);
766
767   gtk_tool_item_set_proxy_menu_item (GTK_TOOL_ITEM (button), MENU_ID, menu_item);
768   
769   return TRUE;
770 }
771
772 static void
773 button_clicked (GtkWidget     *widget,
774                 GtkToolButton *button)
775 {
776   GtkAction *action;
777
778   action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (button));
779   
780   if (action)
781     gtk_action_activate (action);
782
783   g_signal_emit_by_name (button, "clicked");
784 }
785
786 static void
787 gtk_tool_button_toolbar_reconfigured (GtkToolItem *tool_item)
788 {
789   gtk_tool_button_construct_contents (tool_item);
790 }
791
792 static void 
793 gtk_tool_button_update_icon_spacing (GtkToolButton *button)
794 {
795   GtkWidget *box;
796   guint spacing;
797
798   box = gtk_bin_get_child (GTK_BIN (button->priv->button));
799   if (GTK_IS_BOX (box))
800     {
801       gtk_widget_style_get (GTK_WIDGET (button), 
802                             "icon-spacing", &spacing,
803                             NULL);
804       gtk_box_set_spacing (GTK_BOX (box), spacing);      
805     }
806 }
807
808 static void
809 gtk_tool_button_style_updated (GtkWidget *widget)
810 {
811   GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
812
813   gtk_tool_button_update_icon_spacing (GTK_TOOL_BUTTON (widget));
814 }
815
816 static void 
817 gtk_tool_button_activatable_interface_init (GtkActivatableIface  *iface)
818 {
819   parent_activatable_iface = g_type_interface_peek_parent (iface);
820   iface->update = gtk_tool_button_update;
821   iface->sync_action_properties = gtk_tool_button_sync_action_properties;
822 }
823
824 static void
825 gtk_tool_button_update (GtkActivatable *activatable,
826                         GtkAction      *action,
827                         const gchar    *property_name)
828 {
829   GtkToolButton *button;
830   GtkWidget *image;
831
832   parent_activatable_iface->update (activatable, action, property_name);
833
834   if (!gtk_activatable_get_use_action_appearance (activatable))
835     return;
836
837   button = GTK_TOOL_BUTTON (activatable);
838   
839   if (strcmp (property_name, "short-label") == 0)
840     gtk_tool_button_set_label (button, gtk_action_get_short_label (action));
841   else if (strcmp (property_name, "stock-id") == 0)
842     gtk_tool_button_set_stock_id (button, gtk_action_get_stock_id (action));
843   else if (strcmp (property_name, "gicon") == 0)
844     {
845       const gchar *stock_id = gtk_action_get_stock_id (action);
846       GIcon *icon = gtk_action_get_gicon (action);
847       GtkIconSize icon_size = GTK_ICON_SIZE_BUTTON;
848
849       if ((stock_id && gtk_icon_factory_lookup_default (stock_id)) || !icon)
850         image = NULL;
851       else 
852         {   
853           image = gtk_tool_button_get_icon_widget (button);
854           icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (button));
855
856           if (!image)
857             image = gtk_image_new ();
858         }
859
860       gtk_tool_button_set_icon_widget (button, image);
861       gtk_image_set_from_gicon (GTK_IMAGE (image), icon, icon_size);
862
863     }
864   else if (strcmp (property_name, "icon-name") == 0)
865     gtk_tool_button_set_icon_name (button, gtk_action_get_icon_name (action));
866 }
867
868 static void
869 gtk_tool_button_sync_action_properties (GtkActivatable *activatable,
870                                         GtkAction      *action)
871 {
872   GtkToolButton *button;
873   GIcon         *icon;
874   const gchar   *stock_id;
875
876   parent_activatable_iface->sync_action_properties (activatable, action);
877
878   if (!action)
879     return;
880
881   if (!gtk_activatable_get_use_action_appearance (activatable))
882     return;
883
884   button = GTK_TOOL_BUTTON (activatable);
885   stock_id = gtk_action_get_stock_id (action);
886
887   gtk_tool_button_set_label (button, gtk_action_get_short_label (action));
888   gtk_tool_button_set_use_underline (button, TRUE);
889   gtk_tool_button_set_stock_id (button, stock_id);
890   gtk_tool_button_set_icon_name (button, gtk_action_get_icon_name (action));
891
892   if (stock_id && gtk_icon_factory_lookup_default (stock_id))
893       gtk_tool_button_set_icon_widget (button, NULL);
894   else if ((icon = gtk_action_get_gicon (action)) != NULL)
895     {
896       GtkIconSize icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (button));
897       GtkWidget  *image = gtk_tool_button_get_icon_widget (button);
898       
899       if (!image)
900         {
901           image = gtk_image_new ();
902           gtk_widget_show (image);
903           gtk_tool_button_set_icon_widget (button, image);
904         }
905
906       gtk_image_set_from_gicon (GTK_IMAGE (image), icon, icon_size);
907     }
908   else if (gtk_action_get_icon_name (action))
909     gtk_tool_button_set_icon_name (button, gtk_action_get_icon_name (action));
910   else
911     gtk_tool_button_set_label (button, gtk_action_get_short_label (action));
912 }
913
914 /**
915  * gtk_tool_button_new_from_stock:
916  * @stock_id: the name of the stock item 
917  *
918  * Creates a new #GtkToolButton containing the image and text from a
919  * stock item. Some stock ids have preprocessor macros like #GTK_STOCK_OK
920  * and #GTK_STOCK_APPLY.
921  *
922  * It is an error if @stock_id is not a name of a stock item.
923  * 
924  * Return value: A new #GtkToolButton
925  * 
926  * Since: 2.4
927  **/
928 GtkToolItem *
929 gtk_tool_button_new_from_stock (const gchar *stock_id)
930 {
931   GtkToolButton *button;
932
933   g_return_val_if_fail (stock_id != NULL, NULL);
934     
935   button = g_object_new (GTK_TYPE_TOOL_BUTTON,
936                          "stock-id", stock_id,
937                          NULL);
938
939   return GTK_TOOL_ITEM (button);
940 }
941
942 /**
943  * gtk_tool_button_new:
944  * @label: (allow-none): a string that will be used as label, or %NULL
945  * @icon_widget: (allow-none): a #GtkMisc widget that will be used as icon widget, or %NULL
946  *
947  * Creates a new %GtkToolButton using @icon_widget as icon and @label as
948  * label.
949  *
950  * Return value: A new #GtkToolButton
951  * 
952  * Since: 2.4
953  **/
954 GtkToolItem *
955 gtk_tool_button_new (GtkWidget   *icon_widget,
956                      const gchar *label)
957 {
958   GtkToolButton *button;
959
960   g_return_val_if_fail (icon_widget == NULL || GTK_IS_MISC (icon_widget), NULL);
961
962   button = g_object_new (GTK_TYPE_TOOL_BUTTON,
963                          "label", label,
964                          "icon-widget", icon_widget,
965                          NULL);
966
967   return GTK_TOOL_ITEM (button);  
968 }
969
970 /**
971  * gtk_tool_button_set_label:
972  * @button: a #GtkToolButton
973  * @label: (allow-none): a string that will be used as label, or %NULL.
974  *
975  * Sets @label as the label used for the tool button. The "label" property
976  * only has an effect if not overridden by a non-%NULL "label_widget" property.
977  * If both the "label_widget" and "label" properties are %NULL, the label
978  * is determined by the "stock_id" property. If the "stock_id" property is also
979  * %NULL, @button will not have a label.
980  * 
981  * Since: 2.4
982  **/
983 void
984 gtk_tool_button_set_label (GtkToolButton *button,
985                            const gchar   *label)
986 {
987   gchar *old_label;
988   gchar *elided_label;
989   AtkObject *accessible;
990   
991   g_return_if_fail (GTK_IS_TOOL_BUTTON (button));
992
993   old_label = button->priv->label_text;
994
995   button->priv->label_text = g_strdup (label);
996   button->priv->contents_invalid = TRUE;     
997
998   if (label)
999     {
1000       elided_label = _gtk_toolbar_elide_underscores (label);
1001       accessible = gtk_widget_get_accessible (GTK_WIDGET (button->priv->button));
1002       atk_object_set_name (accessible, elided_label);
1003       g_free (elided_label);
1004     }
1005
1006   g_free (old_label);
1007  
1008   g_object_notify (G_OBJECT (button), "label");
1009 }
1010
1011 /**
1012  * gtk_tool_button_get_label:
1013  * @button: a #GtkToolButton
1014  * 
1015  * Returns the label used by the tool button, or %NULL if the tool button
1016  * doesn't have a label. or uses a the label from a stock item. The returned
1017  * string is owned by GTK+, and must not be modified or freed.
1018  * 
1019  * Return value: The label, or %NULL
1020  * 
1021  * Since: 2.4
1022  **/
1023 G_CONST_RETURN gchar *
1024 gtk_tool_button_get_label (GtkToolButton *button)
1025 {
1026   g_return_val_if_fail (GTK_IS_TOOL_BUTTON (button), NULL);
1027
1028   return button->priv->label_text;
1029 }
1030
1031 /**
1032  * gtk_tool_button_set_use_underline:
1033  * @button: a #GtkToolButton
1034  * @use_underline: whether the button label has the form "_Open"
1035  *
1036  * If set, an underline in the label property indicates that the next character
1037  * should be used for the mnemonic accelerator key in the overflow menu. For
1038  * example, if the label property is "_Open" and @use_underline is %TRUE,
1039  * the label on the tool button will be "Open" and the item on the overflow
1040  * menu will have an underlined 'O'.
1041  * 
1042  * Labels shown on tool buttons never have mnemonics on them; this property
1043  * only affects the menu item on the overflow menu.
1044  * 
1045  * Since: 2.4
1046  **/
1047 void
1048 gtk_tool_button_set_use_underline (GtkToolButton *button,
1049                                    gboolean       use_underline)
1050 {
1051   g_return_if_fail (GTK_IS_TOOL_BUTTON (button));
1052
1053   use_underline = use_underline != FALSE;
1054
1055   if (use_underline != button->priv->use_underline)
1056     {
1057       button->priv->use_underline = use_underline;
1058       button->priv->contents_invalid = TRUE;
1059
1060       g_object_notify (G_OBJECT (button), "use-underline");
1061     }
1062 }
1063
1064 /**
1065  * gtk_tool_button_get_use_underline:
1066  * @button: a #GtkToolButton
1067  * 
1068  * Returns whether underscores in the label property are used as mnemonics
1069  * on menu items on the overflow menu. See gtk_tool_button_set_use_underline().
1070  * 
1071  * Return value: %TRUE if underscores in the label property are used as
1072  * mnemonics on menu items on the overflow menu.
1073  * 
1074  * Since: 2.4
1075  **/
1076 gboolean
1077 gtk_tool_button_get_use_underline (GtkToolButton *button)
1078 {
1079   g_return_val_if_fail (GTK_IS_TOOL_BUTTON (button), FALSE);
1080
1081   return button->priv->use_underline;
1082 }
1083
1084 /**
1085  * gtk_tool_button_set_stock_id:
1086  * @button: a #GtkToolButton
1087  * @stock_id: (allow-none): a name of a stock item, or %NULL
1088  *
1089  * Sets the name of the stock item. See gtk_tool_button_new_from_stock().
1090  * The stock_id property only has an effect if not
1091  * overridden by non-%NULL "label" and "icon_widget" properties.
1092  * 
1093  * Since: 2.4
1094  **/
1095 void
1096 gtk_tool_button_set_stock_id (GtkToolButton *button,
1097                               const gchar   *stock_id)
1098 {
1099   gchar *old_stock_id;
1100   
1101   g_return_if_fail (GTK_IS_TOOL_BUTTON (button));
1102
1103   old_stock_id = button->priv->stock_id;
1104
1105   button->priv->stock_id = g_strdup (stock_id);
1106   button->priv->contents_invalid = TRUE;
1107
1108   g_free (old_stock_id);
1109   
1110   g_object_notify (G_OBJECT (button), "stock-id");
1111 }
1112
1113 /**
1114  * gtk_tool_button_get_stock_id:
1115  * @button: a #GtkToolButton
1116  * 
1117  * Returns the name of the stock item. See gtk_tool_button_set_stock_id().
1118  * The returned string is owned by GTK+ and must not be freed or modifed.
1119  * 
1120  * Return value: the name of the stock item for @button.
1121  * 
1122  * Since: 2.4
1123  **/
1124 G_CONST_RETURN gchar *
1125 gtk_tool_button_get_stock_id (GtkToolButton *button)
1126 {
1127   g_return_val_if_fail (GTK_IS_TOOL_BUTTON (button), NULL);
1128
1129   return button->priv->stock_id;
1130 }
1131
1132 /**
1133  * gtk_tool_button_set_icon_name
1134  * @button: a #GtkToolButton
1135  * @icon_name: (allow-none): the name of the themed icon
1136  *
1137  * Sets the icon for the tool button from a named themed icon.
1138  * See the docs for #GtkIconTheme for more details.
1139  * The "icon_name" property only has an effect if not
1140  * overridden by non-%NULL "label", "icon_widget" and "stock_id"
1141  * properties.
1142  * 
1143  * Since: 2.8
1144  **/
1145 void
1146 gtk_tool_button_set_icon_name (GtkToolButton *button,
1147                                const gchar   *icon_name)
1148 {
1149   gchar *old_icon_name;
1150
1151   g_return_if_fail (GTK_IS_TOOL_BUTTON (button));
1152
1153   old_icon_name = button->priv->icon_name;
1154
1155   button->priv->icon_name = g_strdup (icon_name);
1156   button->priv->contents_invalid = TRUE; 
1157
1158   g_free (old_icon_name);
1159
1160   g_object_notify (G_OBJECT (button), "icon-name");
1161 }
1162
1163 /**
1164  * gtk_tool_button_get_icon_name
1165  * @button: a #GtkToolButton
1166  * 
1167  * Returns the name of the themed icon for the tool button,
1168  * see gtk_tool_button_set_icon_name().
1169  *
1170  * Returns: the icon name or %NULL if the tool button has
1171  * no themed icon
1172  * 
1173  * Since: 2.8
1174  **/
1175 G_CONST_RETURN gchar*
1176 gtk_tool_button_get_icon_name (GtkToolButton *button)
1177 {
1178   g_return_val_if_fail (GTK_IS_TOOL_BUTTON (button), NULL);
1179
1180   return button->priv->icon_name;
1181 }
1182
1183 /**
1184  * gtk_tool_button_set_icon_widget:
1185  * @button: a #GtkToolButton
1186  * @icon_widget: (allow-none): the widget used as icon, or %NULL
1187  *
1188  * Sets @icon as the widget used as icon on @button. If @icon_widget is
1189  * %NULL the icon is determined by the "stock_id" property. If the
1190  * "stock_id" property is also %NULL, @button will not have an icon.
1191  * 
1192  * Since: 2.4
1193  **/
1194 void
1195 gtk_tool_button_set_icon_widget (GtkToolButton *button,
1196                                  GtkWidget     *icon_widget)
1197 {
1198   g_return_if_fail (GTK_IS_TOOL_BUTTON (button));
1199   g_return_if_fail (icon_widget == NULL || GTK_IS_WIDGET (icon_widget));
1200
1201   if (icon_widget != button->priv->icon_widget)
1202     {
1203       if (button->priv->icon_widget)
1204         {
1205           GtkWidget *parent;
1206
1207           parent = gtk_widget_get_parent (button->priv->icon_widget);
1208           if (parent)
1209             gtk_container_remove (GTK_CONTAINER (parent),
1210                                   button->priv->icon_widget);
1211
1212           g_object_unref (button->priv->icon_widget);
1213         }
1214       
1215       if (icon_widget)
1216         g_object_ref_sink (icon_widget);
1217
1218       button->priv->icon_widget = icon_widget;
1219       button->priv->contents_invalid = TRUE;
1220       
1221       g_object_notify (G_OBJECT (button), "icon-widget");
1222     }
1223 }
1224
1225 /**
1226  * gtk_tool_button_set_label_widget:
1227  * @button: a #GtkToolButton
1228  * @label_widget: (allow-none): the widget used as label, or %NULL
1229  *
1230  * Sets @label_widget as the widget that will be used as the label
1231  * for @button. If @label_widget is %NULL the "label" property is used
1232  * as label. If "label" is also %NULL, the label in the stock item
1233  * determined by the "stock_id" property is used as label. If
1234  * "stock_id" is also %NULL, @button does not have a label.
1235  * 
1236  * Since: 2.4
1237  **/
1238 void
1239 gtk_tool_button_set_label_widget (GtkToolButton *button,
1240                                   GtkWidget     *label_widget)
1241 {
1242   g_return_if_fail (GTK_IS_TOOL_BUTTON (button));
1243   g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
1244
1245   if (label_widget != button->priv->label_widget)
1246     {
1247       if (button->priv->label_widget)
1248         {
1249           GtkWidget *parent;
1250
1251           parent = gtk_widget_get_parent (button->priv->label_widget);
1252           if (parent)
1253             gtk_container_remove (GTK_CONTAINER (parent),
1254                                   button->priv->label_widget);
1255
1256           g_object_unref (button->priv->label_widget);
1257         }
1258       
1259       if (label_widget)
1260         g_object_ref_sink (label_widget);
1261
1262       button->priv->label_widget = label_widget;
1263       button->priv->contents_invalid = TRUE;
1264       
1265       g_object_notify (G_OBJECT (button), "label-widget");
1266     }
1267 }
1268
1269 /**
1270  * gtk_tool_button_get_label_widget:
1271  * @button: a #GtkToolButton
1272  *
1273  * Returns the widget used as label on @button.
1274  * See gtk_tool_button_set_label_widget().
1275  *
1276  * Return value: (transfer none): The widget used as label
1277  *     on @button, or %NULL.
1278  *
1279  * Since: 2.4
1280  **/
1281 GtkWidget *
1282 gtk_tool_button_get_label_widget (GtkToolButton *button)
1283 {
1284   g_return_val_if_fail (GTK_IS_TOOL_BUTTON (button), NULL);
1285
1286   return button->priv->label_widget;
1287 }
1288
1289 /**
1290  * gtk_tool_button_get_icon_widget:
1291  * @button: a #GtkToolButton
1292  *
1293  * Return the widget used as icon widget on @button.
1294  * See gtk_tool_button_set_icon_widget().
1295  *
1296  * Return value: (transfer none): The widget used as icon
1297  *     on @button, or %NULL.
1298  *
1299  * Since: 2.4
1300  **/
1301 GtkWidget *
1302 gtk_tool_button_get_icon_widget (GtkToolButton *button)
1303 {
1304   g_return_val_if_fail (GTK_IS_TOOL_BUTTON (button), NULL);
1305
1306   return button->priv->icon_widget;
1307 }
1308
1309 GtkWidget *
1310 _gtk_tool_button_get_button (GtkToolButton *button)
1311 {
1312   g_return_val_if_fail (GTK_IS_TOOL_BUTTON (button), NULL);
1313
1314   return button->priv->button;
1315 }