~centralelyon2010/inkscape/imagelinks2

4454 by jiho-sf
Added part of a patch by Adam Strzelecki which makes GTK menu migrate to OS X menubar when GTK+quartz is used. This is taken from Gimp and should be safe enough at this stage since it really only affects OS X.
1
/* GTK+ Integration for the Mac OS X Menubar.
2
 *
3
 * Copyright (C) 2007 Pioneer Research Center USA, Inc.
4
 *
5
 * For further information, see:
6
 * http://developer.imendio.com/projects/gtk-macosx/menubar
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the
20
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
 * Boston, MA 02111-1307, USA.
22
 */
23
24
#ifdef HAVE_CONFIG_H
25
# include "config.h"
26
#endif
27
28
#include <gtk/gtk.h>
29
30
#ifdef GDK_WINDOWING_QUARTZ
31
32
#include <gdk/gdkkeysyms.h>
33
34
#include <Carbon/Carbon.h>
35
36
#include "ige-mac-menu.h"
37
38
39
/* TODO
40
 *
41
 * - Sync adding/removing/reordering items
42
 * - Create on demand? (can this be done with gtk+? ie fill in menu
43
     items when the menu is opened)
44
 * - Figure out what to do per app/window...
45
 *
46
 */
47
48
#define IGE_QUARTZ_MENU_CREATOR 'IGEC'
49
#define IGE_QUARTZ_ITEM_WIDGET  'IWID'
50
51
52
static void   sync_menu_shell (GtkMenuShell *menu_shell,
53
			       MenuRef       carbon_menu,
54
			       gboolean      toplevel,
55
			       gboolean      debug);
56
57
58
/*
59
 * utility functions
60
 */
61
62
static GtkWidget *
63
find_menu_label (GtkWidget *widget)
64
{
65
  GtkWidget *label = NULL;
66
67
  if (GTK_IS_LABEL (widget))
68
    return widget;
69
70
  if (GTK_IS_CONTAINER (widget))
71
    {
72
      GList *children;
73
      GList *l;
74
75
      children = gtk_container_get_children (GTK_CONTAINER (widget));
76
77
      for (l = children; l; l = l->next)
78
	{
79
	  label = find_menu_label (l->data);
80
	  if (label)
81
	    break;
82
	}
83
84
      g_list_free (children);
85
    }
86
87
  return label;
88
}
89
90
static const gchar *
91
get_menu_label_text (GtkWidget  *menu_item,
92
		     GtkWidget **label)
93
{
94
  GtkWidget *my_label;
95
96
  my_label = find_menu_label (menu_item);
97
  if (label)
98
    *label = my_label;
99
100
  if (my_label)
101
    return gtk_label_get_text (GTK_LABEL (my_label));
102
103
  return NULL;
104
}
105
106
static gboolean
107
accel_find_func (GtkAccelKey *key,
108
		 GClosure    *closure,
109
		 gpointer     data)
110
{
111
  return (GClosure *) data == closure;
112
}
113
114
115
/*
116
 * CarbonMenu functions
117
 */
118
119
typedef struct
120
{
121
  MenuRef menu;
122
} CarbonMenu;
123
124
static GQuark carbon_menu_quark = 0;
125
126
static CarbonMenu *
127
carbon_menu_new (void)
128
{
129
  return g_slice_new0 (CarbonMenu);
130
}
131
132
static void
133
carbon_menu_free (CarbonMenu *menu)
134
{
135
  g_slice_free (CarbonMenu, menu);
136
}
137
138
static CarbonMenu *
139
carbon_menu_get (GtkWidget *widget)
140
{
141
  return g_object_get_qdata (G_OBJECT (widget), carbon_menu_quark);
142
}
143
144
static void
145
carbon_menu_connect (GtkWidget *menu,
146
		     MenuRef    menuRef)
147
{
148
  CarbonMenu *carbon_menu = carbon_menu_get (menu);
149
150
  if (!carbon_menu)
151
    {
152
      carbon_menu = carbon_menu_new ();
153
154
      g_object_set_qdata_full (G_OBJECT (menu), carbon_menu_quark,
155
			       carbon_menu,
156
			       (GDestroyNotify) carbon_menu_free);
157
    }
158
159
  carbon_menu->menu = menuRef;
160
}
161
162
163
/*
164
 * CarbonMenuItem functions
165
 */
