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