gtkradiomenuitem.c 17.2 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2
3
4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6
7
8
9
10
11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17
18

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20
21
22
23
24
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

25
#include "config.h"
Tim Janik's avatar
Tim Janik committed
26
#include "gtkaccellabel.h"
27
#include "gtkcheckmenuitemprivate.h"
28
#include "gtkmarshalers.h"
Elliot Lee's avatar
Elliot Lee committed
29
#include "gtkradiomenuitem.h"
30
#include "deprecated/gtkactivatable.h"
31
32
#include "gtkprivate.h"
#include "gtkintl.h"
33
#include "a11y/gtkradiomenuitemaccessible.h"
Elliot Lee's avatar
Elliot Lee committed
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * SECTION:gtkradiomenuitem
 * @Short_description: A choice from multiple check menu items
 * @Title: GtkRadioMenuItem
 * @See_also: #GtkMenuItem, #GtkCheckMenuItem
 *
 * A radio menu item is a check menu item that belongs to a group. At each
 * instant exactly one of the radio menu items from a group is selected.
 *
 * The group list does not need to be freed, as each #GtkRadioMenuItem will
 * remove itself and its list item when it is destroyed.
 *
 * The correct way to create a group of radio menu items is approximatively
 * this:
 *
50
51
 * ## How to create a group of radio menu items.
 *
52
 * |[<!-- language="C" -->
53
54
55
56
57
58
59
60
61
62
63
 * GSList *group = NULL;
 * GtkWidget *item;
 * gint i;
 *
 * for (i = 0; i < 5; i++)
 * {
 *   item = gtk_radio_menu_item_new_with_label (group, "This is an example");
 *   group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
 *   if (i == 1)
 *     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
 * }
64
 * ]|
Matthias Clasen's avatar
Matthias Clasen committed
65
66
67
 *
 * # CSS nodes
 *
68
69
 * |[<!-- language="plain" -->
 * menuitem
70
71
 * ├── radio.left
 * ╰── <child>
72
73
 * ]|
 *
Matthias Clasen's avatar
Matthias Clasen committed
74
75
 * GtkRadioMenuItem has a main CSS node with name menuitem, and a subnode
 * with name radio, which gets the .left or .right style class.
76
77
 */

78
struct _GtkRadioMenuItemPrivate
79
80
81
82
{
  GSList *group;
};

83
84
85
86
87
88
enum {
  PROP_0,
  PROP_GROUP
};


89
static void gtk_radio_menu_item_destroy        (GtkWidget             *widget);
Elliot Lee's avatar
Elliot Lee committed
90
static void gtk_radio_menu_item_activate       (GtkMenuItem           *menu_item);
91
92
93
94
95
96
97
98
static void gtk_radio_menu_item_set_property   (GObject               *object,
						guint                  prop_id,
						const GValue          *value,
						GParamSpec            *pspec);
static void gtk_radio_menu_item_get_property   (GObject               *object,
						guint                  prop_id,
						GValue                *value,
						GParamSpec            *pspec);
Elliot Lee's avatar
Elliot Lee committed
99

100
101
static guint group_changed_signal = 0;

102
G_DEFINE_TYPE_WITH_PRIVATE (GtkRadioMenuItem, gtk_radio_menu_item, GTK_TYPE_CHECK_MENU_ITEM)
Elliot Lee's avatar
Elliot Lee committed
103

104
105
/**
 * gtk_radio_menu_item_new:
106
107
 * @group: (element-type GtkRadioMenuItem) (allow-none): the group to which the
 *   radio menu item is to be attached, or %NULL
108
109
110
111
112
 *
 * Creates a new #GtkRadioMenuItem.
 *
 * Returns: a new #GtkRadioMenuItem
 */
