]> Pileus Git - ~andy/gtk/blob - gtk/gtkradiomenuitem.c
e05062517630ed9f8250b6c23b22d15115d97bc2
[~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 <config.h>
28 #include "gtkaccellabel.h"
29 #include "gtkmarshalers.h"
30 #include "gtkradiomenuitem.h"
31 #include "gtkprivate.h"
32 #include "gtkintl.h"
33 #include "gtkalias.h"
34
35
36 enum {
37   PROP_0,
38   PROP_GROUP
39 };
40
41
42 static void gtk_radio_menu_item_destroy        (GtkObject             *object);
43 static void gtk_radio_menu_item_activate       (GtkMenuItem           *menu_item);
44 static void gtk_radio_menu_item_set_property   (GObject               *object,
45                                                 guint                  prop_id,
46                                                 const GValue          *value,
47                                                 GParamSpec            *pspec);
48 static void gtk_radio_menu_item_get_property   (GObject               *object,
49                                                 guint                  prop_id,
50                                                 GValue                *value,
51                                                 GParamSpec            *pspec);
52
53 static guint group_changed_signal = 0;
54
55 G_DEFINE_TYPE (GtkRadioMenuItem, gtk_radio_menu_item, GTK_TYPE_CHECK_MENU_ITEM);
56
57 GtkWidget*
58 gtk_radio_menu_item_new (GSList *group)
59 {
60   GtkRadioMenuItem *radio_menu_item;
61
62   radio_menu_item = g_object_new (GTK_TYPE_RADIO_MENU_ITEM, NULL);
63
64   gtk_radio_menu_item_set_group (radio_menu_item, group);
65
66   return GTK_WIDGET (radio_menu_item);
67 }
68
69 static void
70 gtk_radio_menu_item_set_property (GObject      *object,
71                                   guint         prop_id,
72                                   const GValue *value,
73                                   GParamSpec   *pspec)
74 {
75   GtkRadioMenuItem *radio_menu_item;
76
77   radio_menu_item = GTK_RADIO_MENU_ITEM (object);
78
79   switch (prop_id)
80     {
81       GSList *slist;
82
83     case PROP_GROUP:
84       if (G_VALUE_HOLDS_OBJECT (value))
85         slist = gtk_radio_menu_item_get_group ((GtkRadioMenuItem*) g_value_get_object (value));
86       else
87         slist = NULL;
88       gtk_radio_menu_item_set_group (radio_menu_item, slist);
89       break;
90     default:
91       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
92       break;
93     }
94 }
95
96 static void
97 gtk_radio_menu_item_get_property (GObject    *object,
98                                   guint       prop_id,
99                                   GValue     *value,
100                                   GParamSpec *pspec)
101 {
102   switch (prop_id)
103     {
104     default:
105       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106       break;
107     }
108 }
109
110 void
111 gtk_radio_menu_item_set_group (GtkRadioMenuItem *radio_menu_item,
112                                GSList           *group)
113 {
114   GtkWidget *old_group_singleton = NULL;
115   GtkWidget *new_group_singleton = NULL;
116   
117   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item));
118   g_return_if_fail (!g_slist_find (group, radio_menu_item));
119
120   if (radio_menu_item->group)
121     {
122       GSList *slist;
123
124       radio_menu_item->group = g_slist_remove (radio_menu_item->group, radio_menu_item);
125       
126       if (radio_menu_item->group && !radio_menu_item->group->next)
127         old_group_singleton = g_object_ref (radio_menu_item->group->data);
128           
129       for (slist = radio_menu_item->group; slist; slist = slist->next)
130         {
131           GtkRadioMenuItem *tmp_item;
132           
133           tmp_item = slist->data;
134           
135           tmp_item->group = radio_menu_item->group;
136         }
137     }
138   
139   if (group && !group->next)
140     new_group_singleton = g_object_ref (group->data);
141   
142   radio_menu_item->group = g_slist_prepend (group, radio_menu_item);
143   
144   if (group)
145     {
146       GSList *slist;
147       
148       for (slist = group; slist; slist = slist->next)
149         {
150           GtkRadioMenuItem *tmp_item;
151           
152           tmp_item = slist->data;
153           
154           tmp_item->group = radio_menu_item->group;
155         }
156     }
157   else
158     {
159       GTK_CHECK_MENU_ITEM (radio_menu_item)->active = TRUE;
160       /* gtk_widget_set_state (GTK_WIDGET (radio_menu_item), GTK_STATE_ACTIVE);
161        */
162     }
163
164   g_object_ref (radio_menu_item);
165
166   g_object_notify (G_OBJECT (radio_menu_item), "group");
167   g_signal_emit (radio_menu_item, group_changed_signal, 0);
168   if (old_group_singleton)
169     {
170       g_signal_emit (old_group_singleton, group_changed_signal, 0);
171       g_object_unref (old_group_singleton);
172     }
173   if (new_group_singleton)
174     {
175       g_signal_emit (new_group_singleton, group_changed_signal, 0);
176       g_object_unref (new_group_singleton);
177     }
178
179   g_object_unref (radio_menu_item);
180 }
181
182 GtkWidget*
183 gtk_radio_menu_item_new_with_label (GSList *group,
184                                     const gchar *label)
185 {
186   GtkWidget *radio_menu_item;
187   GtkWidget *accel_label;
188
189   radio_menu_item = gtk_radio_menu_item_new (group);
190   accel_label = gtk_accel_label_new (label);
191   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
192   gtk_container_add (GTK_CONTAINER (radio_menu_item), accel_label);
193   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), radio_menu_item);
194   gtk_widget_show (accel_label);
195
196   return radio_menu_item;
197 }
198
199
200 /**
201  * gtk_radio_menu_item_new_with_mnemonic:
202  * @group: group the radio menu item is inside
203  * @label: the text of the button, with an underscore in front of the
204  *         mnemonic character
205  * @returns: a new #GtkRadioMenuItem
206  *
207  * Creates a new #GtkRadioMenuItem containing a label. The label
208  * will be created using gtk_label_new_with_mnemonic(), so underscores
209  * in @label indicate the mnemonic for the menu item.
210  **/
211 GtkWidget*
212 gtk_radio_menu_item_new_with_mnemonic (GSList *group,
213                                        const gchar *label)
214 {
215   GtkWidget *radio_menu_item;
216   GtkWidget *accel_label;
217
218   radio_menu_item = gtk_radio_menu_item_new (group);
219   accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
220   gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), label);
221   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
222
223   gtk_container_add (GTK_CONTAINER (radio_menu_item), accel_label);
224   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), radio_menu_item);
225   gtk_widget_show (accel_label);
226
227   return radio_menu_item;
228 }
229
230 /**
231  * gtk_radio_menu_item_new_from_widget:
232  * @group: An existing #GtkRadioMenuItem
233  * 
234  * Creates a new #GtkRadioMenuItem adding it to the same group as @group.
235  * 
236  * Return value: The new #GtkRadioMenuItem
237  * 
238  * Since: 2.4
239  **/
240 GtkWidget *
241 gtk_radio_menu_item_new_from_widget (GtkRadioMenuItem *group)
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 (list);
251 }
252
253 /**
254  * gtk_radio_menu_item_new_with_mnemonic_from_widget:
255  * @group: An existing #GtkRadioMenuItem
256  * @label: the text of the button, with an underscore in front of the
257  *         mnemonic character
258  *
259  * Creates a new GtkRadioMenuItem containing a label. The label will be
260  * created using gtk_label_new_with_mnemonic(), so underscores in label
261  * indicate the mnemonic for the menu item.
262  *
263  * The new #GtkRadioMenuItem is added to the same group as @group.
264  *
265  * Return value: The new #GtkRadioMenuItem
266  * 
267  * Since: 2.4
268  **/
269 GtkWidget *
270 gtk_radio_menu_item_new_with_mnemonic_from_widget (GtkRadioMenuItem *group,
271                                                    const gchar *label)
272 {
273   GSList *list = NULL;
274
275   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
276
277   if (group)
278     list = gtk_radio_menu_item_get_group (group);
279
280   return gtk_radio_menu_item_new_with_mnemonic (list, label);
281 }
282
283 /**
284  * gtk_radio_menu_item_new_with_label_from_widget:
285  * @group: an existing #GtkRadioMenuItem 
286  * @label: the text for the label
287  *
288  * Creates a new GtkRadioMenuItem whose child is a simple GtkLabel.
289  * The new #GtkRadioMenuItem is added to the same group as @group.
290  *
291  * Return value: The new #GtkRadioMenuItem
292  * 
293  * Since: 2.4
294  **/
295 GtkWidget *
296 gtk_radio_menu_item_new_with_label_from_widget (GtkRadioMenuItem *group,
297                                                 const gchar *label)
298 {
299   GSList *list = NULL;
300
301   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
302
303   if (group)
304     list = gtk_radio_menu_item_get_group (group);
305
306   return gtk_radio_menu_item_new_with_label (list, label);
307 }
308
309 GSList*
310 gtk_radio_menu_item_get_group (GtkRadioMenuItem *radio_menu_item)
311 {
312   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item), NULL);
313
314   return radio_menu_item->group;
315 }
316
317
318 static void
319 gtk_radio_menu_item_class_init (GtkRadioMenuItemClass *klass)
320 {
321   GObjectClass *gobject_class;  
322   GtkObjectClass *object_class;
323   GtkMenuItemClass *menu_item_class;
324
325   gobject_class = G_OBJECT_CLASS (klass);
326   object_class = GTK_OBJECT_CLASS (klass);
327   menu_item_class = GTK_MENU_ITEM_CLASS (klass);
328
329   gobject_class->set_property = gtk_radio_menu_item_set_property;
330   gobject_class->get_property = gtk_radio_menu_item_get_property;
331
332   /**
333    * GtkRadioMenuItem:group:
334    * 
335    * The radio menu item whose group this widget belongs to.
336    * 
337    * Since: 2.8
338    */
339   g_object_class_install_property (gobject_class,
340                                    PROP_GROUP,
341                                    g_param_spec_object ("group",
342                                                         P_("Group"),
343                                                         P_("The radio menu item whose group this widget belongs to."),
344                                                         GTK_TYPE_RADIO_MENU_ITEM,
345                                                         GTK_PARAM_WRITABLE));
346
347   object_class->destroy = gtk_radio_menu_item_destroy;
348
349   menu_item_class->activate = gtk_radio_menu_item_activate;
350
351   /**
352    * GtkStyle::group-changed:
353    * @style: the object which received the signal
354    *
355    * Emitted when the group of radio menu items that a radio menu item belongs
356    * to changes. This is emitted when a radio menu item switches from
357    * being alone to being part of a group of 2 or more menu items, or
358    * vice-versa, and when a buttton is moved from one group of 2 or
359    * more menu items ton a different one, but not when the composition
360    * of the group that a menu item belongs to changes.
361    *
362    * Since: 2.4
363    */
364   group_changed_signal = g_signal_new (I_("group-changed"),
365                                        G_OBJECT_CLASS_TYPE (object_class),
366                                        G_SIGNAL_RUN_FIRST,
367                                        G_STRUCT_OFFSET (GtkRadioMenuItemClass, group_changed),
368                                        NULL, NULL,
369                                        _gtk_marshal_VOID__VOID,
370                                        G_TYPE_NONE, 0);
371 }
372
373 static void
374 gtk_radio_menu_item_init (GtkRadioMenuItem *radio_menu_item)
375 {
376   radio_menu_item->group = g_slist_prepend (NULL, radio_menu_item);
377   gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (radio_menu_item), TRUE);
378 }
379
380 static void
381 gtk_radio_menu_item_destroy (GtkObject *object)
382 {
383   GtkWidget *old_group_singleton = NULL;
384   GtkRadioMenuItem *radio_menu_item;
385   GtkRadioMenuItem *tmp_menu_item;
386   GSList *tmp_list;
387   gboolean was_in_group;
388
389   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (object));
390
391   radio_menu_item = GTK_RADIO_MENU_ITEM (object);
392
393   was_in_group = radio_menu_item->group && radio_menu_item->group->next;
394   
395   radio_menu_item->group = g_slist_remove (radio_menu_item->group,
396                                            radio_menu_item);
397   if (radio_menu_item->group && !radio_menu_item->group->next)
398     old_group_singleton = radio_menu_item->group->data;
399
400   tmp_list = radio_menu_item->group;
401
402   while (tmp_list)
403     {
404       tmp_menu_item = tmp_list->data;
405       tmp_list = tmp_list->next;
406
407       tmp_menu_item->group = radio_menu_item->group;
408     }
409
410   /* this radio menu item is no longer in the group */
411   radio_menu_item->group = NULL;
412   
413   if (old_group_singleton)
414     g_signal_emit (old_group_singleton, group_changed_signal, 0);
415   if (was_in_group)
416     g_signal_emit (radio_menu_item, group_changed_signal, 0);
417   
418   if (GTK_OBJECT_CLASS (gtk_radio_menu_item_parent_class)->destroy)
419     (* GTK_OBJECT_CLASS (gtk_radio_menu_item_parent_class)->destroy) (object);
420 }
421
422 static void
423 gtk_radio_menu_item_activate (GtkMenuItem *menu_item)
424 {
425   GtkRadioMenuItem *radio_menu_item;
426   GtkCheckMenuItem *check_menu_item;
427   GtkCheckMenuItem *tmp_menu_item;
428   GSList *tmp_list;
429   gint toggled;
430
431   g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (menu_item));
432
433   radio_menu_item = GTK_RADIO_MENU_ITEM (menu_item);
434   check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
435   toggled = FALSE;
436
437   if (check_menu_item->active)
438     {
439       tmp_menu_item = NULL;
440       tmp_list = radio_menu_item->group;
441
442       while (tmp_list)
443         {
444           tmp_menu_item = tmp_list->data;
445           tmp_list = tmp_list->next;
446
447           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
448             break;
449
450           tmp_menu_item = NULL;
451         }
452
453       if (tmp_menu_item)
454         {
455           toggled = TRUE;
456           check_menu_item->active = !check_menu_item->active;
457         }
458     }
459   else
460     {
461       toggled = TRUE;
462       check_menu_item->active = !check_menu_item->active;
463
464       tmp_list = radio_menu_item->group;
465       while (tmp_list)
466         {
467           tmp_menu_item = tmp_list->data;
468           tmp_list = tmp_list->next;
469
470           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
471             {
472               gtk_menu_item_activate (GTK_MENU_ITEM (tmp_menu_item));
473               break;
474             }
475         }
476     }
477
478   if (toggled)
479     gtk_check_menu_item_toggled (check_menu_item);
480
481   gtk_widget_queue_draw (GTK_WIDGET (radio_menu_item));
482 }
483
484 #define __GTK_RADIO_MENU_ITEM_C__
485 #include "gtkaliasdef.c"