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