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