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