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