]> Pileus Git - ~andy/gtk/blob - gtk/gtktoggletoolbutton.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtktoggletoolbutton.c
1  /* gtktoggletoolbutton.c
2  *
3  * Copyright (C) 2002 Anders Carlsson <andersca@gnome.org>
4  * Copyright (C) 2002 James Henstridge <james@daa.com.au>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "config.h"
21 #include "gtktoggletoolbutton.h"
22 #include "gtkcheckmenuitem.h"
23 #include "gtklabel.h"
24 #include "gtktogglebutton.h"
25 #include "gtkstock.h"
26 #include "gtkintl.h"
27 #include "gtkradiotoolbutton.h"
28 #include "gtktoggleaction.h"
29 #include "gtkactivatable.h"
30 #include "gtkprivate.h"
31
32
33 /**
34  * SECTION:gtktoggletoolbutton
35  * @Short_description: A GtkToolItem containing a toggle button
36  * @Title: GtkToggleToolButton
37  * @See_also: #GtkToolbar, #GtkToolButton, #GtkSeparatorToolItem
38  *
39  * A #GtkToggleToolButton is a #GtkToolItem that contains a toggle
40  * button.
41  *
42  * Use gtk_toggle_tool_button_new() to create a new
43  * #GtkToggleToolButton. Use gtk_toggle_tool_button_new_from_stock() to
44  * create a new #GtkToggleToolButton containing a stock item.
45  */
46
47
48 #define MENU_ID "gtk-toggle-tool-button-menu-id"
49
50 enum {
51   TOGGLED,
52   LAST_SIGNAL
53 };
54
55 enum {
56   PROP_0,
57   PROP_ACTIVE
58 };
59
60
61 struct _GtkToggleToolButtonPrivate
62 {
63   guint active : 1;
64 };
65   
66
67 static void     gtk_toggle_tool_button_set_property        (GObject      *object,
68                                                             guint         prop_id,
69                                                             const GValue *value,
70                                                             GParamSpec   *pspec);
71 static void     gtk_toggle_tool_button_get_property        (GObject      *object,
72                                                             guint         prop_id,
73                                                             GValue       *value,
74                                                             GParamSpec   *pspec);
75
76 static gboolean gtk_toggle_tool_button_create_menu_proxy (GtkToolItem *button);
77
78 static void button_toggled      (GtkWidget           *widget,
79                                  GtkToggleToolButton *button);
80 static void menu_item_activated (GtkWidget           *widget,
81                                  GtkToggleToolButton *button);
82
83
84 static void gtk_toggle_tool_button_activatable_interface_init (GtkActivatableIface  *iface);
85 static void gtk_toggle_tool_button_update                     (GtkActivatable       *activatable,
86                                                                GtkAction            *action,
87                                                                const gchar          *property_name);
88 static void gtk_toggle_tool_button_sync_action_properties     (GtkActivatable       *activatable,
89                                                                GtkAction            *action);
90
91 static GtkActivatableIface *parent_activatable_iface;
92 static guint                toggle_signals[LAST_SIGNAL] = { 0 };
93
94 G_DEFINE_TYPE_WITH_CODE (GtkToggleToolButton, gtk_toggle_tool_button, GTK_TYPE_TOOL_BUTTON,
95                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
96                                                 gtk_toggle_tool_button_activatable_interface_init))
97
98 static void
99 gtk_toggle_tool_button_class_init (GtkToggleToolButtonClass *klass)
100 {
101   GObjectClass *object_class;
102   GtkToolItemClass *toolitem_class;
103   GtkToolButtonClass *toolbutton_class;
104
105   object_class = (GObjectClass *)klass;
106   toolitem_class = (GtkToolItemClass *)klass;
107   toolbutton_class = (GtkToolButtonClass *)klass;
108
109   object_class->set_property = gtk_toggle_tool_button_set_property;
110   object_class->get_property = gtk_toggle_tool_button_get_property;
111
112   toolitem_class->create_menu_proxy = gtk_toggle_tool_button_create_menu_proxy;
113   toolbutton_class->button_type = GTK_TYPE_TOGGLE_BUTTON;
114
115   /**
116    * GtkToggleToolButton:active:
117    *
118    * If the toggle tool button should be pressed in.
119    *
120    * Since: 2.8
121    */
122   g_object_class_install_property (object_class,
123                                    PROP_ACTIVE,
124                                    g_param_spec_boolean ("active",
125                                                          P_("Active"),
126                                                          P_("If the toggle button should be pressed in"),
127                                                          FALSE,
128                                                          GTK_PARAM_READWRITE));
129
130 /**
131  * GtkToggleToolButton::toggled:
132  * @toggle_tool_button: the object that emitted the signal
133  *
134  * Emitted whenever the toggle tool button changes state.
135  **/
136   toggle_signals[TOGGLED] =
137     g_signal_new (I_("toggled"),
138                   G_OBJECT_CLASS_TYPE (klass),
139                   G_SIGNAL_RUN_FIRST,
140                   G_STRUCT_OFFSET (GtkToggleToolButtonClass, toggled),
141                   NULL, NULL,
142                   g_cclosure_marshal_VOID__VOID,
143                   G_TYPE_NONE, 0);
144
145   g_type_class_add_private (object_class, sizeof (GtkToggleToolButtonPrivate));
146 }
147
148 static void
149 gtk_toggle_tool_button_init (GtkToggleToolButton *button)
150 {
151   GtkToolButton *tool_button = GTK_TOOL_BUTTON (button);
152   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (_gtk_tool_button_get_button (tool_button));
153
154   button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
155                                               GTK_TYPE_TOGGLE_TOOL_BUTTON,
156                                               GtkToggleToolButtonPrivate);
157
158   /* If the real button is a radio button, it may have been
159    * active at the time it was created.
160    */
161   button->priv->active = gtk_toggle_button_get_active (toggle_button);
162     
163   g_signal_connect_object (toggle_button,
164                            "toggled", G_CALLBACK (button_toggled), button, 0);
165 }
166
167 static void
168 gtk_toggle_tool_button_set_property (GObject      *object,
169                                      guint         prop_id,
170                                      const GValue *value,
171                                      GParamSpec   *pspec)
172 {
173   GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON (object);
174
175   switch (prop_id)
176     {
177       case PROP_ACTIVE:
178         gtk_toggle_tool_button_set_active (button, 
179                                            g_value_get_boolean (value));
180         break;
181
182       default:
183         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
184         break;
185     }
186 }
187
188 static void
189 gtk_toggle_tool_button_get_property (GObject    *object,
190                                      guint       prop_id,
191                                      GValue     *value,
192                                      GParamSpec *pspec)
193 {
194   GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON (object);
195
196   switch (prop_id)
197     {
198       case PROP_ACTIVE:
199         g_value_set_boolean (value, gtk_toggle_tool_button_get_active (button));
200         break;
201
202       default:
203         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
204         break;
205     }
206 }
207
208 static gboolean
209 gtk_toggle_tool_button_create_menu_proxy (GtkToolItem *item)
210 {
211   GtkToolButton *tool_button = GTK_TOOL_BUTTON (item);
212   GtkToggleToolButton *toggle_tool_button = GTK_TOGGLE_TOOL_BUTTON (item);
213   GtkWidget *menu_item = NULL;
214   GtkStockItem stock_item;
215   gboolean use_mnemonic = TRUE;
216   const char *label;
217   GtkWidget *label_widget;
218   const gchar *label_text;
219   const gchar *stock_id;
220
221   if (_gtk_tool_item_create_menu_proxy (item))
222     return TRUE;
223
224   label_widget = gtk_tool_button_get_label_widget (tool_button);
225   label_text = gtk_tool_button_get_label (tool_button);
226   stock_id = gtk_tool_button_get_stock_id (tool_button);
227
228   if (GTK_IS_LABEL (label_widget))
229     {
230       label = gtk_label_get_label (GTK_LABEL (label_widget));
231       use_mnemonic = gtk_label_get_use_underline (GTK_LABEL (label_widget));
232     }
233   else if (label_text)
234     {
235       label = label_text;
236       use_mnemonic = gtk_tool_button_get_use_underline (tool_button);
237     }
238   else if (stock_id && gtk_stock_lookup (stock_id, &stock_item))
239     {
240       label = stock_item.label;
241     }
242   else
243     {
244       label = "";
245     }
246   
247   if (use_mnemonic)
248     menu_item = gtk_check_menu_item_new_with_mnemonic (label);
249   else
250     menu_item = gtk_check_menu_item_new_with_label (label);
251
252   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
253                                   toggle_tool_button->priv->active);
254
255   if (GTK_IS_RADIO_TOOL_BUTTON (toggle_tool_button))
256     {
257       gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menu_item),
258                                              TRUE);
259     }
260
261   g_signal_connect_closure_by_id (menu_item,
262                                   g_signal_lookup ("activate", G_OBJECT_TYPE (menu_item)), 0,
263                                   g_cclosure_new_object (G_CALLBACK (menu_item_activated),
264                                                          G_OBJECT (toggle_tool_button)),
265                                   FALSE);
266
267   gtk_tool_item_set_proxy_menu_item (item, MENU_ID, menu_item);
268   
269   return TRUE;
270 }
271
272 /* There are two activatable widgets, a toggle button and a menu item.
273  *
274  * If a widget is activated and the state of the tool button is the same as
275  * the new state of the activated widget, then the other widget was the one
276  * that was activated by the user and updated the tool button's state.
277  *
278  * If the state of the tool button is not the same as the new state of the
279  * activated widget, then the activation was activated by the user, and the
280  * widget needs to make sure the tool button is updated before the other
281  * widget is activated. This will make sure the other widget a tool button
282  * in a state that matches its own new state.
283  */
284 static void
285 menu_item_activated (GtkWidget           *menu_item,
286                      GtkToggleToolButton *toggle_tool_button)
287 {
288   GtkToolButton *tool_button = GTK_TOOL_BUTTON (toggle_tool_button);
289   gboolean menu_active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menu_item));
290
291   if (toggle_tool_button->priv->active != menu_active)
292     {
293       toggle_tool_button->priv->active = menu_active;
294
295       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_tool_button_get_button (tool_button)),
296                                     toggle_tool_button->priv->active);
297
298       g_object_notify (G_OBJECT (toggle_tool_button), "active");
299       g_signal_emit (toggle_tool_button, toggle_signals[TOGGLED], 0);
300     }
301 }
302
303 static void
304 button_toggled (GtkWidget           *widget,
305                 GtkToggleToolButton *toggle_tool_button)
306 {
307   gboolean toggle_active;
308
309   toggle_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
310
311   if (toggle_tool_button->priv->active != toggle_active)
312     {
313       GtkWidget *menu_item;
314       
315       toggle_tool_button->priv->active = toggle_active;
316        
317       if ((menu_item =
318            gtk_tool_item_get_proxy_menu_item (GTK_TOOL_ITEM (toggle_tool_button), MENU_ID)))
319         {
320           gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
321                                           toggle_tool_button->priv->active);
322         }
323
324       g_object_notify (G_OBJECT (toggle_tool_button), "active");
325       g_signal_emit (toggle_tool_button, toggle_signals[TOGGLED], 0);
326     }
327 }
328
329 static void
330 gtk_toggle_tool_button_activatable_interface_init (GtkActivatableIface *iface)
331 {
332   parent_activatable_iface = g_type_interface_peek_parent (iface);
333   iface->update = gtk_toggle_tool_button_update;
334   iface->sync_action_properties = gtk_toggle_tool_button_sync_action_properties;
335 }
336
337 static void
338 gtk_toggle_tool_button_update (GtkActivatable *activatable,
339                                GtkAction      *action,
340                                const gchar    *property_name)
341 {
342   GtkToggleToolButton *button;
343
344   parent_activatable_iface->update (activatable, action, property_name);
345
346   button = GTK_TOGGLE_TOOL_BUTTON (activatable);
347
348   if (strcmp (property_name, "active") == 0)
349     {
350       gtk_action_block_activate (action);
351       gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
352       gtk_action_unblock_activate (action);
353     }
354 }
355
356 static void
357 gtk_toggle_tool_button_sync_action_properties (GtkActivatable *activatable,
358                                                GtkAction      *action)
359 {
360   GtkToggleToolButton *button;
361
362   parent_activatable_iface->sync_action_properties (activatable, action);
363
364   if (!GTK_IS_TOGGLE_ACTION (action))
365     return;
366
367   button = GTK_TOGGLE_TOOL_BUTTON (activatable);
368
369   gtk_action_block_activate (action);
370   gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
371   gtk_action_unblock_activate (action);
372 }
373
374
375 /**
376  * gtk_toggle_tool_button_new:
377  * 
378  * Returns a new #GtkToggleToolButton
379  * 
380  * Return value: a newly created #GtkToggleToolButton
381  * 
382  * Since: 2.4
383  **/
384 GtkToolItem *
385 gtk_toggle_tool_button_new (void)
386 {
387   GtkToolButton *button;
388
389   button = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON,
390                          NULL);
391   
392   return GTK_TOOL_ITEM (button);
393 }
394
395 /**
396  * gtk_toggle_tool_button_new_from_stock:
397  * @stock_id: the name of the stock item 
398  *
399  * Creates a new #GtkToggleToolButton containing the image and text from a
400  * stock item. Some stock ids have preprocessor macros like #GTK_STOCK_OK
401  * and #GTK_STOCK_APPLY.
402  *
403  * It is an error if @stock_id is not a name of a stock item.
404  * 
405  * Return value: A new #GtkToggleToolButton
406  * 
407  * Since: 2.4
408  **/
409 GtkToolItem *
410 gtk_toggle_tool_button_new_from_stock (const gchar *stock_id)
411 {
412   GtkToolButton *button;
413
414   g_return_val_if_fail (stock_id != NULL, NULL);
415   
416   button = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON,
417                          "stock-id", stock_id,
418                          NULL);
419   
420   return GTK_TOOL_ITEM (button);
421 }
422
423 /**
424  * gtk_toggle_tool_button_set_active:
425  * @button: a #GtkToggleToolButton
426  * @is_active: whether @button should be active
427  * 
428  * Sets the status of the toggle tool button. Set to %TRUE if you
429  * want the GtkToggleButton to be 'pressed in', and %FALSE to raise it.
430  * This action causes the toggled signal to be emitted.
431  * 
432  * Since: 2.4
433  **/
434 void
435 gtk_toggle_tool_button_set_active (GtkToggleToolButton *button,
436                                    gboolean is_active)
437 {
438   g_return_if_fail (GTK_IS_TOGGLE_TOOL_BUTTON (button));
439
440   is_active = is_active != FALSE;
441
442   if (button->priv->active != is_active)
443     gtk_button_clicked (GTK_BUTTON (_gtk_tool_button_get_button (GTK_TOOL_BUTTON (button))));
444 }
445
446 /**
447  * gtk_toggle_tool_button_get_active:
448  * @button: a #GtkToggleToolButton
449  * 
450  * Queries a #GtkToggleToolButton and returns its current state.
451  * Returns %TRUE if the toggle button is pressed in and %FALSE if it is raised.
452  * 
453  * Return value: %TRUE if the toggle tool button is pressed in, %FALSE if not
454  * 
455  * Since: 2.4
456  **/
457 gboolean
458 gtk_toggle_tool_button_get_active (GtkToggleToolButton *button)
459 {
460   g_return_val_if_fail (GTK_IS_TOGGLE_TOOL_BUTTON (button), FALSE);
461
462   return button->priv->active;
463 }