Elliot Lee's avatar
Elliot Lee committed
113
114
115
116
117
GtkWidget*
gtk_radio_menu_item_new (GSList *group)
{
  GtkRadioMenuItem *radio_menu_item;

Manish Singh's avatar
Manish Singh committed
118
  radio_menu_item = g_object_new (GTK_TYPE_RADIO_MENU_ITEM, NULL);
Elliot Lee's avatar
Elliot Lee committed
119

120
121
122
123
  gtk_radio_menu_item_set_group (radio_menu_item, group);

  return GTK_WIDGET (radio_menu_item);
}
Elliot Lee's avatar
Elliot Lee committed
124

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
static void
gtk_radio_menu_item_set_property (GObject      *object,
				  guint         prop_id,
				  const GValue *value,
				  GParamSpec   *pspec)
{
  GtkRadioMenuItem *radio_menu_item;

  radio_menu_item = GTK_RADIO_MENU_ITEM (object);

  switch (prop_id)
    {
      GSList *slist;

    case PROP_GROUP:
140
141
142
      slist = g_value_get_object (value);
      if (slist)
        slist = gtk_radio_menu_item_get_group ((GtkRadioMenuItem*) g_value_get_object (value));
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
      gtk_radio_menu_item_set_group (radio_menu_item, slist);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_radio_menu_item_get_property (GObject    *object,
				  guint       prop_id,
				  GValue     *value,
				  GParamSpec *pspec)
{
  switch (prop_id)
    {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

165
166
167
/**
 * gtk_radio_menu_item_set_group:
 * @radio_menu_item: a #GtkRadioMenuItem.
168
 * @group: (element-type GtkRadioMenuItem) (allow-none): the new group, or %NULL.
169
170
171
 *
 * Sets the group of a radio menu item, or changes it.
 */
172
173
174
175
void
gtk_radio_menu_item_set_group (GtkRadioMenuItem *radio_menu_item,
			       GSList           *group)
{
176
  GtkRadioMenuItemPrivate *priv;
177
178
  GtkWidget *old_group_singleton = NULL;
  GtkWidget *new_group_singleton = NULL;
179

180
  g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item));
181

182
183
  priv = radio_menu_item->priv;

184
185
186
  if (priv->group == group)
    return;

187
  if (priv->group)
Elliot Lee's avatar
Elliot Lee committed
188
    {
189
      GSList *slist;
190

191
192
193
194
195
196
      priv->group = g_slist_remove (priv->group, radio_menu_item);

      if (priv->group && !priv->group->next)
	old_group_singleton = g_object_ref (priv->group->data);

      for (slist = priv->group; slist; slist = slist->next)
Elliot Lee's avatar
Elliot Lee committed
197
	{
198
199
200
	  GtkRadioMenuItem *tmp_item;
	  
	  tmp_item = slist->data;
201
202

	  tmp_item->priv->group = priv->group;
203
204
205
	}
    }
  
206
207
  if (group && !group->next)
    new_group_singleton = g_object_ref (group->data);
208
209
210

  priv->group = g_slist_prepend (group, radio_menu_item);

211
212
213
214
215
216
217
218
219
  if (group)
    {
      GSList *slist;
      
      for (slist = group; slist; slist = slist->next)
	{
	  GtkRadioMenuItem *tmp_item;
	  
	  tmp_item = slist->data;
220
221

	  tmp_item->priv->group = priv->group;
Elliot Lee's avatar
Elliot Lee committed
222
	}
223
224

      _gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (radio_menu_item), FALSE);
Elliot Lee's avatar
Elliot Lee committed
225
226
227
    }
  else
    {
228
      _gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (radio_menu_item), TRUE);
229
230
      /* gtk_widget_set_state (GTK_WIDGET (radio_menu_item), GTK_STATE_ACTIVE);
       */
Elliot Lee's avatar
Elliot Lee committed
231
    }
232
233
234

  g_object_ref (radio_menu_item);

235
  g_object_notify (G_OBJECT (radio_menu_item), "group");
236
237
238
239
240
241
242
243
244
245
246
247
248
  g_signal_emit (radio_menu_item, group_changed_signal, 0);
  if (old_group_singleton)
    {
      g_signal_emit (old_group_singleton, group_changed_signal, 0);
      g_object_unref (old_group_singleton);
    }
  if (new_group_singleton)
    {
      g_signal_emit (new_group_singleton, group_changed_signal, 0);
      g_object_unref (new_group_singleton);
    }

  g_object_unref (radio_menu_item);
Elliot Lee's avatar
Elliot Lee committed
249
250
}