166
167
typedef struct
168
{
169
  MenuRef        menu;
170
  MenuItemIndex  index;
171
  MenuRef        submenu;
172
  GClosure      *accel_closure;
173
} CarbonMenuItem;
174
175
static GQuark carbon_menu_item_quark = 0;
176
177
static CarbonMenuItem *
178
carbon_menu_item_new (void)
179
{
180
  return g_slice_new0 (CarbonMenuItem);
181
}
182
183
static void
184
carbon_menu_item_free (CarbonMenuItem *menu_item)
185
{
186
  if (menu_item->accel_closure)
187
    g_closure_unref (menu_item->accel_closure);
188
189
  g_slice_free (CarbonMenuItem, menu_item);
190
}
191
192
static CarbonMenuItem *
193
carbon_menu_item_get (GtkWidget *widget)
194
{
195
  return g_object_get_qdata (G_OBJECT (widget), carbon_menu_item_quark);
196
}
197
198
static void
199
carbon_menu_item_update_state (CarbonMenuItem *carbon_item,
200
			       GtkWidget      *widget)
201
{
202
  gboolean sensitive;
203
  gboolean visible;
204
  UInt32   set_attrs = 0;
205
  UInt32   clear_attrs = 0;
206
207
  g_object_get (widget,
208
                "sensitive", &sensitive,
209
                "visible",   &visible,
210
                NULL);
211
212
  if (!sensitive)
213
    set_attrs |= kMenuItemAttrDisabled;
214
  else
215
    clear_attrs |= kMenuItemAttrDisabled;
216
217
  if (!visible)
218
    set_attrs |= kMenuItemAttrHidden;
219
  else
220
    clear_attrs |= kMenuItemAttrHidden;
221
222
  ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
223
                            set_attrs, clear_attrs);
224
}
225
226
static void
227
carbon_menu_item_update_active (CarbonMenuItem *carbon_item,
228
				GtkWidget      *widget)
229
{
230
  gboolean active;
231
232
  g_object_get (widget,
233
                "active", &active,
234
                NULL);
235
236
  CheckMenuItem (carbon_item->menu, carbon_item->index,
237
		 active);
238
}
239
240
static void
241
carbon_menu_item_update_submenu (CarbonMenuItem *carbon_item,
242
				 GtkWidget      *widget)
243
{
244
  GtkWidget *submenu;
245
246
  submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
247
248
  if (submenu)
249
    {
250
      const gchar *label_text;
251
      CFStringRef  cfstr = NULL;
252
253
      label_text = get_menu_label_text (widget, NULL);
254
      if (label_text)
255
        cfstr = CFStringCreateWithCString (NULL, label_text,
256
					   kCFStringEncodingUTF8);
257
258
      CreateNewMenu (0, 0, &carbon_item->submenu);
259
      SetMenuTitleWithCFString (carbon_item->submenu, cfstr);
260
      SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
261
				   carbon_item->submenu);
262
263
      sync_menu_shell (GTK_MENU_SHELL (submenu), carbon_item->submenu, FALSE, FALSE);
264
265
      if (cfstr)
266
	CFRelease (cfstr);
267
    }
268
  else
269
    {
270
      SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
271
				   NULL);
272
      carbon_item->submenu = NULL;
273
    }
274
}
275
276
static void
277
carbon_menu_item_update_label (CarbonMenuItem *carbon_item,
278
			       GtkWidget      *widget)
279
{
280
  const gchar *label_text;
281
  CFStringRef  cfstr = NULL;
282
283
  label_text = get_menu_label_text (widget, NULL);
284
  if (label_text)
285
    cfstr = CFStringCreateWithCString (NULL, label_text,
286
				       kCFStringEncodingUTF8);
287
288
  SetMenuItemTextWithCFString (carbon_item->menu, carbon_item->index,
289
			       cfstr);
290
291
  if (cfstr)
292
    CFRelease (cfstr);
293
}
294
295
static void
296
carbon_menu_item_update_accelerator (CarbonMenuItem *carbon_item,
297
				     GtkWidget      *widget)
