1 /* gtkactivatable.c
2 * Copyright (C) 2008 Tristan Van Berkom <tristan.van.berkom@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /**
19 * SECTION:gtkactivatable
20 * @Short_Description: An interface for activatable widgets
21 * @Title: GtkActivatable
22 *
23 * Activatable widgets can be connected to a #GtkAction and reflects
24 * the state of its action. A #GtkActivatable can also provide feedback
25 * through its action, as they are responsible for activating their
26 * related actions.
27 *
28 * # Implementing GtkActivatable
29 *
30 * When extending a class that is already #GtkActivatable; it is only
31 * necessary to implement the #GtkActivatable->sync_action_properties()
32 * and #GtkActivatable->update() methods and chain up to the parent
33 * implementation, however when introducing
34 * a new #GtkActivatable class; the #GtkActivatable:related-action and
35 * #GtkActivatable:use-action-appearance properties need to be handled by
36 * the implementor. Handling these properties is mostly a matter of installing
37 * the action pointer and boolean flag on your instance, and calling
38 * gtk_activatable_do_set_related_action() and
39 * gtk_activatable_sync_action_properties() at the appropriate times.
40 *
41 * ## A class fragment implementing #GtkActivatable
42 *
43 * |[<!-- language="C" -->
44 *
45 * enum {
46 * ...
47 *
48 * PROP_ACTIVATABLE_RELATED_ACTION,
49 * PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
50 * }
51 *
52 * struct _FooBarPrivate
53 * {
54 *
55 * ...
56 *
57 * GtkAction *action;
58 * gboolean use_action_appearance;
59 * };
60 *
61 * ...
62 *
63 * static void foo_bar_activatable_interface_init (GtkActivatableIface *iface);
64 * static void foo_bar_activatable_update (GtkActivatable *activatable,
65 * GtkAction *action,
66 * const gchar *property_name);
67 * static void foo_bar_activatable_sync_action_properties (GtkActivatable *activatable,
68 * GtkAction *action);
69 * ...
70 *
71 *
72 * static void
73 * foo_bar_class_init (FooBarClass *klass)
74 * {
75 *
76 * ...
77 *
78 * g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
79 * g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
80 *
81 * ...
82 * }
83 *
84 *
85 * static void
86 * foo_bar_activatable_interface_init (GtkActivatableIface *iface)
87 * {
88 * iface->update = foo_bar_activatable_update;
89 * iface->sync_action_properties = foo_bar_activatable_sync_action_properties;
90 * }
91 *
92 * ... Break the reference using gtk_activatable_do_set_related_action()...
93 *
94 * static void
95 * foo_bar_dispose (GObject *object)
96 * {
97 * FooBar *bar = FOO_BAR (object);
98 * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar);
99 *
100 * ...
101 *
102 * if (priv->action)
103 * {
104 * gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (bar), NULL);
105 * priv->action = NULL;
106 * }
107 * G_OBJECT_CLASS (foo_bar_parent_class)->dispose (object);
108 * }
109 *
110 * ... Handle the “related-action” and “use-action-appearance” properties ...
111 *
112 * static void
113 * foo_bar_set_property (GObject *object,
114 * guint prop_id,
115 * const GValue *value,
116 * GParamSpec *pspec)
117 * {
118 * FooBar *bar = FOO_BAR (object);
119 * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar);
120 *
121 * switch (prop_id)
122 * {
123 *
124 * ...
125 *
126 * case PROP_ACTIVATABLE_RELATED_ACTION:
127 * foo_bar_set_related_action (bar, g_value_get_object (value));
128 * break;
129 * case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
130 * foo_bar_set_use_action_appearance (bar, g_value_get_boolean (value));
131 * break;
132 * default:
133 * G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
134 * break;
135 * }
136 * }
137 *
138 * static void
139 * foo_bar_get_property (GObject *object,
140 * guint prop_id,
141 * GValue *value,
142 * GParamSpec *pspec)
143 * {
144 * FooBar *bar = FOO_BAR (object);
145 * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar);
146 *
147 * switch (prop_id)
148 * {
149 *
150 * ...
151 *
152 * case PROP_ACTIVATABLE_RELATED_ACTION:
153 * g_value_set_object (value, priv->action);
154 * break;
155 * case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
156 * g_value_set_boolean (value, priv->use_action_appearance);
157 * break;
158 * default:
159 * G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160 * break;
161 * }
162 * }
163 *
164 *
165 * static void
166 * foo_bar_set_use_action_appearance (FooBar *bar,
167 * gboolean use_appearance)
168 * {
169 * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar);
170 *
171 * if (priv->use_action_appearance != use_appearance)
172 * {
173 * priv->use_action_appearance = use_appearance;
174 *
175 * gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (bar), priv->action);
176 * }
177 * }
178 *
179 * ... call gtk_activatable_do_set_related_action() and then assign the action pointer,
180 * no need to reference the action here since gtk_activatable_do_set_related_action() already
181 * holds a reference here for you...
182 * static void
183 * foo_bar_set_related_action (FooBar *bar,
184 * GtkAction *action)
185 * {
186 * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (bar);
187 *
188 * if (priv->action == action)
189 * return;
190 *
191 * gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (bar), action);
192 *
193 * priv->action = action;
194 * }
195 *
196 * ... Selectively reset and update activatable depending on the use-action-appearance property ...
197 * static void
198 * gtk_button_activatable_sync_action_properties (GtkActivatable *activatable,
199 * GtkAction *action)
200 * {
201 * GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable);
202 *
203 * if (!action)
204 * return;
205 *
206 * if (gtk_action_is_visible (action))
207 * gtk_widget_show (GTK_WIDGET (activatable));
208 * else
209 * gtk_widget_hide (GTK_WIDGET (activatable));
210 *
211 * gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));
212 *
213 * ...
214 *
215 * if (priv->use_action_appearance)
216 * {
217 * if (gtk_action_get_stock_id (action))
218 * foo_bar_set_stock (button, gtk_action_get_stock_id (action));
219 * else if (gtk_action_get_label (action))
220 * foo_bar_set_label (button, gtk_action_get_label (action));
221 *
222 * ...
223 *
224 * }
225 * }
226 *
227 * static void
228 * foo_bar_activatable_update (GtkActivatable *activatable,
229 * GtkAction *action,
230 * const gchar *property_name)
231 * {
232 * FooBarPrivate *priv = FOO_BAR_GET_PRIVATE (activatable);
233 *
234 * if (strcmp (property_name, "visible") == 0)
235 * {
236 * if (gtk_action_is_visible (action))
237 * gtk_widget_show (GTK_WIDGET (activatable));
238 * else
239 * gtk_widget_hide (GTK_WIDGET (activatable));
240 * }
241 * else if (strcmp (property_name, "sensitive") == 0)
242 * gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));
243 *
244 * ...
245 *
246 * if (!priv->use_action_appearance)
247 * return;
248 *
249 * if (strcmp (property_name, "stock-id") == 0)
250 * foo_bar_set_stock (button, gtk_action_get_stock_id (action));
251 * else if (strcmp (property_name, "label") == 0)
252 * foo_bar_set_label (button, gtk_action_get_label (action));
253 *
254 * ...
255 * }
256 * ]|
257 */
258
259 #include "config.h"
260
261 #define GDK_DISABLE_DEPRECATION_WARNINGS
262
263 #include "gtkactivatable.h"
264 #include "gtkactiongroup.h"
265 #include "gtkprivate.h"
266 #include "gtkintl.h"
267
268
269 typedef GtkActivatableIface GtkActivatableInterface;
G_DEFINE_INTERFACE(GtkActivatable,gtk_activatable,G_TYPE_OBJECT)270 G_DEFINE_INTERFACE (GtkActivatable, gtk_activatable, G_TYPE_OBJECT)
271
272 static void
273 gtk_activatable_default_init (GtkActivatableInterface *iface)
274 {
275 /**
276 * GtkActivatable:related-action:
277 *
278 * The action that this activatable will activate and receive
279 * updates from for various states and possibly appearance.
280 *
281 * > #GtkActivatable implementors need to handle the this property and
282 * > call gtk_activatable_do_set_related_action() when it changes.
283 *
284 * Since: 2.16
285 *
286 * Deprecated: 3.10
287 */
288 g_object_interface_install_property (iface,
289 g_param_spec_object ("related-action",
290 P_("Related Action"),
291 P_("The action this activatable will activate and receive updates from"),
292 GTK_TYPE_ACTION,
293 GTK_PARAM_READWRITE));
294
295 /**
296 * GtkActivatable:use-action-appearance:
297 *
298 * Whether this activatable should reset its layout
299 * and appearance when setting the related action or when
300 * the action changes appearance.
301 *
302 * See the #GtkAction documentation directly to find which properties
303 * should be ignored by the #GtkActivatable when this property is %FALSE.
304 *
305 * > #GtkActivatable implementors need to handle this property
306 * > and call gtk_activatable_sync_action_properties() on the activatable
307 * > widget when it changes.
308 *
309 * Since: 2.16
310 *
311 * Deprecated: 3.10
312 */
313 g_object_interface_install_property (iface,
314 g_param_spec_boolean ("use-action-appearance",
315 P_("Use Action Appearance"),
316 P_("Whether to use the related actions appearance properties"),
317 TRUE,
318 GTK_PARAM_READWRITE));
319
320
321 }
322
323 static void
gtk_activatable_update(GtkActivatable * activatable,GtkAction * action,const gchar * property_name)324 gtk_activatable_update (GtkActivatable *activatable,
325 GtkAction *action,
326 const gchar *property_name)
327 {
328 GtkActivatableIface *iface;
329
330 g_return_if_fail (GTK_IS_ACTIVATABLE (activatable));
331
332 iface = GTK_ACTIVATABLE_GET_IFACE (activatable);
333 if (iface->update)
334 iface->update (activatable, action, property_name);
335 else
336 g_critical ("GtkActivatable->update() unimplemented for type %s",
337 g_type_name (G_OBJECT_TYPE (activatable)));
338 }
339
340 /**
341 * gtk_activatable_sync_action_properties:
342 * @activatable: a #GtkActivatable
343 * @action: (allow-none): the related #GtkAction or %NULL
344 *
345 * This is called to update the activatable completely, this is called
346 * internally when the #GtkActivatable:related-action property is set
347 * or unset and by the implementing class when
348 * #GtkActivatable:use-action-appearance changes.
349 *
350 * Since: 2.16
351 *
352 * Deprecated: 3.10
353 **/
354 void
gtk_activatable_sync_action_properties(GtkActivatable * activatable,GtkAction * action)355 gtk_activatable_sync_action_properties (GtkActivatable *activatable,
356 GtkAction *action)
357 {
358 GtkActivatableIface *iface;
359
360 g_return_if_fail (GTK_IS_ACTIVATABLE (activatable));
361
362 iface = GTK_ACTIVATABLE_GET_IFACE (activatable);
363 if (iface->sync_action_properties)
364 iface->sync_action_properties (activatable, action);
365 else
366 g_critical ("GtkActivatable->sync_action_properties() unimplemented for type %s",
367 g_type_name (G_OBJECT_TYPE (activatable)));
368 }
369
370
371 /**
372 * gtk_activatable_set_related_action:
373 * @activatable: a #GtkActivatable
374 * @action: the #GtkAction to set
375 *
376 * Sets the related action on the @activatable object.
377 *
378 * > #GtkActivatable implementors need to handle the #GtkActivatable:related-action
379 * > property and call gtk_activatable_do_set_related_action() when it changes.
380 *
381 * Since: 2.16
382 *
383 * Deprecated: 3.10
384 **/
385 void
gtk_activatable_set_related_action(GtkActivatable * activatable,GtkAction * action)386 gtk_activatable_set_related_action (GtkActivatable *activatable,
387 GtkAction *action)
388 {
389 g_return_if_fail (GTK_IS_ACTIVATABLE (activatable));
390 g_return_if_fail (action == NULL || GTK_IS_ACTION (action));
391
392 g_object_set (activatable, "related-action", action, NULL);
393 }
394
395 static void
gtk_activatable_action_notify(GtkAction * action,GParamSpec * pspec,GtkActivatable * activatable)396 gtk_activatable_action_notify (GtkAction *action,
397 GParamSpec *pspec,
398 GtkActivatable *activatable)
399 {
400 gtk_activatable_update (activatable, action, pspec->name);
401 }
402
403 /**
404 * gtk_activatable_do_set_related_action:
405 * @activatable: a #GtkActivatable
406 * @action: the #GtkAction to set
407 *
408 * This is a utility function for #GtkActivatable implementors.
409 *
410 * When implementing #GtkActivatable you must call this when
411 * handling changes of the #GtkActivatable:related-action, and
412 * you must also use this to break references in #GObject->dispose().
413 *
414 * This function adds a reference to the currently set related
415 * action for you, it also makes sure the #GtkActivatable->update()
416 * method is called when the related #GtkAction properties change
417 * and registers to the action’s proxy list.
418 *
419 * > Be careful to call this before setting the local
420 * > copy of the #GtkAction property, since this function uses
421 * > gtk_activatable_get_related_action() to retrieve the
422 * > previous action.
423 *
424 * Since: 2.16
425 *
426 * Deprecated: 3.10
427 */
428 void
gtk_activatable_do_set_related_action(GtkActivatable * activatable,GtkAction * action)429 gtk_activatable_do_set_related_action (GtkActivatable *activatable,
430 GtkAction *action)
431 {
432 GtkAction *prev_action;
433
434 prev_action = gtk_activatable_get_related_action (activatable);
435
436 if (prev_action != action)
437 {
438 if (prev_action)
439 {
440 g_signal_handlers_disconnect_by_func (prev_action, gtk_activatable_action_notify, activatable);
441
442 /* Check the type so that actions can be activatable too. */
443 if (GTK_IS_WIDGET (activatable))
444 _gtk_action_remove_from_proxy_list (prev_action, GTK_WIDGET (activatable));
445
446 /* Some apps are using the object data directly...
447 * so continue to set it for a bit longer
448 */
449 g_object_set_data (G_OBJECT (activatable), "gtk-action", NULL);
450
451 /*
452 * We don't want prev_action to be activated
453 * during the sync_action_properties() call when syncing "active".
454 */
455 gtk_action_block_activate (prev_action);
456 }
457
458 /* Some applications rely on their proxy UI to be set up
459 * before they receive the ::connect-proxy signal, so we
460 * need to call sync_action_properties() before add_to_proxy_list().
461 */
462 gtk_activatable_sync_action_properties (activatable, action);
463
464 if (prev_action)
465 {
466 gtk_action_unblock_activate (prev_action);
467 g_object_unref (prev_action);
468 }
469
470 if (action)
471 {
472 g_object_ref (action);
473
474 g_signal_connect (G_OBJECT (action), "notify", G_CALLBACK (gtk_activatable_action_notify), activatable);
475
476 if (GTK_IS_WIDGET (activatable))
477 _gtk_action_add_to_proxy_list (action, GTK_WIDGET (activatable));
478
479 g_object_set_data (G_OBJECT (activatable), "gtk-action", action);
480 }
481 }
482 }
483
484 /**
485 * gtk_activatable_get_related_action:
486 * @activatable: a #GtkActivatable
487 *
488 * Gets the related #GtkAction for @activatable.
489 *
490 * Returns: (transfer none): the related #GtkAction if one is set.
491 *
492 * Since: 2.16
493 *
494 * Deprecated: 3.10
495 **/
496 GtkAction *
gtk_activatable_get_related_action(GtkActivatable * activatable)497 gtk_activatable_get_related_action (GtkActivatable *activatable)
498 {
499 GtkAction *action;
500
501 g_return_val_if_fail (GTK_IS_ACTIVATABLE (activatable), NULL);
502
503 g_object_get (activatable, "related-action", &action, NULL);
504
505 /* g_object_get() gives us a ref... */
506 if (action)
507 g_object_unref (action);
508
509 return action;
510 }
511
512 /**
513 * gtk_activatable_set_use_action_appearance:
514 * @activatable: a #GtkActivatable
515 * @use_appearance: whether to use the actions appearance
516 *
517 * Sets whether this activatable should reset its layout and appearance
518 * when setting the related action or when the action changes appearance
519 *
520 * > #GtkActivatable implementors need to handle the
521 * > #GtkActivatable:use-action-appearance property and call
522 * > gtk_activatable_sync_action_properties() to update @activatable
523 * > if needed.
524 *
525 * Since: 2.16
526 *
527 * Deprecated: 3.10
528 **/
529 void
gtk_activatable_set_use_action_appearance(GtkActivatable * activatable,gboolean use_appearance)530 gtk_activatable_set_use_action_appearance (GtkActivatable *activatable,
531 gboolean use_appearance)
532 {
533 g_object_set (activatable, "use-action-appearance", use_appearance, NULL);
534 }
535
536 /**
537 * gtk_activatable_get_use_action_appearance:
538 * @activatable: a #GtkActivatable
539 *
540 * Gets whether this activatable should reset its layout
541 * and appearance when setting the related action or when
542 * the action changes appearance.
543 *
544 * Returns: whether @activatable uses its actions appearance.
545 *
546 * Since: 2.16
547 *
548 * Deprecated: 3.10
549 **/
550 gboolean
gtk_activatable_get_use_action_appearance(GtkActivatable * activatable)551 gtk_activatable_get_use_action_appearance (GtkActivatable *activatable)
552 {
553 gboolean use_appearance;
554
555 g_object_get (activatable, "use-action-appearance", &use_appearance, NULL);
556
557 return use_appearance;
558 }
559