251
252
253

/**
 * gtk_radio_menu_item_new_with_label:
254
255
 * @group: (element-type GtkRadioMenuItem) (allow-none):
 *         group the radio menu item is inside, or %NULL
256
257
258
259
260
261
 * @label: the text for the label
 *
 * Creates a new #GtkRadioMenuItem whose child is a simple #GtkLabel.
 *
 * Returns: (transfer none): A new #GtkRadioMenuItem
 */
Elliot Lee's avatar
Elliot Lee committed
262
263
264
265
GtkWidget*
gtk_radio_menu_item_new_with_label (GSList *group,
				    const gchar *label)
{
266
267
268
269
  return g_object_new (GTK_TYPE_RADIO_MENU_ITEM,
          "group", (group) ? group->data : NULL,
          "label", label,
          NULL);
Elliot Lee's avatar
Elliot Lee committed
270
271
}

272
273
274

/**
 * gtk_radio_menu_item_new_with_mnemonic:
275
276
 * @group: (element-type GtkRadioMenuItem) (allow-none):
 *         group the radio menu item is inside, or %NULL
277
278
279
280
281
282
 * @label: the text of the button, with an underscore in front of the
 *         mnemonic character
 *
 * Creates a new #GtkRadioMenuItem containing a label. The label
 * will be created using gtk_label_new_with_mnemonic(), so underscores
 * in @label indicate the mnemonic for the menu item.
Matthias Clasen's avatar
Matthias Clasen committed
283
284
285
 *
 * Returns: a new #GtkRadioMenuItem
 */
286
287
288
289
GtkWidget*
gtk_radio_menu_item_new_with_mnemonic (GSList *group,
				       const gchar *label)
{
290
291
292
293
294
  return g_object_new (GTK_TYPE_RADIO_MENU_ITEM,
          "group", (group) ? group->data : NULL,
          "label", label,
          "use-underline", TRUE,
          NULL);
295
296
}

297
/**
298
 * gtk_radio_menu_item_new_from_widget: (constructor)
299
 * @group: (allow-none): An existing #GtkRadioMenuItem
Matthias Clasen's avatar
Matthias Clasen committed
300
 *
301
 * Creates a new #GtkRadioMenuItem adding it to the same group as @group.
Matthias Clasen's avatar
Matthias Clasen committed
302
 *
303
 * Returns: (transfer none): The new #GtkRadioMenuItem
Matthias Clasen's avatar
Matthias Clasen committed
304
 *
305
306
307
308
309
310
311
 * Since: 2.4
 **/
GtkWidget *
gtk_radio_menu_item_new_from_widget (GtkRadioMenuItem *group)
{
  GSList *list = NULL;
  
312
  g_return_val_if_fail (group == NULL || GTK_IS_RADIO_MENU_ITEM (group), NULL);
313
314
315
316
317
318
319
320

  if (group)
    list = gtk_radio_menu_item_get_group (group);
  
  return gtk_radio_menu_item_new (list);
}

/**
321
 * gtk_radio_menu_item_new_with_mnemonic_from_widget: (constructor)
322
323
 * @group: (allow-none): An existing #GtkRadioMenuItem
 * @label: (allow-none): the text of the button, with an underscore in front of the
324
325
326
327
328
329
330
331
 *         mnemonic character
 *
 * Creates a new GtkRadioMenuItem containing a label. The label will be
 * created using gtk_label_new_with_mnemonic(), so underscores in label
 * indicate the mnemonic for the menu item.
 *
 * The new #GtkRadioMenuItem is added to the same group as @group.
 *
332
 * Returns: (transfer none): The new #GtkRadioMenuItem
Matthias Clasen's avatar
Matthias Clasen committed
333
 *
334
335
336
337
338
339
340
341
 * Since: 2.4
 **/