298
{
299
  GtkWidget *label;
300
301
  get_menu_label_text (widget, &label);
302
303
  if (GTK_IS_ACCEL_LABEL (label) &&
304
      GTK_ACCEL_LABEL (label)->accel_closure)
305
    {
306
      GtkAccelKey *key;
307
308
      key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group,
309
				  accel_find_func,
310
				  GTK_ACCEL_LABEL (label)->accel_closure);
311
312
      if (key            &&
313
	  key->accel_key &&
314
	  key->accel_flags & GTK_ACCEL_VISIBLE)
315
	{
316
	  GdkDisplay      *display = gtk_widget_get_display (widget);
317
	  GdkKeymap       *keymap  = gdk_keymap_get_for_display (display);
318
	  GdkKeymapKey    *keys;
319
	  gint             n_keys;
320
321
	  if (gdk_keymap_get_entries_for_keyval (keymap, key->accel_key,
322
						 &keys, &n_keys))
323
	    {
324
	      UInt8 modifiers = 0;
325
326
	      SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
327
				     true, keys[0].keycode);
328
329
	      g_free (keys);
330
331
	      if (key->accel_mods)
332
		{
333
		  if (key->accel_mods & GDK_SHIFT_MASK)
334
		    modifiers |= kMenuShiftModifier;
335
336
		  if (key->accel_mods & GDK_MOD1_MASK)
337
		    modifiers |= kMenuOptionModifier;
338
		}
339
340
	      if (!(key->accel_mods & GDK_CONTROL_MASK))
341
		{
342
		  modifiers |= kMenuNoCommandModifier;
343
		}
344
345
	      SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
346
				    modifiers);
347
348
	      return;
349
	    }
350
	}
351
    }
352
353
  /*  otherwise, clear the menu shortcut  */
354
  SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
355
			kMenuNoModifiers | kMenuNoCommandModifier);
356
  ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
357
			    0, kMenuItemAttrUseVirtualKey);
358
  SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
359
			 false, 0);
360
}
361
362
static void
363
carbon_menu_item_accel_changed (GtkAccelGroup   *accel_group,
364
				guint            keyval,
365
				GdkModifierType  modifier,
366
				GClosure        *accel_closure,
367
				GtkWidget       *widget)
368
{
369
  CarbonMenuItem *carbon_item = carbon_menu_item_get (widget);
370
  GtkWidget      *label;
371
372
  get_menu_label_text (widget, &label);
373
374
  if (GTK_IS_ACCEL_LABEL (label) &&
375
      GTK_ACCEL_LABEL (label)->accel_closure == accel_closure)
376
    carbon_menu_item_update_accelerator (carbon_item, widget);
377
}
378
379
static void
380
carbon_menu_item_update_accel_closure (CarbonMenuItem *carbon_item,
381
				       GtkWidget      *widget)
382
{
383
  GtkAccelGroup *group;
384
  GtkWidget     *label;
385
386
  get_menu_label_text (widget, &label);
387
388
  if (carbon_item->accel_closure)
389
    {
390
      group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
391
392
      g_signal_handlers_disconnect_by_func (group,
393
					    carbon_menu_item_accel_changed,
394
					    widget);
395
396
      g_closure_unref (carbon_item->accel_closure);
397
      carbon_item->accel_closure = NULL;
398
    }
399
400
  if (GTK_IS_ACCEL_LABEL (label))
401
    carbon_item->accel_closure = GTK_ACCEL_LABEL (label)->accel_closure;
402
403
  if (carbon_item->accel_closure)
404
    {
405
      g_closure_ref (carbon_item->accel_closure);
406
407
      group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
408
409
      g_signal_connect_object (group, "accel-changed",
410
			       G_CALLBACK (carbon_menu_item_accel_changed),
411
			       widget, 0);
412
    }
413
414
  carbon_menu_item_update_accelerator (carbon_item, widget);
415
}
416
417
static void
418
carbon_menu_item_notify (GObject        *object,
419
			 GParamSpec     *pspec,
420
			 CarbonMenuItem *carbon_item)
