]> Pileus Git - ~andy/gtk/blob - gtk/gtkradioaction.c
Add a current-value property and a setter for it. (#322735, Jorn Baayen)
[~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 the Gnome Library; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * Author: James Henstridge <james@daa.com.au>
24  *
25  * Modified by the GTK+ Team and others 2003.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
29  */
30
31 #include <config.h>
32
33 #include "gtkradioaction.h"
34 #include "gtkradiomenuitem.h"
35 #include "gtktoggleactionprivate.h"
36 #include "gtktoggletoolbutton.h"
37 #include "gtkintl.h"
38 #include "gtkprivate.h"
39 #include "gtkalias.h"
40
41 #define GTK_RADIO_ACTION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RADIO_ACTION, GtkRadioActionPrivate))
42
43 struct _GtkRadioActionPrivate 
44 {
45   GSList *group;
46   gint    value;
47 };
48
49 enum 
50 {
51   CHANGED,
52   LAST_SIGNAL
53 };
54
55 enum 
56 {
57   PROP_0,
58   PROP_VALUE,
59   PROP_GROUP,
60   PROP_CURRENT_VALUE
61 };
62
63 static void gtk_radio_action_init         (GtkRadioAction *action);
64 static void gtk_radio_action_class_init   (GtkRadioActionClass *class);
65 static void gtk_radio_action_finalize     (GObject *object);
66 static void gtk_radio_action_set_property (GObject         *object,
67                                            guint            prop_id,
68                                            const GValue    *value,
69                                            GParamSpec      *pspec);
70 static void gtk_radio_action_get_property (GObject         *object,
71                                            guint            prop_id,
72                                            GValue          *value,
73                                            GParamSpec      *pspec);
74 static void gtk_radio_action_activate     (GtkAction *action);
75 static GtkWidget *create_menu_item        (GtkAction *action);
76
77
78 GType
79 gtk_radio_action_get_type (void)
80 {
81   static GtkType type = 0;
82
83   if (!type)
84     {
85       static const GTypeInfo type_info =
86       {
87         sizeof (GtkRadioActionClass),
88         (GBaseInitFunc) NULL,
89         (GBaseFinalizeFunc) NULL,
90         (GClassInitFunc) gtk_radio_action_class_init,
91         (GClassFinalizeFunc) NULL,
92         NULL,
93         
94         sizeof (GtkRadioAction),
95         0, /* n_preallocs */
96         (GInstanceInitFunc) gtk_radio_action_init,
97       };
98
99       type = g_type_register_static (GTK_TYPE_TOGGLE_ACTION,
100                                      I_("GtkRadioAction"),
101                                      &type_info, 0);
102     }
103   return type;
104 }
105
106 static GObjectClass *parent_class = NULL;
107 static guint         radio_action_signals[LAST_SIGNAL] = { 0 };
108
109 static void
110 gtk_radio_action_class_init (GtkRadioActionClass *klass)
111 {
112   GObjectClass *gobject_class;
113   GtkActionClass *action_class;
114
115   parent_class = g_type_class_peek_parent (klass);
116   gobject_class = G_OBJECT_CLASS (klass);
117   action_class = GTK_ACTION_CLASS (klass);
118
119   gobject_class->finalize = gtk_radio_action_finalize;
120   gobject_class->set_property = gtk_radio_action_set_property;
121   gobject_class->get_property = gtk_radio_action_get_property;
122
123   action_class->activate = gtk_radio_action_activate;
124
125   action_class->create_menu_item = create_menu_item;
126
127   /**
128    * GtkRadioAction:value:
129    *
130    * The value is an arbitrary integer which can be used as a
131    * convenient way to determine which action in the group is 
132    * currently active in an ::activate or ::changed signal handler.
133    * See gtk_radio_action_get_current_value() and #GtkRadioActionEntry
134    * for convenient ways to get and set this property.
135    *
136    * Since: 2.4
137    */
138   g_object_class_install_property (gobject_class,
139                                    PROP_VALUE,
140                                    g_param_spec_int ("value",
141                                                      P_("The value"),
142                                                      P_("The value returned by gtk_radio_action_get_current_value() when this action is the current action of its group."),
143                                                      G_MININT,
144                                                      G_MAXINT,
145                                                      0,
146                                                      GTK_PARAM_READWRITE));
147
148   /**
149    * GtkRadioAction:group:
150    *
151    * Sets a new group for a radio action.
152    *
153    * Since: 2.4
154    */
155   g_object_class_install_property (gobject_class,
156                                    PROP_GROUP,
157                                    g_param_spec_object ("group",
158                                                         P_("Group"),
159                                                         P_("The radio action whose group this action belongs to."),
160                                                         GTK_TYPE_RADIO_ACTION,
161                                                         GTK_PARAM_WRITABLE));
162
163   /**
164    * GtkRadioAction:current-value:
165    *
166    * The value property of the currently active member of the group to which
167    * this action belongs. 
168    *
169    * Since: 2.10
170    */
171   g_object_class_install_property (gobject_class,
172                                    PROP_CURRENT_VALUE,
173                                    g_param_spec_int ("current-value",
174                                                      P_("The current value"),
175                                                      P_("The value property of the currently active member of the group to which this action belongs."),
176                                                      G_MININT,
177                                                      G_MAXINT,
178                                                      0,
179                                                      GTK_PARAM_READWRITE));
180
181   /**
182    * GtkRadioAction::changed:
183    * @action: the action on which the signal is emitted
184    * @current: the member of @action<!-- -->s group which has just been activated
185    *
186    * The ::changed signal is emitted on every member of a radio group when the
187    * active member is changed. The signal gets emitted after the ::activate signals
188    * for the previous and current active members.
189    *
190    * Since: 2.4
191    */
192   radio_action_signals[CHANGED] =
193     g_signal_new (I_("changed"),
194                   G_OBJECT_CLASS_TYPE (klass),
195                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
196                   G_STRUCT_OFFSET (GtkRadioActionClass, changed),  NULL, NULL,
197                   g_cclosure_marshal_VOID__OBJECT,
198                   G_TYPE_NONE, 1, GTK_TYPE_RADIO_ACTION);
199
200   g_type_class_add_private (gobject_class, sizeof (GtkRadioActionPrivate));
201 }
202
203 static void
204 gtk_radio_action_init (GtkRadioAction *action)
205 {
206   action->private_data = GTK_RADIO_ACTION_GET_PRIVATE (action);
207   action->private_data->group = g_slist_prepend (NULL, action);
208   action->private_data->value = 0;
209 }
210
211 /**
212  * gtk_radio_action_new:
213  * @name: A unique name for the action
214  * @label: The label displayed in menu items and on buttons
215  * @tooltip: A tooltip for this action
216  * @stock_id: The stock icon to display in widgets representing this action
217  * @value: The value which gtk_radio_action_get_current_value() should return
218  *    if this action is selected.
219  *
220  * Creates a new #GtkRadioAction object. To add the action to
221  * a #GtkActionGroup and set the accelerator for the action,
222  * call gtk_action_group_add_action_with_accel().
223  *
224  * Return value: a new #GtkRadioAction
225  *
226  * Since: 2.4
227  */
228 GtkRadioAction *
229 gtk_radio_action_new (const gchar *name,
230                       const gchar *label,
231                       const gchar *tooltip,
232                       const gchar *stock_id,
233                       gint value)
234 {
235   GtkRadioAction *action;
236
237   action = g_object_new (GTK_TYPE_RADIO_ACTION,
238                          "name", name,
239                          "label", label,
240                          "tooltip", tooltip,
241                          "stock-id", stock_id,
242                          "value", value,
243                          NULL);
244
245   return action;
246 }
247
248 static void
249 gtk_radio_action_finalize (GObject *object)
250 {
251   GtkRadioAction *action;
252   GSList *tmp_list;
253
254   action = GTK_RADIO_ACTION (object);
255
256   action->private_data->group = g_slist_remove (action->private_data->group, action);
257
258   tmp_list = action->private_data->group;
259
260   while (tmp_list)
261     {
262       GtkRadioAction *tmp_action = tmp_list->data;
263
264       tmp_list = tmp_list->next;
265       tmp_action->private_data->group = action->private_data->group;
266     }
267
268   (* parent_class->finalize) (object);
269 }
270
271 static void
272 gtk_radio_action_set_property (GObject         *object,
273                                guint            prop_id,
274                                const GValue    *value,
275                                GParamSpec      *pspec)
276 {
277   GtkRadioAction *radio_action;
278   
279   radio_action = GTK_RADIO_ACTION (object);
280
281   switch (prop_id)
282     {
283     case PROP_VALUE:
284       radio_action->private_data->value = g_value_get_int (value);
285       break;
286     case PROP_GROUP: 
287       {
288         GtkRadioAction *arg;
289         GSList *slist = NULL;
290         
291         if (G_VALUE_HOLDS_OBJECT (value)) 
292           {
293             arg = GTK_RADIO_ACTION (g_value_get_object (value));
294             if (arg)
295               slist = gtk_radio_action_get_group (arg);
296             gtk_radio_action_set_group (radio_action, slist);
297           }
298       }
299       break;
300     case PROP_CURRENT_VALUE:
301       gtk_radio_action_set_current_value (radio_action,
302                                           g_value_get_int (value));
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
306       break;
307     }
308 }
309
310 static void
311 gtk_radio_action_get_property (GObject    *object,
312                                guint       prop_id,
313                                GValue     *value,
314                                GParamSpec *pspec)
315 {
316   GtkRadioAction *radio_action;
317
318   radio_action = GTK_RADIO_ACTION (object);
319
320   switch (prop_id)
321     {
322     case PROP_VALUE:
323       g_value_set_int (value, radio_action->private_data->value);
324       break;
325     case PROP_CURRENT_VALUE:
326       g_value_set_int (value,
327                        gtk_radio_action_get_current_value (radio_action));
328       break;
329     default:
330       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
331       break;
332     }
333 }
334
335 static void
336 gtk_radio_action_activate (GtkAction *action)
337 {
338   GtkRadioAction *radio_action;
339   GtkToggleAction *toggle_action;
340   GtkToggleAction *tmp_action;
341   GSList *tmp_list;
342
343   radio_action = GTK_RADIO_ACTION (action);
344   toggle_action = GTK_TOGGLE_ACTION (action);
345
346   if (toggle_action->private_data->active)
347     {
348       tmp_list = radio_action->private_data->group;
349
350       while (tmp_list)
351         {
352           tmp_action = tmp_list->data;
353           tmp_list = tmp_list->next;
354
355           if (tmp_action->private_data->active && (tmp_action != toggle_action)) 
356             {
357               toggle_action->private_data->active = !toggle_action->private_data->active;
358               break;
359             }
360         }
361     }
362   else
363     {
364       toggle_action->private_data->active = !toggle_action->private_data->active;
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           if (tmp_action->private_data->active && (tmp_action != toggle_action))
373             {
374               _gtk_action_emit_activate (GTK_ACTION (tmp_action));
375               break;
376             }
377         }
378
379       tmp_list = radio_action->private_data->group;
380       while (tmp_list)
381         {
382           tmp_action = tmp_list->data;
383           tmp_list = tmp_list->next;
384           
385           g_object_notify (G_OBJECT (tmp_action), "current-value");
386
387           g_signal_emit (tmp_action, radio_action_signals[CHANGED], 0, radio_action);
388         }
389     }
390
391   gtk_toggle_action_toggled (toggle_action);
392 }
393
394 static GtkWidget *
395 create_menu_item (GtkAction *action)
396 {
397   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, 
398                        "draw-as-radio", TRUE,
399                        NULL);
400 }
401
402 /**
403  * gtk_radio_action_get_group:
404  * @action: the action object
405  *
406  * Returns the list representing the radio group for this object.
407  * Note that the returned list is only valid until the next change
408  * to the group. 
409  *
410  * A common way to set up a group of radio group is the following:
411  * <informalexample><programlisting>
412  *   GSList *group = NULL;
413  *   GtkRadioAction *action;
414  *  
415  *   while (/<!-- -->* more actions to add *<!-- -->/)
416  *     {
417  *        action = gtk_radio_action_new (...);
418  *        
419  *        gtk_radio_action_set_group (action, group);
420  *        group = gtk_radio_action_get_group (action);
421  *     }
422  * </programlisting></informalexample>
423  *
424  * Returns: the list representing the radio group for this object
425  *
426  * Since: 2.4
427  */
428 GSList *
429 gtk_radio_action_get_group (GtkRadioAction *action)
430 {
431   g_return_val_if_fail (GTK_IS_RADIO_ACTION (action), NULL);
432
433   return action->private_data->group;
434 }
435
436 /**
437  * gtk_radio_action_set_group:
438  * @action: the action object
439  * @group: a list representing a radio group
440  *
441  * Sets the radio group for the radio action object.
442  *
443  * Since: 2.4
444  */
445 void
446 gtk_radio_action_set_group (GtkRadioAction *action, 
447                             GSList         *group)
448 {
449   g_return_if_fail (GTK_IS_RADIO_ACTION (action));
450   g_return_if_fail (!g_slist_find (group, action));
451
452   if (action->private_data->group)
453     {
454       GSList *slist;
455
456       action->private_data->group = g_slist_remove (action->private_data->group, action);
457
458       for (slist = action->private_data->group; slist; slist = slist->next)
459         {
460           GtkRadioAction *tmp_action = slist->data;
461
462           tmp_action->private_data->group = action->private_data->group;
463         }
464     }
465
466   action->private_data->group = g_slist_prepend (group, action);
467
468   if (group)
469     {
470       GSList *slist;
471
472       for (slist = action->private_data->group; slist; slist = slist->next)
473         {
474           GtkRadioAction *tmp_action = slist->data;
475
476           tmp_action->private_data->group = action->private_data->group;
477         }
478     }
479   else
480     {
481       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
482     }
483 }
484
485 /**
486  * gtk_radio_action_get_current_value:
487  * @action: a #GtkRadioAction
488  * 
489  * Obtains the value property of the currently active member of 
490  * the group to which @action belongs.
491  * 
492  * Return value: The value of the currently active group member
493  *
494  * Since: 2.4
495  **/
496 gint
497 gtk_radio_action_get_current_value (GtkRadioAction *action)
498 {
499   GSList *slist;
500
501   g_return_val_if_fail (GTK_IS_RADIO_ACTION (action), 0);
502
503   if (action->private_data->group)
504     {
505       for (slist = action->private_data->group; slist; slist = slist->next)
506         {
507           GtkToggleAction *toggle_action = slist->data;
508
509           if (toggle_action->private_data->active)
510             return GTK_RADIO_ACTION (toggle_action)->private_data->value;
511         }
512     }
513
514   return action->private_data->value;
515 }
516
517 /**
518  * gtk_radio_action_set_current_value:
519  * @action: a #GtkRadioAction
520  * @current_value: the new value
521  * 
522  * Sets the currently active group member to the member with value
523  * property @current_value.
524  *
525  * Since: 2.10
526  **/
527 void
528 gtk_radio_action_set_current_value (GtkRadioAction *action,
529                                     gint            current_value)
530 {
531   GSList *slist;
532
533   g_return_if_fail (GTK_IS_RADIO_ACTION (action));
534
535   if (action->private_data->group)
536     {
537       for (slist = action->private_data->group; slist; slist = slist->next)
538         {
539           GtkRadioAction *radio_action = slist->data;
540
541           if (radio_action->private_data->value == current_value)
542             {
543               gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (radio_action),
544                                             TRUE);
545               return;
546             }
547         }
548     }
549
550   if (action->private_data->value == current_value)
551     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
552   else
553     g_warning ("Radio group does not contain an action with value '%d'",
554                current_value);
555 }
556
557 #define __GTK_RADIO_ACTION_C__
558 #include "gtkaliasdef.c"