GtkWidget *
gtk_radio_menu_item_new_with_mnemonic_from_widget (GtkRadioMenuItem *group,
						   const gchar *label)
{
  GSList *list = NULL;

342
  g_return_val_if_fail (group == NULL || GTK_IS_RADIO_MENU_ITEM (group), NULL);
343
344
345
346
347
348
349
350

  if (group)
    list = gtk_radio_menu_item_get_group (group);

  return gtk_radio_menu_item_new_with_mnemonic (list, label);
}

/**
351
 * gtk_radio_menu_item_new_with_label_from_widget: (constructor)
352
353
 * @group: (allow-none): an existing #GtkRadioMenuItem
 * @label: (allow-none): the text for the label
354
355
356
357
 *
 * Creates a new GtkRadioMenuItem whose child is a simple GtkLabel.
 * The new #GtkRadioMenuItem is added to the same group as @group.
 *
358
 * Returns: (transfer none): The new #GtkRadioMenuItem
Matthias Clasen's avatar
Matthias Clasen committed
359
 *
360
361
362
363
364
365
366
367
 * Since: 2.4
 **/
GtkWidget *
gtk_radio_menu_item_new_with_label_from_widget (GtkRadioMenuItem *group,
						const gchar *label)
{
  GSList *list = NULL;

368
  g_return_val_if_fail (group == NULL || GTK_IS_RADIO_MENU_ITEM (group), NULL);
369
370
371
372
373
374
375

  if (group)
    list = gtk_radio_menu_item_get_group (group);

  return gtk_radio_menu_item_new_with_label (list, label);
}

Matthias Clasen's avatar
Matthias Clasen committed
376
377
378
379
380
381
382
/**
 * gtk_radio_menu_item_get_group:
 * @radio_menu_item: a #GtkRadioMenuItem
 *
 * Returns the group to which the radio menu item belongs, as a #GList of
 * #GtkRadioMenuItem. The list belongs to GTK+ and should not be freed.
 *
383
384
 * Returns: (element-type GtkRadioMenuItem) (transfer none): the group
 *     of @radio_menu_item
Matthias Clasen's avatar
Matthias Clasen committed
385
 */
Elliot Lee's avatar
Elliot Lee committed
386
GSList*
Owen Taylor's avatar
Owen Taylor committed
387
gtk_radio_menu_item_get_group (GtkRadioMenuItem *radio_menu_item)
Elliot Lee's avatar
Elliot Lee committed
388
389
390
{
  g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item), NULL);

391
  return radio_menu_item->priv->group;
Elliot Lee's avatar
Elliot Lee committed
392
393
394
395
396
}

static void
gtk_radio_menu_item_class_init (GtkRadioMenuItemClass *klass)
{
397
  GObjectClass *gobject_class;
398
  GtkWidgetClass *widget_class;
Elliot Lee's avatar
Elliot Lee committed
399
400
  GtkMenuItemClass *menu_item_class;

401
  gobject_class = G_OBJECT_CLASS (klass);
402
  widget_class = GTK_WIDGET_CLASS (klass);
403
  menu_item_class = GTK_MENU_ITEM_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
404

405
406
407
  gobject_class->set_property = gtk_radio_menu_item_set_property;
  gobject_class->get_property = gtk_radio_menu_item_get_property;

408
  widget_class->destroy = gtk_radio_menu_item_destroy;
409

410
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_RADIO_MENU_ITEM_ACCESSIBLE);
411
412
413

  menu_item_class->activate = gtk_radio_menu_item_activate;

Matthias Clasen's avatar
Matthias Clasen committed
414
415
416
417
418
419
420
  /**
   * GtkRadioMenuItem:group:
   * 
   * The radio menu item whose group this widget belongs to.
   * 
   * Since: 2.8
   */
421
422
423
424
  g_object_class_install_property (gobject_class,
				   PROP_GROUP,
				   g_param_spec_object ("group",
							P_("Group"),
Matthias Clasen's avatar
Matthias Clasen committed
425
							P_("The radio menu item whose group this widget belongs to."),
426
427
428
							GTK_TYPE_RADIO_MENU_ITEM,
							GTK_PARAM_WRITABLE));