421
{
422
  if (!strcmp (pspec->name, "sensitive") ||
423
      !strcmp (pspec->name, "visible"))
424
    {
425
      carbon_menu_item_update_state (carbon_item, GTK_WIDGET (object));
426
    }
427
  else if (!strcmp (pspec->name, "active"))
428
    {
429
      carbon_menu_item_update_active (carbon_item, GTK_WIDGET (object));
430
    }
431
  else if (!strcmp (pspec->name, "submenu"))
432
    {
433
      carbon_menu_item_update_submenu (carbon_item, GTK_WIDGET (object));
434
    }
435
}
436
437
static void
438
carbon_menu_item_notify_label (GObject    *object,
439
			       GParamSpec *pspec,
440
			       gpointer    data)
441
{
442
  CarbonMenuItem *carbon_item = carbon_menu_item_get (GTK_WIDGET (object));
443
444
  if (!strcmp (pspec->name, "label"))
445
    {
446
      carbon_menu_item_update_label (carbon_item,
447
				     GTK_WIDGET (object));
448
    }
449
  else if (!strcmp (pspec->name, "accel-closure"))
450
    {
451
      carbon_menu_item_update_accel_closure (carbon_item,
452
					     GTK_WIDGET (object));
453
    }
454
}
455
456
static CarbonMenuItem *
457
carbon_menu_item_connect (GtkWidget     *menu_item,
458
			  GtkWidget     *label,
459
			  MenuRef        menu,
460
			  MenuItemIndex  index)
461
{
462
  CarbonMenuItem *carbon_item = carbon_menu_item_get (menu_item);
463
464
  if (!carbon_item)
465
    {
466
      carbon_item = carbon_menu_item_new ();
467
468
      g_object_set_qdata_full (G_OBJECT (menu_item), carbon_menu_item_quark,
469
			       carbon_item,
470
			       (GDestroyNotify) carbon_menu_item_free);
471
472
      g_signal_connect (menu_item, "notify",
473
                        G_CALLBACK (carbon_menu_item_notify),
474
                        carbon_item);
475
476
      if (label)
477
	g_signal_connect_swapped (label, "notify::label",
478
				  G_CALLBACK (carbon_menu_item_notify_label),
479
				  menu_item);
480
    }
481
482
  carbon_item->menu  = menu;
483
  carbon_item->index = index;
484
485
  return carbon_item;
486
}
487
488
489
/*
490
 * carbon event handler
491
 */
492
493
static OSStatus
494
menu_event_handler_func (EventHandlerCallRef  event_handler_call_ref,
495
			 EventRef             event_ref,
496
			 void                *data)
