]> Pileus Git - ~andy/gtk/blob - gtk/gtkradiomenuitem.c
hu Feb 26 15:43:43 2004 Owen Taylor <otaylor@redhat.com>
[~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 ton a different one, but not when the composition
312    * of the group that a menu item belongs to changes.
313    *
314    * Since: 2.4
315    */
316   group_changed_signal = g_signal_new ("group-changed",
317                                        G_OBJECT_CLASS_TYPE (object_class),
318                                        G_SIGNAL_RUN_FIRST,
319                                        G_STRUCT_OFFSET (GtkRadioMenuItemClass, group_changed),
320                                        NULL, NULL,
321                                        _gtk_marshal_VOID__VOID,
322                                        G_TYPE_NONE, 0);
323 }
324
325 static void
326 gtk_radio_menu_item_init (GtkRadioMenuItem *radio_menu_item)
327 {
328   radio_menu_item->group = g_slist_prepend (NULL, radio_menu_item);
329   gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (radio_menu_item), TRUE);
330 }
331
332 static void
333 gtk_radio_menu_item_destroy (GtkObject *object)
334 {
335   GtkWidget *old_group_singleton = NULL;
336   GtkRadioMenuItem *radio_menu_item;
337   GtkRadioMenuItem *tmp_menu_item;
338   GSList *tmp_list;
339   gboolean was_in_group;
340
341   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (object));
342
343   radio_menu_item = GTK_RADIO_MENU_ITEM (object);
344
345   was_in_group = radio_menu_item->group && radio_menu_item->group->next;
346   
347   radio_menu_item->group = g_slist_remove (radio_menu_item->group,
348                                            radio_menu_item);
349   if (radio_menu_item->group && !radio_menu_item->group->next)
350     old_group_singleton = radio_menu_item->group->data;
351
352   tmp_list = radio_menu_item->group;
353
354   while (tmp_list)
355     {
356       tmp_menu_item = tmp_list->data;
357       tmp_list = tmp_list->next;
358
359       tmp_menu_item->group = radio_menu_item->group;
360     }
361
362   /* this radio menu item is no longer in the group */
363   radio_menu_item->group = NULL;
364   
365   if (old_group_singleton)
366     g_signal_emit (old_group_singleton, group_changed_signal, 0);
367   if (was_in_group)
368     g_signal_emit (radio_menu_item, group_changed_signal, 0);
369   
370   if (GTK_OBJECT_CLASS (parent_class)->destroy)
371     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
372 }
373
374 static void
375 gtk_radio_menu_item_activate (GtkMenuItem *menu_item)
376 {
377   GtkRadioMenuItem *radio_menu_item;
378   GtkCheckMenuItem *check_menu_item;
379   GtkCheckMenuItem *tmp_menu_item;
380   GSList *tmp_list;
381   gint toggled;
382
383   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (menu_item));
384
385   radio_menu_item = GTK_RADIO_MENU_ITEM (menu_item);
386   check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
387   toggled = FALSE;
388
389   if (check_menu_item->active)
390     {
391       tmp_menu_item = NULL;
392       tmp_list = radio_menu_item->group;
393
394       while (tmp_list)
395         {
396           tmp_menu_item = tmp_list->data;
397           tmp_list = tmp_list->next;
398
399           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
400             break;
401
402           tmp_menu_item = NULL;
403         }
404
405       if (tmp_menu_item)
406         {
407           toggled = TRUE;
408           check_menu_item->active = !check_menu_item->active;
409         }
410     }
411   else
412     {
413       toggled = TRUE;
414       check_menu_item->active = !check_menu_item->active;
415
416       tmp_list = radio_menu_item->group;
417       while (tmp_list)
418         {
419           tmp_menu_item = tmp_list->data;
420           tmp_list = tmp_list->next;
421
422           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
423             {
424               gtk_menu_item_activate (GTK_MENU_ITEM (tmp_menu_item));
425               break;
426             }
427         }
428     }
429
430   if (toggled)
431     gtk_check_menu_item_toggled (check_menu_item);
432
433   gtk_widget_queue_draw (GTK_WIDGET (radio_menu_item));
434 }