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