497
{
498
  UInt32  event_class = GetEventClass (event_ref);
499
  UInt32  event_kind = GetEventKind (event_ref);
500
  MenuRef menu_ref;
501
502
  switch (event_class)
503
    {
504
    case kEventClassCommand:
505
      /* This is called when activating (is that the right GTK+ term?)
506
       * a menu item.
507
       */
508
      if (event_kind == kEventCommandProcess)
509
	{
510
	  HICommand command;
511
	  OSStatus  err;
512
513
	  /*g_printerr ("Menu: kEventClassCommand/kEventCommandProcess\n");*/
514
515
	  err = GetEventParameter (event_ref, kEventParamDirectObject,
516
				   typeHICommand, 0,
517
				   sizeof (command), 0, &command);
518
519
	  if (err == noErr)
520
	    {
521
	      GtkWidget *widget = NULL;
522
523
	      /* Get any GtkWidget associated with the item. */
524
	      err = GetMenuItemProperty (command.menu.menuRef,
525
					 command.menu.menuItemIndex,
526
					 IGE_QUARTZ_MENU_CREATOR,
527
					 IGE_QUARTZ_ITEM_WIDGET,
528
					 sizeof (widget), 0, &widget);
529
	      if (err == noErr && GTK_IS_WIDGET (widget))
530
		{
531
		  gtk_menu_item_activate (GTK_MENU_ITEM (widget));
532
		  return noErr;
533
		}
534
	    }
535
	}
536
      break;
537
538
    case kEventClassMenu:
539
      GetEventParameter (event_ref,
540
			 kEventParamDirectObject,
541
			 typeMenuRef,
542
			 NULL,
543
			 sizeof (menu_ref),
544
			 NULL,
545
			 &menu_ref);
546
547
      switch (event_kind)
548
	{
549
	case kEventMenuTargetItem:
550
	  /* This is called when an item is selected (what is the
551
	   * GTK+ term? prelight?)
552
	   */
553
	  /*g_printerr ("kEventClassMenu/kEventMenuTargetItem\n");*/
554
	  break;
555
556
	case kEventMenuOpening:
557
	  /* Is it possible to dynamically build the menu here? We
558
	   * can at least set visibility/sensitivity.
559
	   */
560
	  /*g_printerr ("kEventClassMenu/kEventMenuOpening\n");*/
561
	  break;
562
563
	case kEventMenuClosed:
564
	  /*g_printerr ("kEventClassMenu/kEventMenuClosed\n");*/
565
	  break;
566
567
	default:
568
	  break;
569
	}
570
571
      break;
572
573
    default:
574
      break;
575
    }
576
577
  return CallNextEventHandler (event_handler_call_ref, event_ref);
578
}
579
580
static void
581
setup_menu_event_handler (void)
582
{
583
  EventHandlerUPP menu_event_handler_upp;
584
  EventHandlerRef menu_event_handler_ref;
585
  const EventTypeSpec menu_events[] = {
586
    { kEventClassCommand, kEventCommandProcess },
587
    { kEventClassMenu, kEventMenuTargetItem },
588
    { kEventClassMenu, kEventMenuOpening },
589
    { kEventClassMenu, kEventMenuClosed }
590
  };
591
592
  /* FIXME: We might have to install one per window? */
593
594
  menu_event_handler_upp = NewEventHandlerUPP (menu_event_handler_func);
595
  InstallEventHandler (GetApplicationEventTarget (), menu_event_handler_upp,
596
		       GetEventTypeCount (menu_events), menu_events, 0,
597
		       &menu_event_handler_ref);
598
599
#if 0
600
  /* FIXME: Remove the handler with: */
601
  RemoveEventHandler(menu_event_handler_ref);
602
  DisposeEventHandlerUPP(menu_event_handler_upp);
603
#endif
604
}
605
606
static void
607
sync_menu_shell (GtkMenuShell *menu_shell,
608
                 MenuRef       carbon_menu,
609
		 gboolean      toplevel,
610
		 gboolean      debug)
611
{
612
  GList         *children;
613
  GList         *l;
614
  MenuItemIndex  carbon_index = 1;
615
616
  if (debug)
617
    g_printerr ("%s: syncing shell %p\n", G_STRFUNC, menu_shell);
618
619
  carbon_menu_connect (GTK_WIDGET (menu_shell), carbon_menu);
620
621
  children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
622
623
  for (l = children; l; l = l->next)
624
    {
625
      GtkWidget      *menu_item = l->data;
626
      CarbonMenuItem *carbon_item;
627
628
      if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
629
	continue;
630
631
      if (toplevel && g_object_get_data (G_OBJECT (menu_item),
632
					 "gtk-empty-menu-item"))
633
	continue;
634
635
      carbon_item = carbon_menu_item_get (menu_item);
636
637
      if (debug)
638
	g_printerr ("%s: carbon_item %d for menu_item %d (%s, %s)\n",
639
		    G_STRFUNC, carbon_item ? carbon_item->index : -1,
640
		    carbon_index, get_menu_label_text (menu_item, NULL),
641
		    g_type_name (G_TYPE_FROM_INSTANCE (menu_item)));
642
643
      if (carbon_item && carbon_item->index != carbon_index)
644
	{
645
	  if (debug)
646
	    g_printerr ("%s:   -> not matching, deleting\n", G_STRFUNC);
647
648
	  DeleteMenuItem (carbon_item->menu, carbon_index);
649
	  carbon_item = NULL;
650
	}
651
652
      if (!carbon_item)
653
	{
654
	  GtkWidget          *label      = NULL;
655
	  const gchar        *label_text;
656
	  CFStringRef         cfstr      = NULL;
657
	  MenuItemAttributes  attributes = 0;
658
659
	  if (debug)
660
	    g_printerr ("%s:   -> creating new\n", G_STRFUNC);
661
662
	  label_text = get_menu_label_text (menu_item, &label);
663
	  if (label_text)
664
	    cfstr = CFStringCreateWithCString (NULL, label_text,
665
					       kCFStringEncodingUTF8);
666
667
	  if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
668
	    attributes |= kMenuItemAttrSeparator;
669
9020 by JazzyNico
Code refactoring and merging with trunk (revision 10599).
670
	  if (!gtk_widget_is_sensitive (menu_item))
4454 by jiho-sf
Added part of a patch by Adam Strzelecki which makes GTK menu migrate to OS X menubar when GTK+quartz is used. This is taken from Gimp and should be safe enough at this stage since it really only affects OS X.
671
	    attributes |= kMenuItemAttrDisabled;
672
9020 by JazzyNico
Code refactoring and merging with trunk (revision 10599).
673
	  if (!gtk_widget_get_visible (menu_item))
4454 by jiho-sf
Added part of a patch by Adam Strzelecki which makes GTK menu migrate to OS X menubar when GTK+quartz is used. This is taken from Gimp and should be safe enough at this stage since it really only affects OS X.
674
	    attributes |= kMenuItemAttrHidden;
675
676
	  InsertMenuItemTextWithCFString (carbon_menu, cfstr,
677
					  carbon_index - 1,
678
					  attributes, 0);
679
	  SetMenuItemProperty (carbon_menu, carbon_index,
680
			       IGE_QUARTZ_MENU_CREATOR,
681
			       IGE_QUARTZ_ITEM_WIDGET,
682
			       sizeof (menu_item), &menu_item);
683
684
	  if (cfstr)
685
	    CFRelease (cfstr);
686
687
	  carbon_item = carbon_menu_item_connect (menu_item, label,
688
						  carbon_menu,
689
						  carbon_index);
690
691
	  if (GTK_IS_CHECK_MENU_ITEM (menu_item))
692
	    carbon_menu_item_update_active (carbon_item, menu_item);
693
694
	  carbon_menu_item_update_accel_closure (carbon_item, menu_item);
695
696
	  if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
697
	    carbon_menu_item_update_submenu (carbon_item, menu_item);
698
	}
699
700
      carbon_index++;
701
    }
702
703
  g_list_free (children);
704
}
705
706
707
static gulong emission_hook_id = 0;
708
709
static gboolean
710
parent_set_emission_hook (GSignalInvocationHint *ihint,
711
			  guint                  n_param_values,
712
			  const GValue          *param_values,
713
			  gpointer               data)
