]> Pileus Git - ~andy/gtk/blob - gtk/gtkradioaction.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkradioaction.c
1 /*
2  * GTK - The GIMP Toolkit
3  * Copyright (C) 1998, 1999 Red Hat, Inc.
4  * All rights reserved.
5  *
6  * This Library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /*
21  * Author: James Henstridge <james@daa.com.au>
22  *
23  * Modified by the GTK+ Team and others 2003.  See the AUTHORS
24  * file for a list of people on the GTK+ Team.  See the ChangeLog
25  * files for a list of changes.  These files are distributed with
26  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
27  */
28
29 #include "config.h"
30
31 #include "gtkradioaction.h"
32 #include "gtkradiomenuitem.h"
33 #include "gtktoggletoolbutton.h"
34 #include "gtkintl.h"
35 #include "gtkprivate.h"
36
37 /**
38  * SECTION:gtkradioaction
39  * @Short_description: An action of which only one in a group can be active
40  * @Title: GtkRadioAction
41  *
42  * A #GtkRadioAction is similar to #GtkRadioMenuItem. A number of radio
43  * actions can be linked together so that only one may be active at any
44  * one time.
45  */
46
47
48 struct _GtkRadioActionPrivate 
49 {
50   GSList *group;
51   gint    value;
52 };
53
54 enum 
55 {
56   CHANGED,
57   LAST_SIGNAL
58 };
59
60 enum 
61 {
62   PROP_0,
63   PROP_VALUE,
64   PROP_GROUP,
65   PROP_CURRENT_VALUE
66 };
67
68 static void gtk_radio_action_finalize     (GObject *object);
69 static void gtk_radio_action_set_property (GObject         *object,
70                                            guint            prop_id,
71                                            const GValue    *value,
72                                            GParamSpec      *pspec);
73 static void gtk_radio_action_get_property (GObject         *object,
74                                            guint            prop_id,
75                                            GValue          *value,
76                                            GParamSpec      *pspec);
77 static void gtk_radio_action_activate     (GtkAction *action);
78 static GtkWidget *create_menu_item        (GtkAction *action);
79
80
81 G_DEFINE_TYPE (GtkRadioAction, gtk_radio_action, GTK_TYPE_TOGGLE_ACTION)
82
83 static guint         radio_action_signals[LAST_SIGNAL] = { 0 };
84
85 static void
86 gtk_radio_action_class_init (GtkRadioActionClass *klass)
87 {
88   GObjectClass *gobject_class;
89   GtkActionClass *action_class;
90
91   gobject_class = G_OBJECT_CLASS (klass);
92   action_class = GTK_ACTION_CLASS (klass);
93
94   gobject_class->finalize = gtk_radio_action_finalize;
95   gobject_class->set_property = gtk_radio_action_set_property;
96   gobject_class->get_property = gtk_radio_action_get_property;
97
98   action_class->activate = gtk_radio_action_activate;
99
100   action_class->create_menu_item = create_menu_item;
101
102   /**
103    * GtkRadioAction:value:
104    *
105    * The value is an arbitrary integer which can be used as a
106    * convenient way to determine which action in the group is 
107    * currently active in an ::activate or ::changed signal handler.
108    * See gtk_radio_action_get_current_value() and #GtkRadioActionEntry
109    * for convenient ways to get and set this property.
110    *
111    * Since: 2.4
112    */
113   g_object_class_install_property (gobject_class,
114                                    PROP_VALUE,
115                                    g_param_spec_int ("value",
116                                                      P_("The value"),
117                                                      P_("The value returned by gtk_radio_action_get_current_value() when this action is the current action of its group."),
118                                                      G_MININT,
119                                                      G_MAXINT,
120                                                      0,
121                                                      GTK_PARAM_READWRITE));
122
123   /**
124    * GtkRadioAction:group:
125    *
126    * Sets a new group for a radio action.
127    *
128    * Since: 2.4
129    */
130   g_object_class_install_property (gobject_class,
131                                    PROP_GROUP,
132                                    g_param_spec_object ("group",
133                                                         P_("Group"),
134                                                         P_("The radio action whose group this action belongs to."),
135                                                         GTK_TYPE_RADIO_ACTION,
136                                                         GTK_PARAM_WRITABLE));
137
138   /**
139    * GtkRadioAction:current-value:
140    *
141    * The value property of the currently active member of the group to which
142    * this action belongs. 
143    *
144    * Since: 2.10
145    */
146   g_object_class_install_property (gobject_class,
147                                    PROP_CURRENT_VALUE,
148                                    g_param_spec_int ("current-value",
149                                                      P_("The current value"),
150                                                      P_("The value property of the currently active member of the group to which this action belongs."),
151                                                      G_MININT,
152                                                      G_MAXINT,
153                                                      0,
154                                                      GTK_PARAM_READWRITE));
155
156   /**
157    * GtkRadioAction::changed:
158    * @action: the action on which the signal is emitted
159    * @current: the member of @action<!-- -->s group which has just been activated
160    *
161    * The ::changed signal is emitted on every member of a radio group when the
162    * active member is changed. The signal gets emitted after the ::activate signals
163    * for the previous and current active members.
164    *
165    * Since: 2.4
166    */
167   radio_action_signals[CHANGED] =
168     g_signal_new (I_("changed"),
169                   G_OBJECT_CLASS_TYPE (klass),
170                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
171                   G_STRUCT_OFFSET (GtkRadioActionClass, changed),  NULL, NULL,
172                   g_cclosure_marshal_VOID__OBJECT,
173                   G_TYPE_NONE, 1, GTK_TYPE_RADIO_ACTION);
174
175   g_type_class_add_private (gobject_class, sizeof (GtkRadioActionPrivate));
176 }
177
178 static void
179 gtk_radio_action_init (GtkRadioAction *action)
180 {
181   action->private_data = G_TYPE_INSTANCE_GET_PRIVATE (action,
182                                                       GTK_TYPE_RADIO_ACTION,
183                                                       GtkRadioActionPrivate);
184
185   action->private_data->group = g_slist_prepend (NULL, action);
186   action->private_data->value = 0;
187
188   gtk_toggle_action_set_draw_as_radio (GTK_TOGGLE_ACTION (action), TRUE);
189 }
190
191 /**
192  * gtk_radio_action_new:
193  * @name: A unique name for the action
194  * @label: (allow-none): The label displayed in menu items and on buttons,
195  *   or %NULL
196  * @tooltip: (allow-none): A tooltip for this action, or %NULL
197  * @stock_id: (allow-none): The stock icon to display in widgets representing
198  *   this action, or %NULL
199  * @value: The value which gtk_radio_action_get_current_value() should
200  *   return if this action is selected.
201  *
202  * Creates a new #GtkRadioAction object. To add the action to
203  * a #GtkActionGroup and set the accelerator for the action,
204  * call gtk_action_group_add_action_with_accel().
205  *
206  * Return value: a new #GtkRadioAction
207  *
208  * Since: 2.4
209  */
210 GtkRadioAction *
211 gtk_radio_action_new (const gchar *name,
212                       const gchar *label,
213                       const gchar *tooltip,
214                       const gchar *stock_id,
215                       gint value)
216 {
217   g_return_val_if_fail (name != NULL, NULL);
218
219   return g_object_new (GTK_TYPE_RADIO_ACTION,
220                        "name", name,
221                        "label", label,
222                        "tooltip", tooltip,
223                        "stock-id", stock_id,
224                        "value", value,
225                        NULL);
226 }
227
228 static void
229 gtk_radio_action_finalize (GObject *object)
230 {
231   GtkRadioAction *action;
232   GSList *tmp_list;
233
234   action = GTK_RADIO_ACTION (object);
235
236   action->private_data->group = g_slist_remove (action->private_data->group, action);
237
238   tmp_list = action->private_data->group;
239
240   while (tmp_list)
241     {
242       GtkRadioAction *tmp_action = tmp_list->data;
243
244       tmp_list = tmp_list->next;
245       tmp_action->private_data->group = action->private_data->group;
246     }
247
248   G_OBJECT_CLASS (gtk_radio_action_parent_class)->finalize (object);
249 }
250
251 static void
252 gtk_radio_action_set_property (GObject         *object,
253                                guint            prop_id,
254                                const GValue    *value,
255                                GParamSpec      *pspec)
256 {
257   GtkRadioAction *radio_action;
258   
259   radio_action = GTK_RADIO_ACTION (object);
260
261   switch (prop_id)
262     {
263     case PROP_VALUE:
264       radio_action->private_data->value = g_value_get_int (value);
265       break;
266     case PROP_GROUP: 
267       {
268         GtkRadioAction *arg;
269         GSList *slist = NULL;
270         
271         if (G_VALUE_HOLDS_OBJECT (value)) 
272           {
273             arg = GTK_RADIO_ACTION (g_value_get_object (value));
274             if (arg)
275               slist = gtk_radio_action_get_group (arg);
276             gtk_radio_action_set_group (radio_action, slist);
277           }
278       }
279       break;
280     case PROP_CURRENT_VALUE:
281       gtk_radio_action_set_current_value (radio_action,
282                                           g_value_get_int (value));
283       break;
284     default:
285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286       break;
287     }
288 }
289
290 static void
291 gtk_radio_action_get_property (GObject    *object,
292                                guint       prop_id,
293                                GValue     *value,
294                                GParamSpec *pspec)
295 {
296   GtkRadioAction *radio_action;
297
298   radio_action = GTK_RADIO_ACTION (object);
299
300   switch (prop_id)
301     {
302     case PROP_VALUE:
303       g_value_set_int (value, radio_action->private_data->value);
304       break;
305     case PROP_CURRENT_VALUE:
306       g_value_set_int (value,
307                        gtk_radio_action_get_current_value (radio_action));
308       break;
309     default:
310       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
311       break;
312     }
313 }
314
315 static void
316 gtk_radio_action_activate (GtkAction *action)
317 {
318   GtkRadioAction *radio_action;
319   GtkToggleAction *toggle_action;
320   GtkToggleAction *tmp_action;
321   GSList *tmp_list;
322   gboolean active;
323
324   radio_action = GTK_RADIO_ACTION (action);
325   toggle_action = GTK_TOGGLE_ACTION (action);
326
327   active = gtk_toggle_action_get_active (toggle_action);
328   if (active)
329     {
330       tmp_list = radio_action->private_data->group;
331
332       while (tmp_list)
333         {
334           tmp_action = tmp_list->data;
335           tmp_list = tmp_list->next;
336
337           if (gtk_toggle_action_get_active (tmp_action) &&
338               (tmp_action != toggle_action))
339             {
340               _gtk_toggle_action_set_active (toggle_action, !active);
341
342               break;
343             }
344         }
345       g_object_notify (G_OBJECT (action), "active");
346     }
347   else
348     {
349       _gtk_toggle_action_set_active (toggle_action, !active);
350       g_object_notify (G_OBJECT (action), "active");
351
352       tmp_list = radio_action->private_data->group;
353       while (tmp_list)
354         {
355           tmp_action = tmp_list->data;
356           tmp_list = tmp_list->next;
357
358           if (gtk_toggle_action_get_active (tmp_action) &&
359               (tmp_action != toggle_action))
360             {
361               _gtk_action_emit_activate (GTK_ACTION (tmp_action));
362               break;
363             }
364         }
365
366       tmp_list = radio_action->private_data->group;
367       while (tmp_list)
368         {
369           tmp_action = tmp_list->data;
370           tmp_list = tmp_list->next;
371           
372           g_object_notify (G_OBJECT (tmp_action), "current-value");
373
374           g_signal_emit (tmp_action, radio_action_signals[CHANGED], 0, radio_action);
375         }
376     }
377
378   gtk_toggle_action_toggled (toggle_action);
379 }
380
381 static GtkWidget *
382 create_menu_item (GtkAction *action)
383 {
384   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, 
385                        "draw-as-radio", TRUE,
386                        NULL);
387 }
388
389 /**
390  * gtk_radio_action_get_group:
391  * @action: the action object
392  *
393  * Returns the list representing the radio group for this object.
394  * Note that the returned list is only valid until the next change
395  * to the group. 
396  *
397  * A common way to set up a group of radio group is the following:
398  * |[
399  *   GSList *group = NULL;
400  *   GtkRadioAction *action;
401  *  
402  *   while (/&ast; more actions to add &ast;/)
403  *     {
404  *        action = gtk_radio_action_new (...);
405  *        
406  *        gtk_radio_action_set_group (action, group);
407  *        group = gtk_radio_action_get_group (action);
408  *     }
409  * ]|
410  *
411  * Returns:  (element-type GtkRadioAction) (transfer none): the list representing the radio group for this object
412  *
413  * Since: 2.4
414  */
415 GSList *
416 gtk_radio_action_get_group (GtkRadioAction *action)
417 {
418   g_return_val_if_fail (GTK_IS_RADIO_ACTION (action), NULL);
419
420   return action->private_data->group;
421 }
422
423 /**
424  * gtk_radio_action_set_group:
425  * @action: the action object
426  * @group: (element-type GtkRadioAction): a list representing a radio group
427  *
428  * Sets the radio group for the radio action object.
429  *
430  * Since: 2.4
431  */
432 void
433 gtk_radio_action_set_group (GtkRadioAction *action, 
434                             GSList         *group)
435 {
436   g_return_if_fail (GTK_IS_RADIO_ACTION (action));
437   g_return_if_fail (!g_slist_find (group, action));
438
439   if (action->private_data->group)
440     {
441       GSList *slist;
442
443       action->private_data->group = g_slist_remove (action->private_data->group, action);
444
445       for (slist = action->private_data->group; slist; slist = slist->next)
446         {
447           GtkRadioAction *tmp_action = slist->data;
448
449           tmp_action->private_data->group = action->private_data->group;
450         }
451     }
452
453   action->private_data->group = g_slist_prepend (group, action);
454
455   if (group)
456     {
457       GSList *slist;
458
459       for (slist = action->private_data->group; slist; slist = slist->next)
460         {
461           GtkRadioAction *tmp_action = slist->data;
462
463           tmp_action->private_data->group = action->private_data->group;
464         }
465     }
466   else
467     {
468       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
469     }
470 }
471
472 /**
473  * gtk_radio_action_join_group:
474  * @action: the action object
475  * @group_source: (allow-none): a radio action object whos group we are 
476  *   joining, or %NULL to remove the radio action from its group
477  *
478  * Joins a radio action object to the group of another radio action object.
479  *
480  * Use this in language bindings instead of the gtk_radio_action_get_group() 
481  * and gtk_radio_action_set_group() methods
482  *
483  * A common way to set up a group of radio actions is the following:
484  * |[
485  *   GtkRadioAction *action;
486  *   GtkRadioAction *last_action;
487  *  
488  *   while (/&ast; more actions to add &ast;/)
489  *     {
490  *        action = gtk_radio_action_new (...);
491  *        
492  *        gtk_radio_action_join_group (action, last_action);
493  *        last_action = action;
494  *     }
495  * ]|
496  * 
497  * Since: 3.0
498  */
499 void
500 gtk_radio_action_join_group (GtkRadioAction *action, 
501                              GtkRadioAction *group_source)
502 {
503   g_return_if_fail (GTK_IS_RADIO_ACTION (action));
504   g_return_if_fail (group_source == NULL || GTK_IS_RADIO_ACTION (group_source));  
505
506   if (group_source)
507     {
508       GSList *group;
509       group = gtk_radio_action_get_group (group_source);
510       
511       if (!group)
512         {
513           /* if we are not already part of a group we need to set up a new one
514              and then get the newly created group */  
515           gtk_radio_action_set_group (group_source, NULL);
516           group = gtk_radio_action_get_group (group_source);
517         }
518
519       gtk_radio_action_set_group (action, group);
520     }
521   else
522     {
523       gtk_radio_action_set_group (action, NULL);
524     }
525 }
526
527 /**
528  * gtk_radio_action_get_current_value:
529  * @action: a #GtkRadioAction
530  * 
531  * Obtains the value property of the currently active member of 
532  * the group to which @action belongs.
533  * 
534  * Return value: The value of the currently active group member
535  *
536  * Since: 2.4
537  **/
538 gint
539 gtk_radio_action_get_current_value (GtkRadioAction *action)
540 {
541   GSList *slist;
542
543   g_return_val_if_fail (GTK_IS_RADIO_ACTION (action), 0);
544
545   if (action->private_data->group)
546     {
547       for (slist = action->private_data->group; slist; slist = slist->next)
548         {
549           GtkToggleAction *toggle_action = slist->data;
550
551           if (gtk_toggle_action_get_active (toggle_action))
552             return GTK_RADIO_ACTION (toggle_action)->private_data->value;
553         }
554     }
555
556   return action->private_data->value;
557 }
558
559 /**
560  * gtk_radio_action_set_current_value:
561  * @action: a #GtkRadioAction
562  * @current_value: the new value
563  * 
564  * Sets the currently active group member to the member with value
565  * property @current_value.
566  *
567  * Since: 2.10
568  **/
569 void
570 gtk_radio_action_set_current_value (GtkRadioAction *action,
571                                     gint            current_value)
572 {
573   GSList *slist;
574
575   g_return_if_fail (GTK_IS_RADIO_ACTION (action));
576
577   if (action->private_data->group)
578     {
579       for (slist = action->private_data->group; slist; slist = slist->next)
580         {
581           GtkRadioAction *radio_action = slist->data;
582
583           if (radio_action->private_data->value == current_value)
584             {
585               gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (radio_action),
586                                             TRUE);
587               return;
588             }
589         }
590     }
591
592   if (action->private_data->value == current_value)
593     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
594   else
595     g_warning ("Radio group does not contain an action with value '%d'",
596                current_value);
597 }