429
430
431
432
433
434
435
  /**
   * GtkStyle::group-changed:
   * @style: the object which received the signal
   *
   * Emitted when the group of radio menu items that a radio menu item belongs
   * to changes. This is emitted when a radio menu item switches from
   * being alone to being part of a group of 2 or more menu items, or
Matthias Clasen's avatar
Matthias Clasen committed
436
   * vice-versa, and when a button is moved from one group of 2 or
437
   * more menu items ton a different one, but not when the composition
438
   * of the group that a menu item belongs to changes.
439
440
   *
   * Since: 2.4
441
   */
Matthias Clasen's avatar
Matthias Clasen committed
442
  group_changed_signal = g_signal_new (I_("group-changed"),
443
				       G_OBJECT_CLASS_TYPE (gobject_class),
444
445
446
				       G_SIGNAL_RUN_FIRST,
				       G_STRUCT_OFFSET (GtkRadioMenuItemClass, group_changed),
				       NULL, NULL,
447
				       NULL,
448
				       G_TYPE_NONE, 0);
Elliot Lee's avatar
Elliot Lee committed
449
450
451
452
453
}

static void
gtk_radio_menu_item_init (GtkRadioMenuItem *radio_menu_item)
{
454
  GtkRadioMenuItemPrivate *priv;
455

456
  radio_menu_item->priv = gtk_radio_menu_item_get_instance_private (radio_menu_item);
457
458
459
  priv = radio_menu_item->priv;

  priv->group = g_slist_prepend (NULL, radio_menu_item);
460
  gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (radio_menu_item), TRUE);
Elliot Lee's avatar
Elliot Lee committed
461
462
}

463
static void
464
gtk_radio_menu_item_destroy (GtkWidget *widget)
465
{
466
  GtkRadioMenuItem *radio_menu_item = GTK_RADIO_MENU_ITEM (widget);
467
  GtkRadioMenuItemPrivate *priv = radio_menu_item->priv;
468
  GtkWidget *old_group_singleton = NULL;
469
470
  GtkRadioMenuItem *tmp_menu_item;
  GSList *tmp_list;
471
  gboolean was_in_group;
472

473
474
475
476
477
  was_in_group = priv->group && priv->group->next;

  priv->group = g_slist_remove (priv->group, radio_menu_item);
  if (priv->group && !priv->group->next)
    old_group_singleton = priv->group->data;
478

479
  tmp_list = priv->group;
480
481
482
483
484
485

  while (tmp_list)
    {
      tmp_menu_item = tmp_list->data;
      tmp_list = tmp_list->next;

486
      tmp_menu_item->priv->group = priv->group;
487
488
    }

489
  /* this radio menu item is no longer in the group */
490
  priv->group = NULL;
491
  
492
493
494
495
  if (old_group_singleton)
    g_signal_emit (old_group_singleton, group_changed_signal, 0);
  if (was_in_group)
    g_signal_emit (radio_menu_item, group_changed_signal, 0);
496

497
  GTK_WIDGET_CLASS (gtk_radio_menu_item_parent_class)->destroy (widget);
498
499
}

Elliot Lee's avatar
Elliot Lee committed
500
501
502
static void
gtk_radio_menu_item_activate (GtkMenuItem *menu_item)
{
503
  GtkRadioMenuItem *radio_menu_item = GTK_RADIO_MENU_ITEM (menu_item);
504
  GtkRadioMenuItemPrivate *priv = radio_menu_item->priv;
505
  GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
Elliot Lee's avatar
Elliot Lee committed
506
  GtkCheckMenuItem *tmp_menu_item;
507
  GtkAction        *action;
Elliot Lee's avatar
Elliot Lee committed
508
  GSList *tmp_list;
509
  gboolean active;
Elliot Lee's avatar
Elliot Lee committed
510
511
  gint toggled;

512
513
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;

514
515
516
517
  action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (menu_item));
  if (action && gtk_menu_item_get_submenu (menu_item) == NULL)
    gtk_action_activate (action);

518
519
  G_GNUC_END_IGNORE_DEPRECATIONS;

Elliot Lee's avatar
Elliot Lee committed
520
521
  toggled = FALSE;

