]> Pileus Git - ~andy/gtk/blob - gtk/gtktoggletoolbutton.c
c527c136b28b69a5799475a9b406d0e49ad988db
[~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
205   GtkWidget *label_widget = gtk_tool_button_get_label_widget (tool_button);
206   const gchar *label_text = gtk_tool_button_get_label (tool_button);
207   const gchar *stock_id = gtk_tool_button_get_stock_id (tool_button);
208
209   if (GTK_IS_LABEL (label_widget))
210     {
211       label = gtk_label_get_label (GTK_LABEL (label_widget));
212       use_mnemonic = gtk_label_get_use_underline (GTK_LABEL (label_widget));
213     }
214   else if (label_text)
215     {
216       label = label_text;
217       use_mnemonic = gtk_tool_button_get_use_underline (tool_button);
218     }
219   else if (stock_id && gtk_stock_lookup (stock_id, &stock_item))
220     {
221       label = stock_item.label;
222     }
223   else
224     {
225       label = "";
226     }
227   
228   if (use_mnemonic)
229     menu_item = gtk_check_menu_item_new_with_mnemonic (label);
230   else
231     menu_item = gtk_check_menu_item_new_with_label (label);
232
233   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
234                                   toggle_tool_button->priv->active);
235
236   if (GTK_IS_RADIO_TOOL_BUTTON (toggle_tool_button))
237     {
238       gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menu_item),
239                                              TRUE);
240     }
241
242   g_signal_connect_closure_by_id (menu_item,
243                                   g_signal_lookup ("activate", G_OBJECT_TYPE (menu_item)), 0,
244                                   g_cclosure_new_object (G_CALLBACK (menu_item_activated),
245                                                          G_OBJECT (toggle_tool_button)),
246                                   FALSE);
247
248   gtk_tool_item_set_proxy_menu_item (item, MENU_ID, menu_item);
249   
250   return TRUE;
251 }
252
253 /* There are two activatable widgets, a toggle button and a menu item.
254  *
255  * If a widget is activated and the state of the tool button is the same as
256  * the new state of the activated widget, then the other widget was the one
257  * that was activated by the user and updated the tool button's state.
258  *
259  * If the state of the tool button is not the same as the new state of the
260  * activated widget, then the activation was activated by the user, and the
261  * widget needs to make sure the tool button is updated before the other
262  * widget is activated. This will make sure the other widget a tool button
263  * in a state that matches its own new state.
264  */
265 static void
266 menu_item_activated (GtkWidget           *menu_item,
267                      GtkToggleToolButton *toggle_tool_button)
268 {
269   GtkToolButton *tool_button = GTK_TOOL_BUTTON (toggle_tool_button);
270   gboolean menu_active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menu_item));
271
272   if (toggle_tool_button->priv->active != menu_active)
273     {
274       toggle_tool_button->priv->active = menu_active;
275
276       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_tool_button_get_button (tool_button)),
277                                     toggle_tool_button->priv->active);
278
279       g_object_notify (G_OBJECT (toggle_tool_button), "active");
280       g_signal_emit (toggle_tool_button, toggle_signals[TOGGLED], 0);
281     }
282 }
283
284 static void
285 button_toggled (GtkWidget           *widget,
286                 GtkToggleToolButton *toggle_tool_button)
287 {
288   gboolean toggle_active = GTK_TOGGLE_BUTTON (widget)->active;
289
290   if (toggle_tool_button->priv->active != toggle_active)
291     {
292       GtkWidget *menu_item;
293       
294       toggle_tool_button->priv->active = toggle_active;
295        
296       if ((menu_item =
297            gtk_tool_item_get_proxy_menu_item (GTK_TOOL_ITEM (toggle_tool_button), MENU_ID)))
298         {
299           gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
300                                           toggle_tool_button->priv->active);
301         }
302
303       g_object_notify (G_OBJECT (toggle_tool_button), "active");
304       g_signal_emit (toggle_tool_button, toggle_signals[TOGGLED], 0);
305     }
306 }
307
308 static void 
309 gtk_toggle_tool_button_activatable_interface_init (GtkActivatableIface  *iface)
310 {
311   parent_activatable_iface = g_type_interface_peek_parent (iface);
312   iface->update = gtk_toggle_tool_button_activatable_update;
313   iface->reset = gtk_toggle_tool_button_activatable_reset;
314 }
315
316 static void
317 gtk_toggle_tool_button_activatable_update (GtkActivatable  *activatable,
318                                            GtkAction       *action,
319                                            const gchar     *property_name)
320 {
321   GtkToggleToolButton *button;
322
323   parent_activatable_iface->update (activatable, action, property_name);
324
325   button = GTK_TOGGLE_TOOL_BUTTON (activatable);
326
327   if (strcmp (property_name, "active") == 0)
328     {
329       gtk_action_block_activate (action);
330       gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
331       gtk_action_unblock_activate (action);
332     }
333 }
334
335 static void
336 gtk_toggle_tool_button_activatable_reset (GtkActivatable  *activatable,
337                                           GtkAction       *action)
338 {
339   GtkToggleToolButton *button;
340
341   parent_activatable_iface->reset (activatable, action);
342
343   if (!action)
344     return;
345
346   button = GTK_TOGGLE_TOOL_BUTTON (activatable);
347
348   gtk_action_block_activate (action);
349   gtk_toggle_tool_button_set_active (button, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
350   gtk_action_unblock_activate (action);
351 }
352
353
354 /**
355  * gtk_toggle_tool_button_new:
356  * 
357  * Returns a new #GtkToggleToolButton
358  * 
359  * Return value: a newly created #GtkToggleToolButton
360  * 
361  * Since: 2.4
362  **/
363 GtkToolItem *
364 gtk_toggle_tool_button_new (void)
365 {
366   GtkToolButton *button;
367
368   button = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON,
369                          NULL);
370   
371   return GTK_TOOL_ITEM (button);
372 }
373
374 /**
375  * gtk_toggle_tool_button_new_from_stock:
376  * @stock_id: the name of the stock item 
377  *
378  * Creates a new #GtkToggleToolButton containing the image and text from a
379  * stock item. Some stock ids have preprocessor macros like #GTK_STOCK_OK
380  * and #GTK_STOCK_APPLY.
381  *
382  * It is an error if @stock_id is not a name of a stock item.
383  * 
384  * Return value: A new #GtkToggleToolButton
385  * 
386  * Since: 2.4
387  **/
388 GtkToolItem *
389 gtk_toggle_tool_button_new_from_stock (const gchar *stock_id)
390 {
391   GtkToolButton *button;
392
393   g_return_val_if_fail (stock_id != NULL, NULL);
394   
395   button = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON,
396                          "stock-id", stock_id,
397                          NULL);
398   
399   return GTK_TOOL_ITEM (button);
400 }
401
402 /**
403  * gtk_toggle_tool_button_set_active:
404  * @button: a #GtkToggleToolButton
405  * @is_active: whether @button should be active
406  * 
407  * Sets the status of the toggle tool button. Set to %TRUE if you
408  * want the GtkToggleButton to be 'pressed in', and %FALSE to raise it.
409  * This action causes the toggled signal to be emitted.
410  * 
411  * Since: 2.4
412  **/
413 void
414 gtk_toggle_tool_button_set_active (GtkToggleToolButton *button,
415                                    gboolean is_active)
416 {
417   g_return_if_fail (GTK_IS_TOGGLE_TOOL_BUTTON (button));
418
419   is_active = is_active != FALSE;
420
421   if (button->priv->active != is_active)
422     gtk_button_clicked (GTK_BUTTON (_gtk_tool_button_get_button (GTK_TOOL_BUTTON (button))));
423 }
424
425 /**
426  * gtk_toggle_tool_button_get_active:
427  * @button: a #GtkToggleToolButton
428  * 
429  * Queries a #GtkToggleToolButton and returns its current state.
430  * Returns %TRUE if the toggle button is pressed in and %FALSE if it is raised.
431  * 
432  * Return value: %TRUE if the toggle tool button is pressed in, %FALSE if not
433  * 
434  * Since: 2.4
435  **/
436 gboolean
437 gtk_toggle_tool_button_get_active (GtkToggleToolButton *button)
438 {
439   g_return_val_if_fail (GTK_IS_TOGGLE_TOOL_BUTTON (button), FALSE);
440
441   return button->priv->active;
442 }
443
444 #define __GTK_TOGGLE_TOOL_BUTTON_C__
445 #include "gtkaliasdef.c"