fix up file renaming code a little bit
[ArdourMidi.git] / libs / gtkmm2ext / sync-menu.c
bloba16babce5492bcf89a760087cf357c56d92910bf
1 /* GTK+ Integration for the Mac OS X Menubar.
3 * Copyright (C) 2007 Pioneer Research Center USA, Inc.
4 * Copyright (C) 2007 Imendio AB
6 * For further information, see:
7 * http://developer.imendio.com/projects/gtk-macosx/menubar
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.
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.
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.
25 #include <gtk/gtk.h>
26 #include <gdk/gdkkeysyms.h>
28 #include <Carbon/Carbon.h>
30 #include <gtkmm2ext/sync-menu.h>
33 /* TODO
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...
42 #define IGE_QUARTZ_MENU_CREATOR 'IGEC'
43 #define IGE_QUARTZ_ITEM_WIDGET 'IWID'
46 static void sync_menu_shell (GtkMenuShell *menu_shell,
47 MenuRef carbon_menu,
48 gboolean toplevel,
49 gboolean debug);
53 * utility functions
56 static GtkWidget *
57 find_menu_label (GtkWidget *widget)
59 GtkWidget *label = NULL;
61 if (GTK_IS_LABEL (widget))
62 return widget;
64 if (GTK_IS_CONTAINER (widget))
66 GList *children;
67 GList *l;
69 children = gtk_container_get_children (GTK_CONTAINER (widget));
71 for (l = children; l; l = l->next)
73 label = find_menu_label (l->data);
74 if (label)
75 break;
78 g_list_free (children);
81 return label;
84 static const gchar *
85 get_menu_label_text (GtkWidget *menu_item,
86 GtkWidget **label)
88 GtkWidget *my_label;
90 my_label = find_menu_label (menu_item);
91 if (label)
92 *label = my_label;
94 if (my_label)
95 return gtk_label_get_text (GTK_LABEL (my_label));
97 return NULL;
100 static gboolean
101 accel_find_func (GtkAccelKey *key,
102 GClosure *closure,
103 gpointer data)
105 return (GClosure *) data == closure;
110 * CarbonMenu functions
113 typedef struct
115 MenuRef menu;
116 } CarbonMenu;
118 static GQuark carbon_menu_quark = 0;
120 static CarbonMenu *
121 carbon_menu_new (void)
123 return g_slice_new0 (CarbonMenu);
126 static void
127 carbon_menu_free (CarbonMenu *menu)
129 g_slice_free (CarbonMenu, menu);
132 static CarbonMenu *
133 carbon_menu_get (GtkWidget *widget)
135 return g_object_get_qdata (G_OBJECT (widget), carbon_menu_quark);
138 static void
139 carbon_menu_connect (GtkWidget *menu,
140 MenuRef menuRef)
142 CarbonMenu *carbon_menu = carbon_menu_get (menu);
144 if (!carbon_menu)
146 carbon_menu = carbon_menu_new ();
148 g_object_set_qdata_full (G_OBJECT (menu), carbon_menu_quark,
149 carbon_menu,
150 (GDestroyNotify) carbon_menu_free);
153 carbon_menu->menu = menuRef;
158 * CarbonMenuItem functions
161 typedef struct
163 MenuRef menu;
164 MenuItemIndex index;
165 MenuRef submenu;
166 GClosure *accel_closure;
167 } CarbonMenuItem;
169 static GQuark carbon_menu_item_quark = 0;
171 static CarbonMenuItem *
172 carbon_menu_item_new (void)
174 return g_slice_new0 (CarbonMenuItem);
177 static void
178 carbon_menu_item_free (CarbonMenuItem *menu_item)
180 if (menu_item->accel_closure)
181 g_closure_unref (menu_item->accel_closure);
183 g_slice_free (CarbonMenuItem, menu_item);
186 static CarbonMenuItem *
187 carbon_menu_item_get (GtkWidget *widget)
189 return g_object_get_qdata (G_OBJECT (widget), carbon_menu_item_quark);
192 static void
193 carbon_menu_item_update_state (CarbonMenuItem *carbon_item,
194 GtkWidget *widget)
196 gboolean sensitive;
197 gboolean visible;
198 UInt32 set_attrs = 0;
199 UInt32 clear_attrs = 0;
201 g_object_get (widget,
202 "sensitive", &sensitive,
203 "visible", &visible,
204 NULL);
206 if (!sensitive)
207 set_attrs |= kMenuItemAttrDisabled;
208 else
209 clear_attrs |= kMenuItemAttrDisabled;
211 if (!visible)
212 set_attrs |= kMenuItemAttrHidden;
213 else
214 clear_attrs |= kMenuItemAttrHidden;
216 ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
217 set_attrs, clear_attrs);
220 static void
221 carbon_menu_item_update_active (CarbonMenuItem *carbon_item,
222 GtkWidget *widget)
224 gboolean active;
226 g_object_get (widget,
227 "active", &active,
228 NULL);
230 CheckMenuItem (carbon_item->menu, carbon_item->index,
231 active);
234 static void
235 carbon_menu_item_update_submenu (CarbonMenuItem *carbon_item,
236 GtkWidget *widget)
238 GtkWidget *submenu;
240 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
242 if (submenu)
244 const gchar *label_text;
245 CFStringRef cfstr = NULL;
247 label_text = get_menu_label_text (widget, NULL);
248 if (label_text)
249 cfstr = CFStringCreateWithCString (NULL, label_text,
250 kCFStringEncodingUTF8);
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);
257 sync_menu_shell (GTK_MENU_SHELL (submenu), carbon_item->submenu, FALSE, FALSE);
259 if (cfstr)
260 CFRelease (cfstr);
262 else
264 SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
265 NULL);
266 carbon_item->submenu = NULL;
270 static void
271 carbon_menu_item_update_label (CarbonMenuItem *carbon_item,
272 GtkWidget *widget)
274 const gchar *label_text;
275 CFStringRef cfstr = NULL;
277 label_text = get_menu_label_text (widget, NULL);
278 if (label_text)
279 cfstr = CFStringCreateWithCString (NULL, label_text,
280 kCFStringEncodingUTF8);
282 SetMenuItemTextWithCFString (carbon_item->menu, carbon_item->index,
283 cfstr);
285 if (cfstr)
286 CFRelease (cfstr);
289 static void
290 carbon_menu_item_update_accelerator (CarbonMenuItem *carbon_item,
291 GtkWidget *widget)
293 GtkWidget *label;
295 get_menu_label_text (widget, &label);
297 if (GTK_IS_ACCEL_LABEL (label) &&
298 GTK_ACCEL_LABEL (label)->accel_closure)
300 GtkAccelKey *key;
302 key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group,
303 accel_find_func,
304 GTK_ACCEL_LABEL (label)->accel_closure);
306 if (key &&
307 key->accel_key &&
308 key->accel_flags & GTK_ACCEL_VISIBLE)
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 */
318 if (gdk_keymap_get_entries_for_keyval (keymap, key->accel_key,
319 &keys, &n_keys) == 0)
321 gint realkey = -1;
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;
344 if (realkey != -1) {
345 SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
346 false, realkey);
347 add_modifiers = TRUE;
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;
357 g_free (keys);
358 add_modifiers = TRUE;
361 if (add_modifiers)
363 UInt8 modifiers = 0; /* implies Command key */
365 use_command = 0;
367 if (key->accel_mods)
369 if (key->accel_mods & GDK_SHIFT_MASK) {
370 modifiers |= kMenuShiftModifier;
373 /* gdk/quartz maps Alt/Option to Mod1 */
375 if (key->accel_mods & (GDK_MOD1_MASK)) {
376 modifiers |= kMenuOptionModifier;
379 if (key->accel_mods & GDK_CONTROL_MASK) {
380 modifiers |= kMenuControlModifier;
383 /* gdk/quartz maps Command to Meta */
385 if (key->accel_mods & GDK_META_MASK) {
386 use_command = 1;
390 if (!use_command)
391 modifiers |= kMenuNoCommandModifier;
393 SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
394 modifiers);
396 return;
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);
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)
417 CarbonMenuItem *carbon_item = carbon_menu_item_get (widget);
418 GtkWidget *label;
420 get_menu_label_text (widget, &label);
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);
427 static void
428 carbon_menu_item_update_accel_closure (CarbonMenuItem *carbon_item,
429 GtkWidget *widget)
431 GtkAccelGroup *group;
432 GtkWidget *label;
434 get_menu_label_text (widget, &label);
436 if (carbon_item->accel_closure)
438 group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
440 g_signal_handlers_disconnect_by_func (group,
441 carbon_menu_item_accel_changed,
442 widget);
444 g_closure_unref (carbon_item->accel_closure);
445 carbon_item->accel_closure = NULL;
448 if (GTK_IS_ACCEL_LABEL (label))
449 carbon_item->accel_closure = GTK_ACCEL_LABEL (label)->accel_closure;
451 if (carbon_item->accel_closure)
453 g_closure_ref (carbon_item->accel_closure);
455 group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
457 g_signal_connect_object (group, "accel-changed",
458 G_CALLBACK (carbon_menu_item_accel_changed),
459 widget, 0);
462 carbon_menu_item_update_accelerator (carbon_item, widget);
465 static void
466 carbon_menu_item_notify (GObject *object,
467 GParamSpec *pspec,
468 CarbonMenuItem *carbon_item)
470 if (!strcmp (pspec->name, "sensitive") ||
471 !strcmp (pspec->name, "visible"))
473 carbon_menu_item_update_state (carbon_item, GTK_WIDGET (object));
475 else if (!strcmp (pspec->name, "active"))
477 carbon_menu_item_update_active (carbon_item, GTK_WIDGET (object));
479 else if (!strcmp (pspec->name, "submenu"))
481 carbon_menu_item_update_submenu (carbon_item, GTK_WIDGET (object));
485 static void
486 carbon_menu_item_notify_label (GObject *object,
487 GParamSpec *pspec,
488 gpointer data)
490 CarbonMenuItem *carbon_item = carbon_menu_item_get (GTK_WIDGET (object));
492 if (!strcmp (pspec->name, "label"))
494 carbon_menu_item_update_label (carbon_item,
495 GTK_WIDGET (object));
497 else if (!strcmp (pspec->name, "accel-closure"))
499 carbon_menu_item_update_accel_closure (carbon_item,
500 GTK_WIDGET (object));
504 static CarbonMenuItem *
505 carbon_menu_item_connect (GtkWidget *menu_item,
506 GtkWidget *label,
507 MenuRef menu,
508 MenuItemIndex index)
510 CarbonMenuItem *carbon_item = carbon_menu_item_get (menu_item);
512 if (!carbon_item)
514 carbon_item = carbon_menu_item_new ();
516 g_object_set_qdata_full (G_OBJECT (menu_item), carbon_menu_item_quark,
517 carbon_item,
518 (GDestroyNotify) carbon_menu_item_free);
520 g_signal_connect (menu_item, "notify",
521 G_CALLBACK (carbon_menu_item_notify),
522 carbon_item);
524 if (label)
525 g_signal_connect_swapped (label, "notify::label",
526 G_CALLBACK (carbon_menu_item_notify_label),
527 menu_item);
530 carbon_item->menu = menu;
531 carbon_item->index = index;
533 return carbon_item;
538 * carbon event handler
541 static int _in_carbon_menu_event_handler = 0;
543 int
544 gdk_quartz_in_carbon_menu_event_handler ()
546 return _in_carbon_menu_event_handler;
549 static gboolean
550 dummy_gtk_menu_item_activate (gpointer *arg)
552 gtk_menu_item_activate (GTK_MENU_ITEM(arg));
553 return FALSE;
556 static OSStatus
557 menu_event_handler_func (EventHandlerCallRef event_handler_call_ref,
558 EventRef event_ref,
559 void *data)
561 UInt32 event_class = GetEventClass (event_ref);
562 UInt32 event_kind = GetEventKind (event_ref);
563 MenuRef menu_ref;
564 OSStatus ret;
566 _in_carbon_menu_event_handler = 1;
568 switch (event_class)
570 case kEventClassCommand:
571 /* This is called when activating (is that the right GTK+ term?)
572 * a menu item.
574 if (event_kind == kEventCommandProcess)
576 HICommand command;
577 OSStatus err;
579 /*g_printerr ("Menu: kEventClassCommand/kEventCommandProcess\n");*/
581 err = GetEventParameter (event_ref, kEventParamDirectObject,
582 typeHICommand, 0,
583 sizeof (command), 0, &command);
585 if (err == noErr)
587 GtkWidget *widget = NULL;
589 /* Get any GtkWidget associated with the item. */
590 err = GetMenuItemProperty (command.menu.menuRef,
591 command.menu.menuItemIndex,
592 IGE_QUARTZ_MENU_CREATOR,
593 IGE_QUARTZ_ITEM_WIDGET,
594 sizeof (widget), 0, &widget);
595 if (err == noErr && GTK_IS_WIDGET (widget))
597 g_idle_add ((GSourceFunc) dummy_gtk_menu_item_activate, widget);
598 // gtk_menu_item_activate (GTK_MENU_ITEM (widget));
599 _in_carbon_menu_event_handler = 0;
600 return noErr;
604 break;
606 case kEventClassMenu:
607 GetEventParameter (event_ref,
608 kEventParamDirectObject,
609 typeMenuRef,
610 NULL,
611 sizeof (menu_ref),
612 NULL,
613 &menu_ref);
615 switch (event_kind)
617 case kEventMenuTargetItem:
618 /* This is called when an item is selected (what is the
619 * GTK+ term? prelight?)
621 /*g_printerr ("kEventClassMenu/kEventMenuTargetItem\n");*/
622 break;
624 case kEventMenuOpening:
625 /* Is it possible to dynamically build the menu here? We
626 * can at least set visibility/sensitivity.
628 /*g_printerr ("kEventClassMenu/kEventMenuOpening\n");*/
629 break;
631 case kEventMenuClosed:
632 /*g_printerr ("kEventClassMenu/kEventMenuClosed\n");*/
633 break;
635 default:
636 break;
639 break;
641 default:
642 break;
645 ret = CallNextEventHandler (event_handler_call_ref, event_ref);
646 _in_carbon_menu_event_handler = 0;
647 return ret;
650 static void
651 setup_menu_event_handler (void)
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 }
662 /* FIXME: We might have to install one per window? */
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);
669 #if 0
670 /* FIXME: Remove the handler with: */
671 RemoveEventHandler(menu_event_handler_ref);
672 DisposeEventHandlerUPP(menu_event_handler_upp);
673 #endif
676 static void
677 sync_menu_shell (GtkMenuShell *menu_shell,
678 MenuRef carbon_menu,
679 gboolean toplevel,
680 gboolean debug)
682 GList *children;
683 GList *l;
684 MenuItemIndex carbon_index = 1;
686 if (debug)
687 g_printerr ("%s: syncing shell %p\n", G_STRFUNC, menu_shell);
689 carbon_menu_connect (GTK_WIDGET (menu_shell), carbon_menu);
691 children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
693 for (l = children; l; l = l->next)
695 GtkWidget *menu_item = l->data;
696 CarbonMenuItem *carbon_item;
698 if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
699 continue;
701 if (toplevel && g_object_get_data (G_OBJECT (menu_item),
702 "gtk-empty-menu-item"))
703 continue;
705 carbon_item = carbon_menu_item_get (menu_item);
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)));
713 if (carbon_item && carbon_item->index != carbon_index)
715 if (debug)
716 g_printerr ("%s: -> not matching, deleting\n", G_STRFUNC);
718 DeleteMenuItem (carbon_item->menu, carbon_index);
719 carbon_item = NULL;
722 if (!carbon_item)
724 GtkWidget *label = NULL;
725 const gchar *label_text;
726 CFStringRef cfstr = NULL;
727 MenuItemAttributes attributes = 0;
729 if (debug)
730 g_printerr ("%s: -> creating new\n", G_STRFUNC);
732 label_text = get_menu_label_text (menu_item, &label);
733 if (label_text)
734 cfstr = CFStringCreateWithCString (NULL, label_text,
735 kCFStringEncodingUTF8);
737 if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
738 attributes |= kMenuItemAttrSeparator;
740 if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
741 attributes |= kMenuItemAttrDisabled;
743 if (!GTK_WIDGET_VISIBLE (menu_item))
744 attributes |= kMenuItemAttrHidden;
746 InsertMenuItemTextWithCFString (carbon_menu, cfstr,
747 carbon_index - 1,
748 attributes, 0);
749 SetMenuItemProperty (carbon_menu, carbon_index,
750 IGE_QUARTZ_MENU_CREATOR,
751 IGE_QUARTZ_ITEM_WIDGET,
752 sizeof (menu_item), &menu_item);
754 if (cfstr)
755 CFRelease (cfstr);
757 carbon_item = carbon_menu_item_connect (menu_item, label,
758 carbon_menu,
759 carbon_index);
761 if (GTK_IS_CHECK_MENU_ITEM (menu_item))
762 carbon_menu_item_update_active (carbon_item, menu_item);
764 carbon_menu_item_update_accel_closure (carbon_item, menu_item);
766 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
767 carbon_menu_item_update_submenu (carbon_item, menu_item);
770 carbon_index++;
773 g_list_free (children);
777 static gulong emission_hook_id = 0;
779 static gboolean
780 parent_set_emission_hook (GSignalInvocationHint *ihint,
781 guint n_param_values,
782 const GValue *param_values,
783 gpointer data)
785 GtkWidget *instance = g_value_get_object (param_values);
787 if (GTK_IS_MENU_ITEM (instance))
789 GtkWidget *previous_parent = g_value_get_object (param_values + 1);
790 GtkWidget *menu_shell = NULL;
792 if (GTK_IS_MENU_SHELL (previous_parent))
794 menu_shell = previous_parent;
796 else if (GTK_IS_MENU_SHELL (instance->parent))
798 menu_shell = instance->parent;
801 if (menu_shell)
803 CarbonMenu *carbon_menu = carbon_menu_get (menu_shell);
805 if (carbon_menu)
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
815 sync_menu_shell (GTK_MENU_SHELL (menu_shell),
816 carbon_menu->menu,
817 carbon_menu->menu == (MenuRef) data,
818 FALSE);
823 return TRUE;
826 static void
827 parent_set_emission_hook_remove (GtkWidget *widget,
828 gpointer data)
830 g_signal_remove_emission_hook (g_signal_lookup ("parent-set",
831 GTK_TYPE_WIDGET),
832 emission_hook_id);
837 * public functions
840 void
841 ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell)
843 MenuRef carbon_menubar;
845 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
847 if (carbon_menu_quark == 0)
848 carbon_menu_quark = g_quark_from_static_string ("CarbonMenu");
850 if (carbon_menu_item_quark == 0)
851 carbon_menu_item_quark = g_quark_from_static_string ("CarbonMenuItem");
853 CreateNewMenu (0 /*id*/, 0 /*options*/, &carbon_menubar);
854 SetRootMenu (carbon_menubar);
856 setup_menu_event_handler ();
858 emission_hook_id =
859 g_signal_add_emission_hook (g_signal_lookup ("parent-set",
860 GTK_TYPE_WIDGET),
862 parent_set_emission_hook,
863 carbon_menubar, NULL);
865 g_signal_connect (menu_shell, "destroy",
866 G_CALLBACK (parent_set_emission_hook_remove),
867 NULL);
869 sync_menu_shell (menu_shell, carbon_menubar, TRUE, FALSE);
872 void
873 ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item)
875 MenuRef appmenu;
876 MenuItemIndex index;
878 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
880 if (GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
881 &appmenu, &index) == noErr)
883 SetMenuItemCommandID (appmenu, index, 0);
884 SetMenuItemProperty (appmenu, index,
885 IGE_QUARTZ_MENU_CREATOR,
886 IGE_QUARTZ_ITEM_WIDGET,
887 sizeof (menu_item), &menu_item);
889 gtk_widget_hide (GTK_WIDGET (menu_item));
894 struct _IgeMacMenuGroup
896 GList *items;
899 static GList *app_menu_groups = NULL;
901 IgeMacMenuGroup *
902 ige_mac_menu_add_app_menu_group (void)
904 IgeMacMenuGroup *group = g_slice_new0 (IgeMacMenuGroup);
906 app_menu_groups = g_list_append (app_menu_groups, group);
908 return group;
911 void
912 ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group,
913 GtkMenuItem *menu_item,
914 const gchar *label)
916 MenuRef appmenu;
917 GList *list;
918 gint index = 0;
920 g_return_if_fail (group != NULL);
921 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
923 if (GetIndMenuItemWithCommandID (NULL, kHICommandHide, 1,
924 &appmenu, NULL) != noErr)
926 g_warning ("%s: retrieving app menu failed",
927 G_STRFUNC);
928 return;
931 for (list = app_menu_groups; list; list = g_list_next (list))
933 IgeMacMenuGroup *list_group = list->data;
935 index += g_list_length (list_group->items);
937 /* adjust index for the separator between groups, but not
938 * before the first group
940 if (list_group->items && list->prev)
941 index++;
943 if (group == list_group)
945 CFStringRef cfstr;
947 /* add a separator before adding the first item, but not
948 * for the first group
950 if (!group->items && list->prev)
952 InsertMenuItemTextWithCFString (appmenu, NULL, index,
953 kMenuItemAttrSeparator, 0);
954 index++;
957 if (!label)
958 label = get_menu_label_text (GTK_WIDGET (menu_item), NULL);
960 cfstr = CFStringCreateWithCString (NULL, label,
961 kCFStringEncodingUTF8);
963 InsertMenuItemTextWithCFString (appmenu, cfstr, index, 0, 0);
964 SetMenuItemProperty (appmenu, index + 1,
965 IGE_QUARTZ_MENU_CREATOR,
966 IGE_QUARTZ_ITEM_WIDGET,
967 sizeof (menu_item), &menu_item);
969 CFRelease (cfstr);
971 gtk_widget_hide (GTK_WIDGET (menu_item));
973 group->items = g_list_append (group->items, menu_item);
975 return;
979 if (!list)
980 g_warning ("%s: app menu group %p does not exist",
981 G_STRFUNC, group);