522
523
  active = gtk_check_menu_item_get_active (check_menu_item);
  if (active)
Elliot Lee's avatar
Elliot Lee committed
524
525
    {
      tmp_menu_item = NULL;
526
      tmp_list = priv->group;
Elliot Lee's avatar
Elliot Lee committed
527
528
529
530
531
532

      while (tmp_list)
	{
	  tmp_menu_item = tmp_list->data;
	  tmp_list = tmp_list->next;

533
534
          if (gtk_check_menu_item_get_active (tmp_menu_item) &&
              tmp_menu_item != check_menu_item)
Elliot Lee's avatar
Elliot Lee committed
535
536
537
538
539
540
541
542
	    break;

	  tmp_menu_item = NULL;
	}

      if (tmp_menu_item)
	{
	  toggled = TRUE;
543
          _gtk_check_menu_item_set_active (check_menu_item, !active);
Elliot Lee's avatar
Elliot Lee committed
544
545
546
547
548
	}
    }
  else
    {
      toggled = TRUE;
549
      _gtk_check_menu_item_set_active (check_menu_item, !active);
Elliot Lee's avatar
Elliot Lee committed
550

551
      tmp_list = priv->group;
Elliot Lee's avatar
Elliot Lee committed
552
553
554
555
556
      while (tmp_list)
	{
	  tmp_menu_item = tmp_list->data;
	  tmp_list = tmp_list->next;

557
558
          if (gtk_check_menu_item_get_active (tmp_menu_item) &&
              tmp_menu_item != check_menu_item)
Elliot Lee's avatar
Elliot Lee committed
559
	    {
560
              gtk_menu_item_activate (GTK_MENU_ITEM (tmp_menu_item));
Elliot Lee's avatar
Elliot Lee committed
561
562
563
564
565
566
	      break;
	    }
	}
    }

  if (toggled)
567
568
569
    {
      gtk_check_menu_item_toggled (check_menu_item);
    }
570

571
  gtk_widget_queue_draw (GTK_WIDGET (radio_menu_item));
Elliot Lee's avatar
Elliot Lee committed
572
}
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628

/**
 * gtk_radio_menu_item_join_group:
 * @radio_menu_item: a #GtkRadioMenuItem
 * @group_source: (allow-none): a #GtkRadioMenuItem whose group we are
 *   joining, or %NULL to remove the @radio_menu_item from its current
 *   group
 *
 * Joins a #GtkRadioMenuItem object to the group of another #GtkRadioMenuItem
 * object.
 *
 * This function should be used by language bindings to avoid the memory
 * manangement of the opaque #GSList of gtk_radio_menu_item_get_group()
 * and gtk_radio_menu_item_set_group().
 *
 * A common way to set up a group of #GtkRadioMenuItem instances is:
 *
 * |[
 *   GtkRadioMenuItem *last_item = NULL;
 *
 *   while ( ...more items to add... )
 *     {
 *       GtkRadioMenuItem *radio_item;
 *
 *       radio_item = gtk_radio_menu_item_new (...);
 *
 *       gtk_radio_menu_item_join_group (radio_item, last_item);
 *       last_item = radio_item;
 *     }
 * ]|
 *
 * Since: 3.18
 */
void
gtk_radio_menu_item_join_group (GtkRadioMenuItem *radio_menu_item,
                                GtkRadioMenuItem *group_source)
{
  g_return_if_fail (GTK_IS_RADIO_MENU_ITEM (radio_menu_item));
  g_return_if_fail (group_source == NULL || GTK_IS_RADIO_MENU_ITEM (group_source));

  if (group_source != NULL)
    {
      GSList *group = gtk_radio_menu_item_get_group (group_source);

      if (group == NULL)
        {
          /* if the group source does not have a group, we force one */
          gtk_radio_menu_item_set_group (group_source, NULL);
          group = gtk_radio_menu_item_get_group (group_source);
        }

      gtk_radio_menu_item_set_group (radio_menu_item, group);
    }
  else
    gtk_radio_menu_item_set_group (radio_menu_item, NULL);
}