]> Pileus Git - ~andy/gtk/blob - gtk/gtkradiomenuitem.c
gtk/: fully remove gtkalias hacks
[~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 "gtkactivatable.h"
32 #include "gtkprivate.h"
33 #include "gtkintl.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
183 /**
184  * gtk_radio_menu_item_new_with_label:
185  * @group: (element-type GtkRadioMenuItem) (transfer full):
186  * @label: the text for the label
187  *
188  * Creates a new #GtkRadioMenuItem whose child is a simple #GtkLabel.
189  *
190  * Returns: (transfer none): A new #GtkRadioMenuItem
191  */
192 GtkWidget*
193 gtk_radio_menu_item_new_with_label (GSList *group,
194                                     const gchar *label)
195 {
196   GtkWidget *radio_menu_item;
197   GtkWidget *accel_label;
198
199   radio_menu_item = gtk_radio_menu_item_new (group);
200   accel_label = gtk_accel_label_new (label);
201   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
202   gtk_container_add (GTK_CONTAINER (radio_menu_item), accel_label);
203   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), radio_menu_item);
204   gtk_widget_show (accel_label);
205
206   return radio_menu_item;
207 }
208
209
210 /**
211  * gtk_radio_menu_item_new_with_mnemonic:
212  * @group: group the radio menu item is inside
213  * @label: the text of the button, with an underscore in front of the
214  *         mnemonic character
215  * @returns: a new #GtkRadioMenuItem
216  *
217  * Creates a new #GtkRadioMenuItem containing a label. The label
218  * will be created using gtk_label_new_with_mnemonic(), so underscores
219  * in @label indicate the mnemonic for the menu item.
220  **/
221 GtkWidget*
222 gtk_radio_menu_item_new_with_mnemonic (GSList *group,
223                                        const gchar *label)
224 {
225   GtkWidget *radio_menu_item;
226   GtkWidget *accel_label;
227
228   radio_menu_item = gtk_radio_menu_item_new (group);
229   accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
230   gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), label);
231   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
232
233   gtk_container_add (GTK_CONTAINER (radio_menu_item), accel_label);
234   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), radio_menu_item);
235   gtk_widget_show (accel_label);
236
237   return radio_menu_item;
238 }
239
240 /**
241  * gtk_radio_menu_item_new_from_widget:
242  * @group: An existing #GtkRadioMenuItem
243  * 
244  * Creates a new #GtkRadioMenuItem adding it to the same group as @group.
245  * 
246  * Return value: The new #GtkRadioMenuItem
247  * 
248  * Since: 2.4
249  **/
250 GtkWidget *
251 gtk_radio_menu_item_new_from_widget (GtkRadioMenuItem *group)
252 {
253   GSList *list = NULL;
254   
255   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
256
257   if (group)
258     list = gtk_radio_menu_item_get_group (group);
259   
260   return gtk_radio_menu_item_new (list);
261 }
262
263 /**
264  * gtk_radio_menu_item_new_with_mnemonic_from_widget:
265  * @group: An existing #GtkRadioMenuItem
266  * @label: the text of the button, with an underscore in front of the
267  *         mnemonic character
268  *
269  * Creates a new GtkRadioMenuItem containing a label. The label will be
270  * created using gtk_label_new_with_mnemonic(), so underscores in label
271  * indicate the mnemonic for the menu item.
272  *
273  * The new #GtkRadioMenuItem is added to the same group as @group.
274  *
275  * Return value: The new #GtkRadioMenuItem
276  * 
277  * Since: 2.4
278  **/
279 GtkWidget *
280 gtk_radio_menu_item_new_with_mnemonic_from_widget (GtkRadioMenuItem *group,
281                                                    const gchar *label)
282 {
283   GSList *list = NULL;
284
285   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
286
287   if (group)
288     list = gtk_radio_menu_item_get_group (group);
289
290   return gtk_radio_menu_item_new_with_mnemonic (list, label);
291 }
292
293 /**
294  * gtk_radio_menu_item_new_with_label_from_widget:
295  * @group: an existing #GtkRadioMenuItem 
296  * @label: the text for the label
297  *
298  * Creates a new GtkRadioMenuItem whose child is a simple GtkLabel.
299  * The new #GtkRadioMenuItem is added to the same group as @group.
300  *
301  * Return value: The new #GtkRadioMenuItem
302  * 
303  * Since: 2.4
304  **/
305 GtkWidget *
306 gtk_radio_menu_item_new_with_label_from_widget (GtkRadioMenuItem *group,
307                                                 const gchar *label)
308 {
309   GSList *list = NULL;
310
311   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (group), NULL);
312
313   if (group)
314     list = gtk_radio_menu_item_get_group (group);
315
316   return gtk_radio_menu_item_new_with_label (list, label);
317 }
318
319 GSList*
320 gtk_radio_menu_item_get_group (GtkRadioMenuItem *radio_menu_item)
321 {
322   g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item), NULL);
323
324   return radio_menu_item->group;
325 }
326
327
328 static void
329 gtk_radio_menu_item_class_init (GtkRadioMenuItemClass *klass)
330 {
331   GObjectClass *gobject_class;  
332   GtkObjectClass *object_class;
333   GtkMenuItemClass *menu_item_class;
334
335   gobject_class = G_OBJECT_CLASS (klass);
336   object_class = GTK_OBJECT_CLASS (klass);
337   menu_item_class = GTK_MENU_ITEM_CLASS (klass);
338
339   gobject_class->set_property = gtk_radio_menu_item_set_property;
340   gobject_class->get_property = gtk_radio_menu_item_get_property;
341
342   /**
343    * GtkRadioMenuItem:group:
344    * 
345    * The radio menu item whose group this widget belongs to.
346    * 
347    * Since: 2.8
348    */
349   g_object_class_install_property (gobject_class,
350                                    PROP_GROUP,
351                                    g_param_spec_object ("group",
352                                                         P_("Group"),
353                                                         P_("The radio menu item whose group this widget belongs to."),
354                                                         GTK_TYPE_RADIO_MENU_ITEM,
355                                                         GTK_PARAM_WRITABLE));
356
357   object_class->destroy = gtk_radio_menu_item_destroy;
358
359   menu_item_class->activate = gtk_radio_menu_item_activate;
360
361   /**
362    * GtkStyle::group-changed:
363    * @style: the object which received the signal
364    *
365    * Emitted when the group of radio menu items that a radio menu item belongs
366    * to changes. This is emitted when a radio menu item switches from
367    * being alone to being part of a group of 2 or more menu items, or
368    * vice-versa, and when a button is moved from one group of 2 or
369    * more menu items ton a different one, but not when the composition
370    * of the group that a menu item belongs to changes.
371    *
372    * Since: 2.4
373    */
374   group_changed_signal = g_signal_new (I_("group-changed"),
375                                        G_OBJECT_CLASS_TYPE (object_class),
376                                        G_SIGNAL_RUN_FIRST,
377                                        G_STRUCT_OFFSET (GtkRadioMenuItemClass, group_changed),
378                                        NULL, NULL,
379                                        _gtk_marshal_VOID__VOID,
380                                        G_TYPE_NONE, 0);
381 }
382
383 static void
384 gtk_radio_menu_item_init (GtkRadioMenuItem *radio_menu_item)
385 {
386   radio_menu_item->group = g_slist_prepend (NULL, radio_menu_item);
387   gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (radio_menu_item), TRUE);
388 }
389
390 static void
391 gtk_radio_menu_item_destroy (GtkObject *object)
392 {
393   GtkRadioMenuItem *radio_menu_item = GTK_RADIO_MENU_ITEM (object);
394   GtkWidget *old_group_singleton = NULL;
395   GtkRadioMenuItem *tmp_menu_item;
396   GSList *tmp_list;
397   gboolean was_in_group;
398
399   was_in_group = radio_menu_item->group && radio_menu_item->group->next;
400   
401   radio_menu_item->group = g_slist_remove (radio_menu_item->group,
402                                            radio_menu_item);
403   if (radio_menu_item->group && !radio_menu_item->group->next)
404     old_group_singleton = radio_menu_item->group->data;
405
406   tmp_list = radio_menu_item->group;
407
408   while (tmp_list)
409     {
410       tmp_menu_item = tmp_list->data;
411       tmp_list = tmp_list->next;
412
413       tmp_menu_item->group = radio_menu_item->group;
414     }
415
416   /* this radio menu item is no longer in the group */
417   radio_menu_item->group = NULL;
418   
419   if (old_group_singleton)
420     g_signal_emit (old_group_singleton, group_changed_signal, 0);
421   if (was_in_group)
422     g_signal_emit (radio_menu_item, group_changed_signal, 0);
423
424   GTK_OBJECT_CLASS (gtk_radio_menu_item_parent_class)->destroy (object);
425 }
426
427 static void
428 gtk_radio_menu_item_activate (GtkMenuItem *menu_item)
429 {
430   GtkRadioMenuItem *radio_menu_item = GTK_RADIO_MENU_ITEM (menu_item);
431   GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
432   GtkCheckMenuItem *tmp_menu_item;
433   GtkAction        *action;
434   GSList *tmp_list;
435   gint toggled;
436
437   action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (menu_item));
438   if (action && gtk_menu_item_get_submenu (menu_item) == NULL)
439     gtk_action_activate (action);
440
441   toggled = FALSE;
442
443   if (check_menu_item->active)
444     {
445       tmp_menu_item = NULL;
446       tmp_list = radio_menu_item->group;
447
448       while (tmp_list)
449         {
450           tmp_menu_item = tmp_list->data;
451           tmp_list = tmp_list->next;
452
453           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
454             break;
455
456           tmp_menu_item = NULL;
457         }
458
459       if (tmp_menu_item)
460         {
461           toggled = TRUE;
462           check_menu_item->active = !check_menu_item->active;
463         }
464     }
465   else
466     {
467       toggled = TRUE;
468       check_menu_item->active = !check_menu_item->active;
469
470       tmp_list = radio_menu_item->group;
471       while (tmp_list)
472         {
473           tmp_menu_item = tmp_list->data;
474           tmp_list = tmp_list->next;
475
476           if (tmp_menu_item->active && (tmp_menu_item != check_menu_item))
477             {
478               gtk_menu_item_activate (GTK_MENU_ITEM (tmp_menu_item));
479               break;
480             }
481         }
482     }
483
484   if (toggled)
485     {
486       gtk_check_menu_item_toggled (check_menu_item);
487     }
488
489   gtk_widget_queue_draw (GTK_WIDGET (radio_menu_item));
490 }