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