714
{
715
  GtkWidget *instance = g_value_get_object (param_values);
716
717
  if (GTK_IS_MENU_ITEM (instance))
718
    {
719
      GtkWidget *previous_parent = g_value_get_object (param_values + 1);
720
      GtkWidget *menu_shell      = NULL;
721
722
      if (GTK_IS_MENU_SHELL (previous_parent))
723
	{
724
	  menu_shell = previous_parent;
725
        }
726
      else if (GTK_IS_MENU_SHELL (instance->parent))
727
	{
728
	  menu_shell = instance->parent;
729
	}
730
731
      if (menu_shell)
732
        {
733
	  CarbonMenu *carbon_menu = carbon_menu_get (menu_shell);
734
735
	  if (carbon_menu)
736
	    {
737
#if 0
738
	      g_printerr ("%s: item %s %p (%s, %s)\n", G_STRFUNC,
739
			  previous_parent ? "removed from" : "added to",
740
			  menu_shell,
741
			  get_menu_label_text (instance, NULL),
742
			  g_type_name (G_TYPE_FROM_INSTANCE (instance)));
743
#endif
744
745
	      sync_menu_shell (GTK_MENU_SHELL (menu_shell),
746
			       carbon_menu->menu,
747
			       carbon_menu->menu == (MenuRef) data,
748
			       FALSE);
749
	    }
750
        }
751
    }
752
753
  return TRUE;
754
}
755
756
static void
757
parent_set_emission_hook_remove (GtkWidget *widget,
758
				 gpointer   data)
759
{
760
  g_signal_remove_emission_hook (g_signal_lookup ("parent-set",
761
						  GTK_TYPE_WIDGET),
762
				 emission_hook_id);
763
}
764
765
766
/*
767
 * public functions
768
 */
