]> Pileus Git - ~andy/gtk/blob - gtk/gtktoggletoolbutton.c
Make the new GtkAction code work with PolicyKit-gnome's use of actions.
[~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 #include "gtkalias.h"
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 #define GTK_TOGGLE_TOOL_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TOGGLE_TOOL_BUTTON, GtkToggleToolButtonPrivate))
49
50 struct _GtkToggleToolButtonPrivate
51 {
52   guint active : 1;
53 };
54   
55
56 static void     gtk_toggle_tool_button_set_property        (GObject      *object,
57                                                             guint         prop_id,
58                                                             const GValue *value,
59                                                             GParamSpec   *pspec);
60 static void     gtk_toggle_tool_button_get_property        (GObject      *object,
61                                                             guint         prop_id,
62                                                             GValue       *value,
63                                                             GParamSpec   *pspec);
64
65 static gboolean gtk_toggle_tool_button_create_menu_proxy (GtkToolItem *button);
66
67 static void button_toggled      (GtkWidget           *widget,
68                                  GtkToggleToolButton *button);
69 static void menu_item_activated (GtkWidget           *widget,
70                                  GtkToggleToolButton *button);
71
72
73 static void gtk_toggle_tool_button_activatable_interface_init (GtkActivatableIface  *iface);
74 static void gtk_toggle_tool_button_activatable_update         (GtkActivatable       *activatable,
75                                                                GtkAction            *action,
76                                                                const gchar          *property_name);
77 static void gtk_toggle_tool_button_activatable_reset          (GtkActivatable       *activatable,
78                                                                GtkAction            *action);
79
80 static GtkActivatableIface *parent_activatable_iface;
81 static guint                toggle_signals[LAST_SIGNAL] = { 0 };
82
83 G_DEFINE_TYPE_WITH_CODE (GtkToggleToolButton, gtk_toggle_tool_button, GTK_TYPE_TOOL_BUTTON,
84                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
85                                                 gtk_toggle_tool_button_activatable_interface_init))
86
87 static void
88 gtk_toggle_tool_button_class_init (GtkToggleToolButtonClass *klass)
89 {
90   GObjectClass *object_class;
91   GtkToolItemClass *toolitem_class;
92   GtkToolButtonClass *toolbutton_class;
93
94   object_class = (GObjectClass *)klass;
95   toolitem_class = (GtkToolItemClass *)klass;
96   toolbutton_class = (GtkToolButtonClass *)klass;
97
98   object_class->set_property = gtk_toggle_tool_button_set_property;
99   object_class->get_property = gtk_toggle_tool_button_get_property;
100
101   toolitem_class->create_menu_proxy = gtk_toggle_tool_button_create_menu_proxy;
102   toolbutton_class->button_type = GTK_TYPE_TOGGLE_BUTTON;
103
104   /**
105    * GtkToggleToolButton:active:
106    *
107    * If the toggle tool button should be pressed in or not.
108    *
109    * Since: 2.8
110    */
111   g_object_class_install_property (object_class,
112                                    PROP_ACTIVE,
113                                    g_param_spec_boolean ("active",
114                                                          P_("Active"),
115                                                          P_("If the toggle button should be pressed in or not"),
116                                                          FALSE,
117                                                          GTK_PARAM_READWRITE));
118
119 /**
120  * GtkToggleToolButton::toggled:
121  * @toggle_tool_button: the object that emitted the signal
122  *
123  * Emitted whenever the toggle tool button changes state.
124  **/
125   toggle_signals[TOGGLED] =
126     g_signal_new (I_("toggled"),
127                   G_OBJECT_CLASS_TYPE (klass),
128                   G_SIGNAL_RUN_FIRST,
129                   G_STRUCT_OFFSET (GtkToggleToolButtonClass, toggled),
130                   NULL, NULL,
131                   g_cclosure_marshal_VOID__VOID,
132                   G_TYPE_NONE, 0);
133
134   g_type_class_add_private (object_class, sizeof (GtkToggleToolButtonPrivate));
135 }
136
137 static void
138 gtk_toggle_tool_button_init (GtkToggleToolButton *button)
139 {
140   GtkToolButton *tool_button = GTK_TOOL_BUTTON (button);
141   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (_gtk_tool_button_get_button (tool_button));
142
143   button->priv = GTK_TOGGLE_TOOL_BUTTON_GET_PRIVATE (button);
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 = GTK_TOGGLE_BUTTON (widget)->active;
295
296   if (toggle_tool_button->priv->active != toggle_active)
297     {
298       GtkWidget *menu_item;
299       
300       toggle_tool_button->priv->active = toggle_active;
301        
302       if ((menu_item =
303            gtk_tool_item_get_proxy_menu_item (GTK_TOOL_ITEM (toggle_tool_button), MENU_ID)))
304         {
305           gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
306                                           toggle_tool_button->priv->active);
307         }
308
309       g_object_notify (G_OBJECT (toggle_tool_button), "active");
310       g_signal_emit (toggle_tool_button, toggle_signals[TOGGLED], 0);
311     }
312 }
313
314 static void 
315 gtk_toggle_tool_button_activatable_interface_init (GtkActivatableIface  *iface)
316 {
317   parent_activatable_iface = g_type_interface_peek_parent (iface);
318   iface->update = gtk_toggle_tool_button_activatable_update;
319   iface->reset = gtk_toggle_tool_button_activatable_reset;
320 }
321
322 static void
323 gtk_toggle_tool_button_activatable_update (GtkActivatable  *activatable,
324                                            GtkAction       *action,
325                                            const gchar     *property_name)
326 {
327   GtkToggleToolButton *button;
328
329   parent_activatable_iface->update (activatable, action, property_name);
330
331   button = GTK_TOGGLE_TOOL_BUTTON (activatable);
332
333   if (strcmp (property_name, "active") == 0)
334     {
335       gtk_action_block_activate (action);
336       gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
337       gtk_action_unblock_activate (action);
338     }
339 }
340
341 static void
342 gtk_toggle_tool_button_activatable_reset (GtkActivatable  *activatable,
343                                           GtkAction       *action)
344 {
345   GtkToggleToolButton *button;
346
347   parent_activatable_iface->reset (activatable, action);
348
349   if (!GTK_IS_TOGGLE_ACTION (action))
350     return;
351
352   button = GTK_TOGGLE_TOOL_BUTTON (activatable);
353
354   gtk_action_block_activate (action);
355   gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
356   gtk_action_unblock_activate (action);
357 }
358
359
360 /**
361  * gtk_toggle_tool_button_new:
362  * 
363  * Returns a new #GtkToggleToolButton
364  * 
365  * Return value: a newly created #GtkToggleToolButton
366  * 
367  * Since: 2.4
368  **/
369 GtkToolItem *
370 gtk_toggle_tool_button_new (void)
371 {
372   GtkToolButton *button;
373
374   button = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON,
375                          NULL);
376   
377   return GTK_TOOL_ITEM (button);
378 }
379
380 /**
381  * gtk_toggle_tool_button_new_from_stock:
382  * @stock_id: the name of the stock item 
383  *
384  * Creates a new #GtkToggleToolButton containing the image and text from a
385  * stock item. Some stock ids have preprocessor macros like #GTK_STOCK_OK
386  * and #GTK_STOCK_APPLY.
387  *
388  * It is an error if @stock_id is not a name of a stock item.
389  * 
390  * Return value: A new #GtkToggleToolButton
391  * 
392  * Since: 2.4
393  **/
394 GtkToolItem *
395 gtk_toggle_tool_button_new_from_stock (const gchar *stock_id)
396 {
397   GtkToolButton *button;
398
399   g_return_val_if_fail (stock_id != NULL, NULL);
400   
401   button = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON,
402                          "stock-id", stock_id,
403                          NULL);
404   
405   return GTK_TOOL_ITEM (button);
406 }
407
408 /**
409  * gtk_toggle_tool_button_set_active:
410  * @button: a #GtkToggleToolButton
411  * @is_active: whether @button should be active
412  * 
413  * Sets the status of the toggle tool button. Set to %TRUE if you
414  * want the GtkToggleButton to be 'pressed in', and %FALSE to raise it.
415  * This action causes the toggled signal to be emitted.
416  * 
417  * Since: 2.4
418  **/
419 void
420 gtk_toggle_tool_button_set_active (GtkToggleToolButton *button,
421                                    gboolean is_active)
422 {
423   g_return_if_fail (GTK_IS_TOGGLE_TOOL_BUTTON (button));
424
425   is_active = is_active != FALSE;
426
427   if (button->priv->active != is_active)
428     gtk_button_clicked (GTK_BUTTON (_gtk_tool_button_get_button (GTK_TOOL_BUTTON (button))));
429 }
430
431 /**
432  * gtk_toggle_tool_button_get_active:
433  * @button: a #GtkToggleToolButton
434  * 
435  * Queries a #GtkToggleToolButton and returns its current state.
436  * Returns %TRUE if the toggle button is pressed in and %FALSE if it is raised.
437  * 
438  * Return value: %TRUE if the toggle tool button is pressed in, %FALSE if not
439  * 
440  * Since: 2.4
441  **/
442 gboolean
443 gtk_toggle_tool_button_get_active (GtkToggleToolButton *button)
444 {
445   g_return_val_if_fail (GTK_IS_TOGGLE_TOOL_BUTTON (button), FALSE);
446
447   return button->priv->active;
448 }
449
450 #define __GTK_TOGGLE_TOOL_BUTTON_C__
451 #include "gtkaliasdef.c"