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