]> Pileus Git - ~andy/gtk/blob - gtk/gtktoolbutton.c
e2876d8fb5800af16f306d97880b4c56e93f0b4f
[~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_set      (GtkWidget          *widget,
77                                             GtkStyle           *prev_style);
78
79 static void gtk_tool_button_construct_contents (GtkToolItem *tool_item);
80
81 static void gtk_tool_button_activatable_interface_init (GtkActivatableIface  *iface);
82 static void gtk_tool_button_update                     (GtkActivatable       *activatable,
83                                                         GtkAction            *action,
84                                                         const gchar          *property_name);
85 static void gtk_tool_button_sync_action_properties     (GtkActivatable       *activatable,
86                                                         GtkAction            *action);
87
88
89 struct _GtkToolButtonPrivate
90 {
91   GtkWidget *button;
92
93   gchar *stock_id;
94   gchar *icon_name;
95   gchar *label_text;
96   GtkWidget *label_widget;
97   GtkWidget *icon_widget;
98
99   GtkSizeGroup *text_size_group;
100
101   guint use_underline : 1;
102   guint contents_invalid : 1;
103 };
104
105 static GObjectClass        *parent_class = NULL;
106 static GtkActivatableIface *parent_activatable_iface;
107 static guint                toolbutton_signals[LAST_SIGNAL] = { 0 };
108
109
110 GType
111 gtk_tool_button_get_type (void)
112 {
113   static GType type = 0;
114   
115   if (!type)
116     {
117       const GInterfaceInfo activatable_info =
118       {
119         (GInterfaceInitFunc) gtk_tool_button_activatable_interface_init,
120         (GInterfaceFinalizeFunc) NULL,
121         NULL
122       };
123
124       type = g_type_register_static_simple (GTK_TYPE_TOOL_ITEM,
125                                             I_("GtkToolButton"),
126                                             sizeof (GtkToolButtonClass),
127                                             (GClassInitFunc) gtk_tool_button_class_init,
128                                             sizeof (GtkToolButton),
129                                             (GInstanceInitFunc) gtk_tool_button_init,
130                                             0);
131
132       g_type_add_interface_static (type, GTK_TYPE_ACTIVATABLE,
133                                    &activatable_info);
134     }
135   return type;
136 }
137
138 static void
139 gtk_tool_button_class_init (GtkToolButtonClass *klass)
140 {
141   GObjectClass *object_class;
142   GtkWidgetClass *widget_class;
143   GtkToolItemClass *tool_item_class;
144   
145   parent_class = g_type_class_peek_parent (klass);
146   
147   object_class = (GObjectClass *)klass;
148   widget_class = (GtkWidgetClass *)klass;
149   tool_item_class = (GtkToolItemClass *)klass;
150   
151   object_class->set_property = gtk_tool_button_set_property;
152   object_class->get_property = gtk_tool_button_get_property;
153   object_class->notify = gtk_tool_button_property_notify;
154   object_class->finalize = gtk_tool_button_finalize;
155
156   widget_class->style_set = gtk_tool_button_style_set;
157
158   tool_item_class->create_menu_proxy = gtk_tool_button_create_menu_proxy;
159   tool_item_class->toolbar_reconfigured = gtk_tool_button_toolbar_reconfigured;
160   
161   klass->button_type = GTK_TYPE_BUTTON;
162
163   /* Properties are interpreted like this:
164    *
165    *          - if the tool button has an icon_widget, then that widget
166    *            will be used as the icon. Otherwise, if the tool button
167    *            has a stock id, the corresponding stock icon will be
168    *            used. Otherwise, if the tool button has an icon name,
169    *            the corresponding icon from the theme will be used.
170    *            Otherwise, the tool button will not have an icon.
171    *
172    *          - if the tool button has a label_widget then that widget
173    *            will be used as the label. Otherwise, if the tool button
174    *            has a label text, that text will be used as label. Otherwise,
175    *            if the toolbutton has a stock id, the corresponding text
176    *            will be used as label. Otherwise, if the tool button has
177    *            an icon name, the corresponding icon name from the theme will
178    *            be used. Otherwise, the toolbutton will have an empty label.
179    *
180    *          - The use_underline property only has an effect when the label
181    *            on the toolbutton comes from the label property (ie. not from
182    *            label_widget or from stock_id).
183    *
184    *            In that case, if use_underline is set,
185    *
186    *                    - underscores are removed from the label text before
187    *                      the label is shown on the toolbutton unless the
188    *                      underscore is followed by another underscore
189    *
190    *                    - an underscore indicates that the next character when
191    *                      used in the overflow menu should be used as a
192    *                      mnemonic.
193    *
194    *            In short: use_underline = TRUE means that the label text has
195    *            the form "_Open" and the toolbar should take appropriate
196    *            action.
197    */
198
199   g_object_class_install_property (object_class,
200                                    PROP_LABEL,
201                                    g_param_spec_string ("label",
202                                                         P_("Label"),
203                                                         P_("Text to show in the item."),
204                                                         NULL,
205                                                         GTK_PARAM_READWRITE));
206   g_object_class_install_property (object_class,
207                                    PROP_USE_UNDERLINE,
208                                    g_param_spec_boolean ("use-underline",
209                                                          P_("Use underline"),
210                                                          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"),
211                                                          FALSE,
212                                                          GTK_PARAM_READWRITE));
213   g_object_class_install_property (object_class,
214                                    PROP_LABEL_WIDGET,
215                                    g_param_spec_object ("label-widget",
216                                                         P_("Label widget"),
217                                                         P_("Widget to use as the item label"),
218                                                         GTK_TYPE_WIDGET,
219                                                         GTK_PARAM_READWRITE));
220   g_object_class_install_property (object_class,
221                                    PROP_STOCK_ID,
222                                    g_param_spec_string ("stock-id",
223                                                         P_("Stock Id"),
224                                                         P_("The stock icon displayed on the item"),
225                                                         NULL,
226                                                         GTK_PARAM_READWRITE));
227
228   /**
229    * GtkToolButton:icon-name:
230    * 
231    * The name of the themed icon displayed on the item.
232    * This property only has an effect if not overridden by "label", 
233    * "icon_widget" or "stock_id" properties.
234    *
235    * Since: 2.8 
236    */
237   g_object_class_install_property (object_class,
238                                    PROP_ICON_NAME,
239                                    g_param_spec_string ("icon-name",
240                                                         P_("Icon name"),
241                                                         P_("The name of the themed icon displayed on the item"),
242                                                         NULL,
243                                                         GTK_PARAM_READWRITE));
244   g_object_class_install_property (object_class,
245                                    PROP_ICON_WIDGET,
246                                    g_param_spec_object ("icon-widget",
247                                                         P_("Icon widget"),
248                                                         P_("Icon widget to display in the item"),
249                                                         GTK_TYPE_WIDGET,
250                                                         GTK_PARAM_READWRITE));
251
252   /**
253    * GtkButton:icon-spacing:
254    * 
255    * Spacing in pixels between the icon and label.
256    * 
257    * Since: 2.10
258    */
259   gtk_widget_class_install_style_property (widget_class,
260                                            g_param_spec_int ("icon-spacing",
261                                                              P_("Icon spacing"),
262                                                              P_("Spacing in pixels between the icon and label"),
263                                                              0,
264                                                              G_MAXINT,
265                                                              3,
266                                                              GTK_PARAM_READWRITE));
267
268 /**
269  * GtkToolButton::clicked:
270  * @toolbutton: the object that emitted the signal
271  *
272  * This signal is emitted when the tool button is clicked with the mouse
273  * or activated with the keyboard.
274  **/
275   toolbutton_signals[CLICKED] =
276     g_signal_new (I_("clicked"),
277                   G_OBJECT_CLASS_TYPE (klass),
278                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
279                   G_STRUCT_OFFSET (GtkToolButtonClass, clicked),
280                   NULL, NULL,
281                   g_cclosure_marshal_VOID__VOID,
282                   G_TYPE_NONE, 0);
283   
284   g_type_class_add_private (object_class, sizeof (GtkToolButtonPrivate));
285 }
286
287 static void
288 gtk_tool_button_init (GtkToolButton      *button,
289                       GtkToolButtonClass *klass)
290 {
291   GtkToolItem *toolitem = GTK_TOOL_ITEM (button);
292
293   button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
294                                               GTK_TYPE_TOOL_BUTTON,
295                                               GtkToolButtonPrivate);
296
297   button->priv->contents_invalid = TRUE;
298
299   gtk_tool_item_set_homogeneous (toolitem, TRUE);
300
301   /* create button */
302   button->priv->button = g_object_new (klass->button_type, NULL);
303   gtk_button_set_focus_on_click (GTK_BUTTON (button->priv->button), FALSE);
304   g_signal_connect_object (button->priv->button, "clicked",
305                            G_CALLBACK (button_clicked), button, 0);
306
307   gtk_container_add (GTK_CONTAINER (button), button->priv->button);
308   gtk_widget_show (button->priv->button);
309 }
310
311 static void
312 gtk_tool_button_construct_contents (GtkToolItem *tool_item)
313 {
314   GtkToolButton *button = GTK_TOOL_BUTTON (tool_item);
315   GtkWidget *child;
316   GtkWidget *label = NULL;
317   GtkWidget *icon = NULL;
318   GtkToolbarStyle style;
319   gboolean need_label = FALSE;
320   gboolean need_icon = FALSE;
321   GtkIconSize icon_size;
322   GtkWidget *box = NULL;
323   guint icon_spacing;
324   GtkOrientation text_orientation = GTK_ORIENTATION_HORIZONTAL;
325   GtkSizeGroup *size_group = NULL;
326   GtkWidget *parent;
327
328   button->priv->contents_invalid = FALSE;
329
330   gtk_widget_style_get (GTK_WIDGET (tool_item), 
331                         "icon-spacing", &icon_spacing,
332                         NULL);
333
334   if (button->priv->icon_widget)
335     {
336       parent = gtk_widget_get_parent (button->priv->icon_widget);
337       if (parent)
338         {
339           gtk_container_remove (GTK_CONTAINER (parent),
340                                 button->priv->icon_widget);
341         }
342     }
343
344   if (button->priv->label_widget)
345     {
346       parent = gtk_widget_get_parent (button->priv->label_widget);
347       if (parent)
348         {
349           gtk_container_remove (GTK_CONTAINER (parent),
350                                 button->priv->label_widget);
351         }
352     }
353
354   child = gtk_bin_get_child (GTK_BIN (button->priv->button));
355   if (child)
356     {
357       /* Note: we are not destroying the label_widget or icon_widget
358        * here because they were removed from their containers above
359        */
360       gtk_widget_destroy (child);
361     }
362
363   style = gtk_tool_item_get_toolbar_style (GTK_TOOL_ITEM (button));
364   
365   if (style != GTK_TOOLBAR_TEXT)
366     need_icon = TRUE;
367
368   if (style != GTK_TOOLBAR_ICONS && style != GTK_TOOLBAR_BOTH_HORIZ)
369     need_label = TRUE;
370
371   if (style == GTK_TOOLBAR_BOTH_HORIZ &&
372       (gtk_tool_item_get_is_important (GTK_TOOL_ITEM (button)) ||
373        gtk_tool_item_get_orientation (GTK_TOOL_ITEM (button)) == GTK_ORIENTATION_VERTICAL ||
374        gtk_tool_item_get_text_orientation (GTK_TOOL_ITEM (button)) == GTK_ORIENTATION_VERTICAL))
375     {
376       need_label = TRUE;
377     }
378   
379   if (style == GTK_TOOLBAR_ICONS && button->priv->icon_widget == NULL &&
380       button->priv->stock_id == NULL && button->priv->icon_name == NULL)
381     {
382       need_label = TRUE;
383       need_icon = FALSE;
384       style = GTK_TOOLBAR_TEXT;
385     }
386
387   if (style == GTK_TOOLBAR_TEXT && button->priv->label_widget == NULL &&
388       button->priv->stock_id == NULL && button->priv->label_text == NULL)
389     {
390       need_label = FALSE;
391       need_icon = TRUE;
392       style = GTK_TOOLBAR_ICONS;
393     }
394
395   if (need_label)
396     {
397       if (button->priv->label_widget)
398         {
399           label = button->priv->label_widget;
400         }
401       else
402         {
403           GtkStockItem stock_item;
404           gboolean elide;
405           gchar *label_text;
406
407           if (button->priv->label_text)
408             {
409               label_text = button->priv->label_text;
410               elide = button->priv->use_underline;
411             }
412           else if (button->priv->stock_id && gtk_stock_lookup (button->priv->stock_id, &stock_item))
413             {
414               label_text = stock_item.label;
415               elide = TRUE;
416             }
417           else
418             {
419               label_text = "";
420               elide = FALSE;
421             }
422
423           if (elide)
424             label_text = _gtk_toolbar_elide_underscores (label_text);
425           else
426             label_text = g_strdup (label_text);
427
428           label = gtk_label_new (label_text);
429
430           g_free (label_text);
431           
432           gtk_widget_show (label);
433         }
434
435       if (GTK_IS_LABEL (label))
436         {
437           gtk_label_set_ellipsize (GTK_LABEL (label),
438                                    gtk_tool_item_get_ellipsize_mode (GTK_TOOL_ITEM (button)));
439           text_orientation = gtk_tool_item_get_text_orientation (GTK_TOOL_ITEM (button));
440           if (text_orientation == GTK_ORIENTATION_HORIZONTAL)
441             {
442               gtk_label_set_angle (GTK_LABEL (label), 0);
443               gtk_misc_set_alignment (GTK_MISC (label),
444                                       gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)),
445                                       0.5);
446             }
447           else
448             {
449               gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_NONE);
450               if (gtk_widget_get_direction (GTK_WIDGET (tool_item)) == GTK_TEXT_DIR_RTL)
451                 gtk_label_set_angle (GTK_LABEL (label), -90);
452               else
453                 gtk_label_set_angle (GTK_LABEL (label), 90);
454               gtk_misc_set_alignment (GTK_MISC (label),
455                                       0.5,
456                                       1 - gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)));
457             }
458         }
459     }
460
461   icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (button));
462   if (need_icon)
463     {
464       if (button->priv->icon_widget)
465         {
466           icon = button->priv->icon_widget;
467           
468           if (GTK_IS_IMAGE (icon))
469             {
470               g_object_set (button->priv->icon_widget,
471                             "icon-size", icon_size,
472                             NULL);
473             }
474         }
475       else if (button->priv->stock_id && 
476                gtk_icon_factory_lookup_default (button->priv->stock_id))
477         {
478           icon = gtk_image_new_from_stock (button->priv->stock_id, icon_size);
479           gtk_widget_show (icon);
480         }
481       else if (button->priv->icon_name)
482         {
483           icon = gtk_image_new_from_icon_name (button->priv->icon_name, icon_size);
484           gtk_widget_show (icon);
485         }
486
487       if (GTK_IS_MISC (icon) && text_orientation == GTK_ORIENTATION_HORIZONTAL)
488         gtk_misc_set_alignment (GTK_MISC (icon),
489                                 1.0 - gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)),
490                                 0.5);
491       else if (GTK_IS_MISC (icon))
492         gtk_misc_set_alignment (GTK_MISC (icon),
493                                 0.5,
494                                 gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)));
495
496       if (icon)
497         {
498           size_group = gtk_tool_item_get_text_size_group (GTK_TOOL_ITEM (button));
499           if (size_group != NULL)
500             gtk_size_group_add_widget (size_group, icon);
501         }
502     }
503
504   switch (style)
505     {
506     case GTK_TOOLBAR_ICONS:
507       if (icon)
508         gtk_container_add (GTK_CONTAINER (button->priv->button), icon);
509       break;
510
511     case GTK_TOOLBAR_BOTH:
512       if (text_orientation == GTK_ORIENTATION_HORIZONTAL)
513         box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, icon_spacing);
514       else
515         box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, icon_spacing);
516       if (icon)
517         gtk_box_pack_start (GTK_BOX (box), icon, TRUE, TRUE, 0);
518       gtk_box_pack_end (GTK_BOX (box), label, FALSE, TRUE, 0);
519       gtk_container_add (GTK_CONTAINER (button->priv->button), box);
520       break;
521
522     case GTK_TOOLBAR_BOTH_HORIZ:
523       if (text_orientation == GTK_ORIENTATION_HORIZONTAL)
524         {
525           box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, icon_spacing);
526           if (icon)
527             gtk_box_pack_start (GTK_BOX (box), icon, label? FALSE : TRUE, TRUE, 0);
528           if (label)
529             gtk_box_pack_end (GTK_BOX (box), label, TRUE, TRUE, 0);
530         }
531       else
532         {
533           box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, icon_spacing);
534           if (icon)
535             gtk_box_pack_end (GTK_BOX (box), icon, label ? FALSE : TRUE, TRUE, 0);
536           if (label)
537             gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
538         }
539       gtk_container_add (GTK_CONTAINER (button->priv->button), box);
540       break;
541
542     case GTK_TOOLBAR_TEXT:
543       gtk_container_add (GTK_CONTAINER (button->priv->button), label);
544       break;
545     }
546
547   if (box)
548     gtk_widget_show (box);
549
550   gtk_button_set_relief (GTK_BUTTON (button->priv->button),
551                          gtk_tool_item_get_relief_style (GTK_TOOL_ITEM (button)));
552
553   gtk_tool_item_rebuild_menu (tool_item);
554   
555   gtk_widget_queue_resize (GTK_WIDGET (button));
556 }
557
558 static void
559 gtk_tool_button_set_property (GObject         *object,
560                               guint            prop_id,
561                               const GValue    *value,
562                               GParamSpec      *pspec)
563 {
564   GtkToolButton *button = GTK_TOOL_BUTTON (object);
565   
566   switch (prop_id)
567     {
568     case PROP_LABEL:
569       gtk_tool_button_set_label (button, g_value_get_string (value));
570       break;
571     case PROP_USE_UNDERLINE:
572       gtk_tool_button_set_use_underline (button, g_value_get_boolean (value));
573       break;
574     case PROP_LABEL_WIDGET:
575       gtk_tool_button_set_label_widget (button, g_value_get_object (value));
576       break;
577     case PROP_STOCK_ID:
578       gtk_tool_button_set_stock_id (button, g_value_get_string (value));
579       break;
580     case PROP_ICON_NAME:
581       gtk_tool_button_set_icon_name (button, g_value_get_string (value));
582       break;
583     case PROP_ICON_WIDGET:
584       gtk_tool_button_set_icon_widget (button, g_value_get_object (value));
585       break;
586     default:
587       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
588       break;
589     }
590 }
591
592 static void
593 gtk_tool_button_property_notify (GObject          *object,
594                                  GParamSpec       *pspec)
595 {
596   GtkToolButton *button = GTK_TOOL_BUTTON (object);
597
598   if (button->priv->contents_invalid ||
599       strcmp ("is-important", pspec->name) == 0)
600     gtk_tool_button_construct_contents (GTK_TOOL_ITEM (object));
601
602   if (parent_class->notify)
603     parent_class->notify (object, pspec);
604 }
605
606 static void
607 gtk_tool_button_get_property (GObject         *object,
608                               guint            prop_id,
609                               GValue          *value,
610                               GParamSpec      *pspec)
611 {
612   GtkToolButton *button = GTK_TOOL_BUTTON (object);
613
614   switch (prop_id)
615     {
616     case PROP_LABEL:
617       g_value_set_string (value, gtk_tool_button_get_label (button));
618       break;
619     case PROP_LABEL_WIDGET:
620       g_value_set_object (value, gtk_tool_button_get_label_widget (button));
621       break;
622     case PROP_USE_UNDERLINE:
623       g_value_set_boolean (value, gtk_tool_button_get_use_underline (button));
624       break;
625     case PROP_STOCK_ID:
626       g_value_set_string (value, button->priv->stock_id);
627       break;
628     case PROP_ICON_NAME:
629       g_value_set_string (value, button->priv->icon_name);
630       break;
631     case PROP_ICON_WIDGET:
632       g_value_set_object (value, button->priv->icon_widget);
633       break;
634     default:
635       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
636       break;
637     }
638 }
639
640 static void
641 gtk_tool_button_finalize (GObject *object)
642 {
643   GtkToolButton *button = GTK_TOOL_BUTTON (object);
644
645   g_free (button->priv->stock_id);
646   g_free (button->priv->icon_name);
647   g_free (button->priv->label_text);
648
649   if (button->priv->label_widget)
650     g_object_unref (button->priv->label_widget);
651
652   if (button->priv->icon_widget)
653     g_object_unref (button->priv->icon_widget);
654   
655   parent_class->finalize (object);
656 }
657
658 static GtkWidget *
659 clone_image_menu_size (GtkImage *image, GtkSettings *settings)
660 {
661   GtkImageType storage_type = gtk_image_get_storage_type (image);
662
663   if (storage_type == GTK_IMAGE_STOCK)
664     {
665       gchar *stock_id;
666       gtk_image_get_stock (image, &stock_id, NULL);
667       return gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
668     }
669   else if (storage_type == GTK_IMAGE_ICON_NAME)
670     {
671       const gchar *icon_name;
672       gtk_image_get_icon_name (image, &icon_name, NULL);
673       return gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
674     }
675   else if (storage_type == GTK_IMAGE_ICON_SET)
676     {
677       GtkIconSet *icon_set;
678       gtk_image_get_icon_set (image, &icon_set, NULL);
679       return gtk_image_new_from_icon_set (icon_set, GTK_ICON_SIZE_MENU);
680     }
681   else if (storage_type == GTK_IMAGE_GICON)
682     {
683       GIcon *icon;
684       gtk_image_get_gicon (image, &icon, NULL);
685       return gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
686     }
687   else if (storage_type == GTK_IMAGE_PIXBUF)
688     {
689       gint width, height;
690       
691       if (settings &&
692           gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
693                                              &width, &height))
694         {
695           GdkPixbuf *src_pixbuf, *dest_pixbuf;
696           GtkWidget *cloned_image;
697
698           src_pixbuf = gtk_image_get_pixbuf (image);
699           dest_pixbuf = gdk_pixbuf_scale_simple (src_pixbuf, width, height,
700                                                  GDK_INTERP_BILINEAR);
701
702           cloned_image = gtk_image_new_from_pixbuf (dest_pixbuf);
703           g_object_unref (dest_pixbuf);
704
705           return cloned_image;
706         }
707     }
708
709   return NULL;
710 }
711       
712 static gboolean
713 gtk_tool_button_create_menu_proxy (GtkToolItem *item)
714 {
715   GtkToolButton *button = GTK_TOOL_BUTTON (item);
716   GtkWidget *menu_item;
717   GtkWidget *menu_image = NULL;
718   GtkStockItem stock_item;
719   gboolean use_mnemonic = TRUE;
720   const char *label;
721
722   if (_gtk_tool_item_create_menu_proxy (item))
723     return TRUE;
724  
725   if (GTK_IS_LABEL (button->priv->label_widget))
726     {
727       label = gtk_label_get_label (GTK_LABEL (button->priv->label_widget));
728       use_mnemonic = gtk_label_get_use_underline (GTK_LABEL (button->priv->label_widget));
729     }
730   else if (button->priv->label_text)
731     {
732       label = button->priv->label_text;
733       use_mnemonic = button->priv->use_underline;
734     }
735   else if (button->priv->stock_id && gtk_stock_lookup (button->priv->stock_id, &stock_item))
736     {
737       label = stock_item.label;
738     }
739   else
740     {
741       label = "";
742     }
743   
744   if (use_mnemonic)
745     menu_item = gtk_image_menu_item_new_with_mnemonic (label);
746   else
747     menu_item = gtk_image_menu_item_new_with_label (label);
748
749   if (GTK_IS_IMAGE (button->priv->icon_widget))
750     {
751       menu_image = clone_image_menu_size (GTK_IMAGE (button->priv->icon_widget),
752                                           gtk_widget_get_settings (GTK_WIDGET (button)));
753     }
754   else if (button->priv->stock_id)
755     {
756       menu_image = gtk_image_new_from_stock (button->priv->stock_id, GTK_ICON_SIZE_MENU);
757     }
758
759   if (menu_image)
760     gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image);
761
762   g_signal_connect_closure_by_id (menu_item,
763                                   g_signal_lookup ("activate", G_OBJECT_TYPE (menu_item)), 0,
764                                   g_cclosure_new_object_swap (G_CALLBACK (gtk_button_clicked),
765                                                               G_OBJECT (GTK_TOOL_BUTTON (button)->priv->button)),
766                                   FALSE);
767
768   gtk_tool_item_set_proxy_menu_item (GTK_TOOL_ITEM (button), MENU_ID, menu_item);
769   
770   return TRUE;
771 }
772
773 static void
774 button_clicked (GtkWidget     *widget,
775                 GtkToolButton *button)
776 {
777   GtkAction *action;
778
779   action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (button));
780   
781   if (action)
782     gtk_action_activate (action);
783
784   g_signal_emit_by_name (button, "clicked");
785 }
786
787 static void
788 gtk_tool_button_toolbar_reconfigured (GtkToolItem *tool_item)
789 {
790   gtk_tool_button_construct_contents (tool_item);
791 }
792
793 static void 
794 gtk_tool_button_update_icon_spacing (GtkToolButton *button)
795 {
796   GtkWidget *box;
797   guint spacing;
798
799   box = gtk_bin_get_child (GTK_BIN (button->priv->button));
800   if (GTK_IS_BOX (box))
801     {
802       gtk_widget_style_get (GTK_WIDGET (button), 
803                             "icon-spacing", &spacing,
804                             NULL);
805       gtk_box_set_spacing (GTK_BOX (box), spacing);      
806     }
807 }
808
809 static void
810 gtk_tool_button_style_set (GtkWidget *widget,
811                            GtkStyle  *prev_style)
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 }