]> Pileus Git - ~andy/gtk/blob - gtk/gtkradioaction.c
815dc4f2549289b7f53f29778f7ce2cdbfdb213e
[~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 };
61
62 static void gtk_radio_action_init         (GtkRadioAction *action);
63 static void gtk_radio_action_class_init   (GtkRadioActionClass *class);
64 static void gtk_radio_action_finalize     (GObject *object);
65 static void gtk_radio_action_set_property (GObject         *object,
66                                            guint            prop_id,
67                                            const GValue    *value,
68                                            GParamSpec      *pspec);
69 static void gtk_radio_action_get_property (GObject         *object,
70                                            guint            prop_id,
71                                            GValue          *value,
72                                            GParamSpec      *pspec);
73 static void gtk_radio_action_activate     (GtkAction *action);
74 static GtkWidget *create_menu_item        (GtkAction *action);
75
76
77 GType
78 gtk_radio_action_get_type (void)
79 {
80   static GtkType type = 0;
81
82   if (!type)
83     {
84       static const GTypeInfo type_info =
85       {
86         sizeof (GtkRadioActionClass),
87         (GBaseInitFunc) NULL,
88         (GBaseFinalizeFunc) NULL,
89         (GClassInitFunc) gtk_radio_action_class_init,
90         (GClassFinalizeFunc) NULL,
91         NULL,
92         
93         sizeof (GtkRadioAction),
94         0, /* n_preallocs */
95         (GInstanceInitFunc) gtk_radio_action_init,
96       };
97
98       type = g_type_register_static (GTK_TYPE_TOGGLE_ACTION,
99                                      "GtkRadioAction",
100                                      &type_info, 0);
101     }
102   return type;
103 }
104
105 static GObjectClass *parent_class = NULL;
106 static guint         radio_action_signals[LAST_SIGNAL] = { 0 };
107
108 static void
109 gtk_radio_action_class_init (GtkRadioActionClass *klass)
110 {
111   GObjectClass *gobject_class;
112   GtkActionClass *action_class;
113
114   parent_class = g_type_class_peek_parent (klass);
115   gobject_class = G_OBJECT_CLASS (klass);
116   action_class = GTK_ACTION_CLASS (klass);
117
118   gobject_class->finalize = gtk_radio_action_finalize;
119   gobject_class->set_property = gtk_radio_action_set_property;
120   gobject_class->get_property = gtk_radio_action_get_property;
121
122   action_class->activate = gtk_radio_action_activate;
123
124   action_class->create_menu_item = create_menu_item;
125
126   /**
127    * GtkRadioAction:value:
128    *
129    * The value is an arbitrary integer which can be used as a
130    * convenient way to determine which action in the group is 
131    * currently active in an ::activate or ::changed signal handler.
132    * See gtk_radio_action_get_current_value() and #GtkRadioActionEntry
133    * for convenient ways to get and set this property.
134    *
135    * Since: 2.4
136    */
137   g_object_class_install_property (gobject_class,
138                                    PROP_VALUE,
139                                    g_param_spec_int ("value",
140                                                      P_("The value"),
141                                                      P_("The value returned by gtk_radio_action_get_current_value() when this action is the current action of its group."),
142                                                      G_MININT,
143                                                      G_MAXINT,
144                                                      0,
145                                                      GTK_PARAM_READWRITE));
146
147   /**
148    * GtkRadioAction:group:
149    *
150    * Sets a new group for a radio action.
151    *
152    * Since: 2.4
153    */
154   g_object_class_install_property (gobject_class,
155                                    PROP_GROUP,
156                                    g_param_spec_object ("group",
157                                                         P_("Group"),
158                                                         P_("The radio action whose group this action belongs to."),
159                                                         GTK_TYPE_RADIO_ACTION,
160                                                         GTK_PARAM_WRITABLE));
161
162   /**
163    * GtkRadioAction::changed:
164    * @action: the action on which the signal is emitted
165    * @current: the member of @action<!-- -->s group which has just been activated
166    *
167    * The ::changed signal is emitted on every member of a radio group when the
168    * active member is changed. The signal gets emitted after the ::activate signals
169    * for the previous and current active members.
170    *
171    * Since: 2.4
172    */
173   radio_action_signals[CHANGED] =
174     g_signal_new ("changed",
175                   G_OBJECT_CLASS_TYPE (klass),
176                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
177                   G_STRUCT_OFFSET (GtkRadioActionClass, changed),  NULL, NULL,
178                   g_cclosure_marshal_VOID__OBJECT,
179                   G_TYPE_NONE, 1, GTK_TYPE_RADIO_ACTION);
180
181   g_type_class_add_private (gobject_class, sizeof (GtkRadioActionPrivate));
182 }
183
184 static void
185 gtk_radio_action_init (GtkRadioAction *action)
186 {
187   action->private_data = GTK_RADIO_ACTION_GET_PRIVATE (action);
188   action->private_data->group = g_slist_prepend (NULL, action);
189   action->private_data->value = 0;
190 }
191
192 /**
193  * gtk_radio_action_new:
194  * @name: A unique name for the action
195  * @label: The label displayed in menu items and on buttons
196  * @tooltip: A tooltip for this action
197  * @stock_id: The stock icon to display in widgets representing this action
198  * @value: The value which gtk_radio_action_get_current_value() should return
199  *    if this action is selected.
200  *
201  * Creates a new #GtkRadioAction object. To add the action to
202  * a #GtkActionGroup and set the accelerator for the action,
203  * call gtk_action_group_add_action_with_accel().
204  *
205  * Return value: a new #GtkRadioAction
206  *
207  * Since: 2.4
208  */
209 GtkRadioAction *
210 gtk_radio_action_new (const gchar *name,
211                       const gchar *label,
212                       const gchar *tooltip,
213                       const gchar *stock_id,
214                       gint value)
215 {
216   GtkRadioAction *action;
217
218   action = g_object_new (GTK_TYPE_RADIO_ACTION,
219                          "name", name,
220                          "label", label,
221                          "tooltip", tooltip,
222                          "stock_id", stock_id,
223                          "value", value,
224                          NULL);
225
226   return action;
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   g_print ("finalize %p\n", action);
238
239   action->private_data->group = g_slist_remove (action->private_data->group, action);
240
241   tmp_list = action->private_data->group;
242
243   while (tmp_list)
244     {
245       GtkRadioAction *tmp_action = tmp_list->data;
246
247       tmp_list = tmp_list->next;
248       tmp_action->private_data->group = action->private_data->group;
249     }
250
251   (* parent_class->finalize) (object);
252 }
253
254 static void
255 gtk_radio_action_set_property (GObject         *object,
256                                guint            prop_id,
257                                const GValue    *value,
258                                GParamSpec      *pspec)
259 {
260   GtkRadioAction *radio_action;
261   
262   radio_action = GTK_RADIO_ACTION (object);
263
264   switch (prop_id)
265     {
266     case PROP_VALUE:
267       radio_action->private_data->value = g_value_get_int (value);
268       break;
269     case PROP_GROUP: 
270       {
271         GtkRadioAction *arg;
272         GSList *slist = NULL;
273         
274         if (G_VALUE_HOLDS_OBJECT (value)) 
275           {
276             arg = GTK_RADIO_ACTION (g_value_get_object (value));
277             if (arg)
278               slist = gtk_radio_action_get_group (arg);
279             gtk_radio_action_set_group (radio_action, slist);
280           }
281       }
282       break;
283     default:
284       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
285       break;
286     }
287 }
288
289 static void
290 gtk_radio_action_get_property (GObject    *object,
291                                guint       prop_id,
292                                GValue     *value,
293                                GParamSpec *pspec)
294 {
295   GtkRadioAction *radio_action;
296
297   radio_action = GTK_RADIO_ACTION (object);
298
299   switch (prop_id)
300     {
301     case PROP_VALUE:
302       g_value_set_int (value, radio_action->private_data->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_activate (GtkAction *action)
312 {
313   GtkRadioAction *radio_action;
314   GtkToggleAction *toggle_action;
315   GtkToggleAction *tmp_action;
316   GSList *tmp_list;
317
318   radio_action = GTK_RADIO_ACTION (action);
319   toggle_action = GTK_TOGGLE_ACTION (action);
320
321   if (toggle_action->private_data->active)
322     {
323       tmp_list = radio_action->private_data->group;
324
325       while (tmp_list)
326         {
327           tmp_action = tmp_list->data;
328           tmp_list = tmp_list->next;
329
330           if (tmp_action->private_data->active && (tmp_action != toggle_action)) 
331             {
332               toggle_action->private_data->active = !toggle_action->private_data->active;
333               break;
334             }
335         }
336     }
337   else
338     {
339       toggle_action->private_data->active = !toggle_action->private_data->active;
340
341       tmp_list = radio_action->private_data->group;
342       while (tmp_list)
343         {
344           tmp_action = tmp_list->data;
345           tmp_list = tmp_list->next;
346
347           if (tmp_action->private_data->active && (tmp_action != toggle_action))
348             {
349               _gtk_action_emit_activate (GTK_ACTION (tmp_action));
350               break;
351             }
352         }
353
354       tmp_list = radio_action->private_data->group;
355       while (tmp_list)
356         {
357           tmp_action = tmp_list->data;
358           tmp_list = tmp_list->next;
359           
360           g_signal_emit (tmp_action, radio_action_signals[CHANGED], 0, radio_action);
361         }
362     }
363
364   gtk_toggle_action_toggled (toggle_action);
365 }
366
367 static GtkWidget *
368 create_menu_item (GtkAction *action)
369 {
370   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, 
371                        "draw_as_radio", TRUE,
372                        NULL);
373 }
374
375 /**
376  * gtk_radio_action_get_group:
377  * @action: the action object
378  *
379  * Returns the list representing the radio group for this object.
380  * Note that the returned list is only valid until the next change
381  * to the group. 
382  *
383  * A common way to set up a group of radio group is the following:
384  * <informalexample><programlisting>
385  *   GSList *group = NULL;
386  *   GtkRadioAction *action;
387  *  
388  *   while (/<!-- -->* more actions to add *<!-- -->/)
389  *     {
390  *        action = gtk_radio_action_new (...);
391  *
392  *        gtk_radio_action_set_group (action, group);
393  *        group = gtk_radio_action_get_group (action);
394  *     }
395  * </programlisting></informalexample>
396  *
397  * Returns: the list representing the radio group for this object
398  *
399  * Since: 2.4
400  */
401 GSList *
402 gtk_radio_action_get_group (GtkRadioAction *action)
403 {
404   g_return_val_if_fail (GTK_IS_RADIO_ACTION (action), NULL);
405
406   return action->private_data->group;
407 }
408
409 /**
410  * gtk_radio_action_set_group:
411  * @action: the action object
412  * @group: a list representing a radio group
413  *
414  * Sets the radio group for the radio action object.
415  *
416  * Since: 2.4
417  */
418 void
419 gtk_radio_action_set_group (GtkRadioAction *action, 
420                             GSList         *group)
421 {
422   g_return_if_fail (GTK_IS_RADIO_ACTION (action));
423   g_return_if_fail (!g_slist_find (group, action));
424
425   if (action->private_data->group)
426     {
427       GSList *slist;
428
429       action->private_data->group = g_slist_remove (action->private_data->group, action);
430
431       for (slist = action->private_data->group; slist; slist = slist->next)
432         {
433           GtkRadioAction *tmp_action = slist->data;
434
435           tmp_action->private_data->group = action->private_data->group;
436         }
437     }
438
439   action->private_data->group = g_slist_prepend (group, action);
440
441   if (group)
442     {
443       GSList *slist;
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   else
453     {
454       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
455     }
456 }
457
458 /**
459  * gtk_radio_action_get_current_value:
460  * @action: a #GtkRadioAction
461  * 
462  * Obtains the value property of the currently active member of 
463  * the group to which @action belongs.
464  * 
465  * Return value: The value of the currently active group member
466  *
467  * Since: 2.4
468  **/
469 gint
470 gtk_radio_action_get_current_value (GtkRadioAction *action)
471 {
472   GSList *slist;
473
474   g_return_val_if_fail (GTK_IS_RADIO_ACTION (action), 0);
475
476   if (action->private_data->group)
477     {
478       for (slist = action->private_data->group; slist; slist = slist->next)
479         {
480           GtkToggleAction *toggle_action = slist->data;
481
482           if (toggle_action->private_data->active)
483             return GTK_RADIO_ACTION (toggle_action)->private_data->value;
484         }
485     }
486
487   return action->private_data->value;
488 }
489
490 #define __GTK_RADIO_ACTION_C__
491 #include "gtkaliasdef.c"