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