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