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