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