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