]> Pileus Git - ~andy/gtk/blob - gtk/gtkradiomenuitem.c
Add a ::group-changed signal emitted when the radio button/menu item is
[~andy/gtk] / gtk / gtkradiomenuitem.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtkaccellabel.h"
28 #include "gtkmarshalers.h"
29 #include "gtkradiomenuitem.h"
30
31
32 static void gtk_radio_menu_item_class_init     (GtkRadioMenuItemClass *klass);
33 static void gtk_radio_menu_item_init           (GtkRadioMenuItem      *radio_menu_item);
34 static void gtk_radio_menu_item_destroy        (GtkObject             *object);
35 static void gtk_radio_menu_item_activate       (GtkMenuItem           *menu_item);
36
37 static GtkCheckMenuItemClass *parent_class = NULL;
38
39 static guint group_changed_signal = 0;
40
41 GType
42 gtk_radio_menu_item_get_type (void)
43 {
44   static GType radio_menu_item_type = 0;
45
46   if (!radio_menu_item_type)
47     {
48       static const GTypeInfo radio_menu_item_info =
49       {
50         sizeof (GtkRadioMenuItemClass),
51         NULL,           /* base_init */
52         NULL,           /* base_finalize */
53         (GClassInitFunc) gtk_radio_menu_item_class_init,
54         NULL,           /* class_finalize */
55         NULL,           /* class_data */
56         sizeof (GtkRadioMenuItem),
57         0,              /* n_preallocs */
58         (GInstanceInitFunc) gtk_radio_menu_item_init,
59       };
60
61       radio_menu_item_type =
62         g_type_register_static (GTK_TYPE_CHECK_MENU_ITEM, "GtkRadioMenuItem",
63                                 &radio_menu_item_info, 0);
64     }
65
66   return radio_menu_item_type;
67 }
68
69 GtkWidget*
70 gtk_radio_menu_item_new (GSList *group)
71 {
72   GtkRadioMenuItem *radio_menu_item;
73
74   radio_menu_item = g_object_new (GTK_TYPE_RADIO_MENU_ITEM, NULL);
75
76   gtk_radio_menu_item_set_group (radio_menu_item, group);
77
78   return GTK_WIDGET (radio_menu_item);
79 }
80
81 void
82 gtk_radio_menu_item_set_group (GtkRadioMenuItem *radio_menu_item,
83                                GSList           *group)
84 {
85   GtkWidget *old_group_singleton = NULL;
86   GtkWidget *new_group_singleton = NULL;
87   
88   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item));
89   g_return_if_fail (!g_slist_find (group, radio_menu_item));
90
91   if (radio_menu_item->group)
92     {
93       GSList *slist;
94
95       radio_menu_item->group = g_slist_remove (radio_menu_item->group, radio_menu_item);
96       
97       if (radio_menu_item->group && !radio_menu_item->group->next)
98         old_group_singleton = g_object_ref (radio_menu_item->group->data);
99           
100       for (slist = radio_menu_item->group; slist; slist = slist->next)
101         {
102           GtkRadioMenuItem *tmp_item;
103           
104           tmp_item = slist->data;
105           
106           tmp_item->group = radio_menu_item->group;
107         }
108     }
109   
110   if (group && !group->next)
111     new_group_singleton = g_object_ref (group->data);
112   
113   radio_menu_item->group = g_slist_prepend (group, radio_menu_item);
114   
115   if (group)
116     {
117       GSList *slist;
118       
119       for (slist = group; slist; slist = slist->next)
120         {
121           GtkRadioMenuItem *tmp_item;
122           
123           tmp_item = slist->data;
124           
125           tmp_item->group = radio_menu_item->group;
126         }
127     }
128   else
129     {
130       GTK_CHECK_MENU_ITEM (radio_menu_item)->active = TRUE;
131       /* gtk_widget_set_state (GTK_WIDGET (radio_menu_item), GTK_STATE_ACTIVE);
132        */
133     }
134
135   g_object_ref (radio_menu_item);
136
137   g_signal_emit (radio_menu_item, group_changed_signal, 0);
138   if (old_group_singleton)
139     {
140       g_signal_emit (old_group_singleton, group_changed_signal, 0);
141       g_object_unref (old_group_singleton);
142     }
143   if (new_group_singleton)
144     {
145       g_signal_emit (new_group_singleton, group_changed_signal, 0);
146       g_object_unref (new_group_singleton);
147     }
148
149   g_object_unref (radio_menu_item);
150 }
151
152 GtkWidget*
153 gtk_radio_menu_item_new_with_label (GSList *group,
154                                     const gchar *label)
155 {
156   GtkWidget *radio_menu_item;
157   GtkWidget *accel_label;
158
159   radio_menu_item = gtk_radio_menu_item_new (group);
160   accel_label = gtk_accel_label_new (label);
161   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
162   gtk_container_add (GTK_CONTAINER (radio_menu_item), accel_label);
163   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), radio_menu_item);
164   gtk_widget_show (accel_label);
165
166   return radio_menu_item;
167 }
168
169
170 /**
171  * gtk_radio_menu_item_new_with_mnemonic:
172  * @group: group the radio menu item is inside
173  * @label: the text of the button, with an underscore in front of the
174  *         mnemonic character
175  * @returns: a new #GtkRadioMenuItem
176  *
177  * Creates a new #GtkRadioMenuItem containing a label. The label
178  * will be created using gtk_label_new_with_mnemonic(), so underscores
179  * in @label indicate the mnemonic for the menu item.
180  **/
181 GtkWidget*
182 gtk_radio_menu_item_new_with_mnemonic (GSList *group,
183                                        const gchar *label)
184 {
185   GtkWidget *radio_menu_item;
186   GtkWidget *accel_label;
187
188   radio_menu_item = gtk_radio_menu_item_new (group);
189   accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
190   gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), label);
191   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
192
193   gtk_container_add (GTK_CONTAINER (radio_menu_item), accel_label);
194   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), radio_menu_item);
195   gtk_widget_show (accel_label);
196
197   return radio_menu_item;
198 }
199
200 /**
201  * gtk_radio_menu_item_new_from_widget:
202  * @group: An existing #GtkRadioMenuItem
203  * 
204  * Creates a new #GtkRadioMenuItem adding it to the same group as @group.
205  * 
206  * Return value: The new #GtkRadioMenuItem
207  * 
208  * Since: 2.4
209  **/
210 GtkWidget *
211 gtk_radio_menu_item_new_from_widget (GtkRadioMenuItem *group)
212 {
213   GSList *list = NULL;
214   
215   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
216
217   if (group)
218     list = gtk_radio_menu_item_get_group (group);
219   
220   return gtk_radio_menu_item_new (list);
221 }
222
223 /**
224  * gtk_radio_menu_item_new_with_mnemonic_from_widget:
225  * @group: An existing #GtkRadioMenuItem
226  * @label: the text of the button, with an underscore in front of the
227  *         mnemonic character
228  *
229  * Creates a new GtkRadioMenuItem containing a label. The label will be
230  * created using gtk_label_new_with_mnemonic(), so underscores in label
231  * indicate the mnemonic for the menu item.
232  *
233  * The new #GtkRadioMenuItem is added to the same group as @group.
234  *
235  * Return value: The new #GtkRadioMenuItem
236  * 
237  * Since: 2.4
238  **/
239 GtkWidget *
240 gtk_radio_menu_item_new_with_mnemonic_from_widget (GtkRadioMenuItem *group,
241                                                    const gchar *label)
242 {
243   GSList *list = NULL;
244
245   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
246
247   if (group)
248     list = gtk_radio_menu_item_get_group (group);
249
250   return gtk_radio_menu_item_new_with_mnemonic (list, label);
251 }
252
253 /**
254  * gtk_radio_menu_item_new_with_label_from_widget:
255  * @group: an existing #GtkRadioMenuItem 
256  * @label: the text for the label
257  *
258  * Creates a new GtkRadioMenuItem whose child is a simple GtkLabel.
259  * The new #GtkRadioMenuItem is added to the same group as @group.
260  *
261  * Return value: The new #GtkRadioMenuItem
262  * 
263  * Since: 2.4
264  **/
265 GtkWidget *
266 gtk_radio_menu_item_new_with_label_from_widget (GtkRadioMenuItem *group,
267                                                 const gchar *label)
268 {
269   GSList *list = NULL;
270
271   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
272
273   if (group)
274     list = gtk_radio_menu_item_get_group (group);
275
276   return gtk_radio_menu_item_new_with_label (list, label);
277 }
278
279 GSList*
280 gtk_radio_menu_item_get_group (GtkRadioMenuItem *radio_menu_item)
281 {
282   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item), NULL);
283
284   return radio_menu_item->group;
285 }
286
287
288 static void
289 gtk_radio_menu_item_class_init (GtkRadioMenuItemClass *klass)
290 {
291   GtkObjectClass *object_class;
292   GtkMenuItemClass *menu_item_class;
293
294   object_class = (GtkObjectClass*) klass;
295   menu_item_class = (GtkMenuItemClass*) klass;
296
297   parent_class = g_type_class_peek_parent (klass);
298
299   object_class->destroy = gtk_radio_menu_item_destroy;
300
301   menu_item_class->activate = gtk_radio_menu_item_activate;
302
303   /**
304    * GtkStyle::group-changed:
305    * @style: the object which received the signal
306    *
307    * Emitted when the group of radio menu items that a radio menu item belongs
308    * to changes. This is emitted when a radio menu item switches from
309    * being alone to being part of a group of 2 or more menu items, or
310    * vice-versa, and when a buttton is moved from one group of 2 or
311    * more menu items to a different one, but not when the composition
312    * of the group that a menu item belongs to changes.
313    */
314   group_changed_signal = g_signal_new ("group-changed",
315                                        G_OBJECT_CLASS_TYPE (object_class),
316                                        G_SIGNAL_RUN_FIRST,
317                                        G_STRUCT_OFFSET (GtkRadioMenuItemClass, group_changed),
318                                        NULL, NULL,
319                                        _gtk_marshal_VOID__VOID,
320                                        G_TYPE_NONE, 0);
321 }
322
323 static void
324 gtk_radio_menu_item_init (GtkRadioMenuItem *radio_menu_item)
325 {
326   radio_menu_item->group = g_slist_prepend (NULL, radio_menu_item);
327   gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (radio_menu_item), TRUE);
328 }
329
330 static void
331 gtk_radio_menu_item_destroy (GtkObject *object)
332 {
333   GtkWidget *old_group_singleton = NULL;
334   GtkRadioMenuItem *radio_menu_item;
335   GtkRadioMenuItem *tmp_menu_item;
336   GSList *tmp_list;
337   gboolean was_in_group;
338
339   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (object));
340
341   radio_menu_item = GTK_RADIO_MENU_ITEM (object);
342
343   was_in_group = radio_menu_item->group && radio_menu_item->group->next;
344   
345   radio_menu_item->group = g_slist_remove (radio_menu_item->group,
346                                            radio_menu_item);
347   if (radio_menu_item->group && !radio_menu_item->group->next)
348     old_group_singleton = radio_menu_item->group->data;
349
350   tmp_list = radio_menu_item->group;
351
352   while (tmp_list)
353     {
354       tmp_menu_item = tmp_list->data;
355       tmp_list = tmp_list->next;
356
357       tmp_menu_item->group = radio_menu_item->group;
358     }
359
360   /* this radio menu item is no longer in the group */
361   radio_menu_item->group = NULL;
362   
363   if (old_group_singleton)
364     g_signal_emit (old_group_singleton, group_changed_signal, 0);
365   if (was_in_group)
366     g_signal_emit (radio_menu_item, group_changed_signal, 0);
367   
368   if (GTK_OBJECT_CLASS (parent_class)->destroy)
369     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
370 }
371
372 static void
373 gtk_radio_menu_item_activate (GtkMenuItem *menu_item)
374 {
375   GtkRadioMenuItem *radio_menu_item;
376   GtkCheckMenuItem *check_menu_item;
377   GtkCheckMenuItem *tmp_menu_item;
378   GSList *tmp_list;
379   gint toggled;
380
381   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (menu_item));
382
383   radio_menu_item = GTK_RADIO_MENU_ITEM (menu_item);
384   check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
385   toggled = FALSE;
386
387   if (check_menu_item->active)
388     {
389       tmp_menu_item = NULL;
390       tmp_list = radio_menu_item->group;
391
392       while (tmp_list)
393         {
394           tmp_menu_item = tmp_list->data;
395           tmp_list = tmp_list->next;
396
397           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
398             break;
399
400           tmp_menu_item = NULL;
401         }
402
403       if (tmp_menu_item)
404         {
405           toggled = TRUE;
406           check_menu_item->active = !check_menu_item->active;
407         }
408     }
409   else
410     {
411       toggled = TRUE;
412       check_menu_item->active = !check_menu_item->active;
413
414       tmp_list = radio_menu_item->group;
415       while (tmp_list)
416         {
417           tmp_menu_item = tmp_list->data;
418           tmp_list = tmp_list->next;
419
420           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
421             {
422               gtk_menu_item_activate (GTK_MENU_ITEM (tmp_menu_item));
423               break;
424             }
425         }
426     }
427
428   if (toggled)
429     gtk_check_menu_item_toggled (check_menu_item);
430
431   gtk_widget_queue_draw (GTK_WIDGET (radio_menu_item));
432 }