769
770
void
771
ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell)
772
{
773
  MenuRef carbon_menubar;
774
  guint   hook_id;
775
776
  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
777
778
  if (carbon_menu_quark == 0)
779
    carbon_menu_quark = g_quark_from_static_string ("CarbonMenu");
780
781
  if (carbon_menu_item_quark == 0)
782
    carbon_menu_item_quark = g_quark_from_static_string ("CarbonMenuItem");
783
784
  CreateNewMenu (0 /*id*/, 0 /*options*/, &carbon_menubar);
785
  SetRootMenu (carbon_menubar);
786
787
  setup_menu_event_handler ();
788
789
  emission_hook_id =
790
    g_signal_add_emission_hook (g_signal_lookup ("parent-set",
791
						 GTK_TYPE_WIDGET),
792
				0,
793
				parent_set_emission_hook,
794
				carbon_menubar, NULL);
795
796
  g_signal_connect (menu_shell, "destroy",
797
		    G_CALLBACK (parent_set_emission_hook_remove),
798
		    NULL);
799
800
  sync_menu_shell (menu_shell, carbon_menubar, TRUE, FALSE);
801
}
802
803
void
804
ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item)
805
{
806
  MenuRef       appmenu;
807
  MenuItemIndex index;
808
809
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
810
811
  if (GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
812
                                   &appmenu, &index) == noErr)
813
    {
814
      SetMenuItemCommandID (appmenu, index, 0);
815
      SetMenuItemProperty (appmenu, index,
816
                           IGE_QUARTZ_MENU_CREATOR,
817
                           IGE_QUARTZ_ITEM_WIDGET,
818
                           sizeof (menu_item), &menu_item);
819
820
      gtk_widget_hide (GTK_WIDGET (menu_item));
821
    }
822
}
823
824
825
struct _IgeMacMenuGroup
826
{
827
  GList *items;
828
};
829
830
static GList *app_menu_groups = NULL;
831
832
IgeMacMenuGroup *
833
ige_mac_menu_add_app_menu_group (void)
834
{
835
  IgeMacMenuGroup *group = g_slice_new0 (IgeMacMenuGroup);
836
837
  app_menu_groups = g_list_append (app_menu_groups, group);
838
839
  return group;
840
}
841
842
void
843
ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group,
844
				GtkMenuItem     *menu_item,
845
				const gchar     *label)
846
{
847
  MenuRef  appmenu;
848
  GList   *list;
849
  gint     index = 0;
850
851
  g_return_if_fail (group != NULL);
852
  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
853
854
  if (GetIndMenuItemWithCommandID (NULL, kHICommandHide, 1,
855
                                   &appmenu, NULL) != noErr)
856
    {
857
      g_warning ("%s: retrieving app menu failed",
858
		 G_STRFUNC);
859
      return;
860
    }
861
862
  for (list = app_menu_groups; list; list = g_list_next (list))
863
    {
864
      IgeMacMenuGroup *list_group = list->data;
865
866
      index += g_list_length (list_group->items);
867
868
      /*  adjust index for the separator between groups, but not
869
       *  before the first group
870
       */
871
      if (list_group->items && list->prev)
872
	index++;
873
874
      if (group == list_group)
875
	{
876
	  CFStringRef cfstr;
877
878
	  /*  add a separator before adding the first item, but not
879
	   *  for the first group
880
	   */
881
	  if (!group->items && list->prev)
882
	    {
883
	      InsertMenuItemTextWithCFString (appmenu, NULL, index,
884
					      kMenuItemAttrSeparator, 0);
885
	      index++;
886
	    }
887
888
	  if (!label)
889
	    label = get_menu_label_text (GTK_WIDGET (menu_item), NULL);
890
891
	  cfstr = CFStringCreateWithCString (NULL, label,
892
					     kCFStringEncodingUTF8);
893
894
	  InsertMenuItemTextWithCFString (appmenu, cfstr, index, 0, 0);
895
	  SetMenuItemProperty (appmenu, index + 1,
896
			       IGE_QUARTZ_MENU_CREATOR,
897
			       IGE_QUARTZ_ITEM_WIDGET,
898
			       sizeof (menu_item), &menu_item);
899
900
	  CFRelease (cfstr);
901
902
	  gtk_widget_hide (GTK_WIDGET (menu_item));
903
904
	  group->items = g_list_append (group->items, menu_item);
905
906
	  return;
907
	}
908
    }
909
910
  if (!list)
911
    g_warning ("%s: app menu group %p does not exist",
912
	       G_STRFUNC, group);
913
}
914
915
#endif /* GDK_WINDOWING_QUARTZ */