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