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_update                     (GtkActivatable       *activatable,
75 							       GtkAction            *action,
76 							       const gchar          *property_name);
77 static void gtk_toggle_tool_button_sync_action_properties     (GtkActivatable       *activatable,
78 							       GtkAction            *action);
79 
80 static GtkActivatableIface *parent_activatable_iface;
81 static guint                toggle_signals[LAST_SIGNAL] = { 0 };
82 
G_DEFINE_TYPE_WITH_CODE(GtkToggleToolButton,gtk_toggle_tool_button,GTK_TYPE_TOOL_BUTTON,G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,gtk_toggle_tool_button_activatable_interface_init))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
gtk_toggle_tool_button_init(GtkToggleToolButton * button)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
gtk_toggle_tool_button_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gtk_toggle_tool_button_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gtk_toggle_tool_button_create_menu_proxy(GtkToolItem * item)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
menu_item_activated(GtkWidget * menu_item,GtkToggleToolButton * toggle_tool_button)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
button_toggled(GtkWidget * widget,GtkToggleToolButton * toggle_tool_button)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
gtk_toggle_tool_button_activatable_interface_init(GtkActivatableIface * iface)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_update;
319   iface->sync_action_properties = gtk_toggle_tool_button_sync_action_properties;
320 }
321 
322 static void
gtk_toggle_tool_button_update(GtkActivatable * activatable,GtkAction * action,const gchar * property_name)323 gtk_toggle_tool_button_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
gtk_toggle_tool_button_sync_action_properties(GtkActivatable * activatable,GtkAction * action)342 gtk_toggle_tool_button_sync_action_properties (GtkActivatable *activatable,
343 					       GtkAction      *action)
344 {
345   GtkToggleToolButton *button;
346 
347   parent_activatable_iface->sync_action_properties (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 *
gtk_toggle_tool_button_new(void)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 *
gtk_toggle_tool_button_new_from_stock(const gchar * stock_id)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
gtk_toggle_tool_button_set_active(GtkToggleToolButton * button,gboolean is_active)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
gtk_toggle_tool_button_get_active(GtkToggleToolButton * button)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"
452