1 /* GTK+ application-level integration for the Mac OS X/Cocoa
3 * Copyright (C) 2007 Pioneer Research Center USA, Inc.
4 * Copyright (C) 2007 Imendio AB
5 * Copyright (C) 2009 Paul Davis
7 * This is a reimplementation in Cocoa of the sync-menu.c concept
8 * from Imendio, although without the "set quit menu" API since
9 * a Cocoa app needs to handle termination anyway.
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; version 2.1
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
27 #import <AppKit/NSMenu.h>
28 #import <AppKit/NSMenuItem.h>
29 #import <AppKit/NSCell.h>
30 #import <AppKit/NSEvent.h>
31 #import <AppKit/NSApplication.h>
32 #import <Foundation/NSString.h>
33 #import <Foundation/NSNotification.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <gtkmm2ext/gtkapplication.h>
39 #include <gtkmm2ext/gtkapplication-private.h>
41 // #define DEBUG(format, ...) g_printerr ("%s: " format, G_STRFUNC, ## __VA_ARGS__)
42 #define DEBUG(format, ...)
46 * - Sync adding/removing/reordering items
47 * - Create on demand? (can this be done with gtk+? ie fill in menu
48 items when the menu is opened)
49 * - Figure out what to do per app/window...
54 gdk_quartz_keyval_to_ns_keyval (guint keyval)
58 return NSBackspaceCharacter;
60 return NSDeleteFunctionKey;
62 return NSPauseFunctionKey;
64 return NSScrollLockFunctionKey;
66 return NSSysReqFunctionKey;
68 return NSHomeFunctionKey;
71 return NSLeftArrowFunctionKey;
74 return NSUpArrowFunctionKey;
77 return NSRightArrowFunctionKey;
80 return NSDownArrowFunctionKey;
82 return NSPageUpFunctionKey;
84 return NSPageDownFunctionKey;
86 return NSEndFunctionKey;
88 return NSBeginFunctionKey;
90 return NSSelectFunctionKey;
92 return NSPrintFunctionKey;
94 return NSExecuteFunctionKey;
96 return NSInsertFunctionKey;
98 return NSUndoFunctionKey;
100 return NSRedoFunctionKey;
102 return NSMenuFunctionKey;
104 return NSFindFunctionKey;
106 return NSHelpFunctionKey;
108 return NSBreakFunctionKey;
109 case GDK_Mode_switch:
110 return NSModeSwitchFunctionKey;
112 return NSF1FunctionKey;
114 return NSF2FunctionKey;
116 return NSF3FunctionKey;
118 return NSF4FunctionKey;
120 return NSF5FunctionKey;
122 return NSF6FunctionKey;
124 return NSF7FunctionKey;
126 return NSF8FunctionKey;
128 return NSF9FunctionKey;
130 return NSF10FunctionKey;
132 return NSF11FunctionKey;
134 return NSF12FunctionKey;
136 return NSF13FunctionKey;
138 return NSF14FunctionKey;
140 return NSF15FunctionKey;
142 return NSF16FunctionKey;
144 return NSF17FunctionKey;
146 return NSF18FunctionKey;
148 return NSF19FunctionKey;
150 return NSF20FunctionKey;
152 return NSF21FunctionKey;
154 return NSF22FunctionKey;
156 return NSF23FunctionKey;
158 return NSF24FunctionKey;
160 return NSF25FunctionKey;
162 return NSF26FunctionKey;
164 return NSF27FunctionKey;
166 return NSF28FunctionKey;
168 return NSF29FunctionKey;
170 return NSF30FunctionKey;
172 return NSF31FunctionKey;
174 return NSF32FunctionKey;
176 return NSF33FunctionKey;
178 return NSF34FunctionKey;
180 return NSF35FunctionKey;
189 keyval_is_keypad (guint keyval)
202 case GDK_KP_Page_Down:
208 case GDK_KP_Multiply:
210 case GDK_KP_Separator:
211 case GDK_KP_Subtract:
233 keyval_keypad_nonkeypad_equivalent (guint keyval)
256 case GDK_KP_Page_Down:
257 return GDK_Page_Down;
268 case GDK_KP_Multiply:
272 case GDK_KP_Subtract:
302 return GDK_VoidSymbol;
306 gdk_quartz_keyval_to_string (guint keyval)
453 case GDK_bracketleft:
457 case GDK_bracketright:
459 case GDK_asciicircum:
480 keyval_is_uppercase (guint keyval)
516 /* gtk/osx has a problem in that mac main menu events
517 are handled using an "internal" event handling system that
518 doesn't pass things back to the glib/gtk main loop. if we call
519 gtk_main_iteration() block while in a menu event handler, then
520 glib gets confused and thinks there are two threads running
521 g_main_poll_func(). apps call call gdk_quartz_in_menu_event_handler()
522 if they need to check this.
525 static int _in_menu_event_handler = 0;
528 gdk_quartz_in_menu_event_handler ()
530 return _in_menu_event_handler;
534 idle_call_activate (gpointer data)
536 gtk_menu_item_activate ((GtkMenuItem*) data);
540 @interface GNSMenuItem : NSMenuItem
543 GtkMenuItem* gtk_menu_item;
544 GClosure *accel_closure;
546 - (id) initWithTitle:(NSString*) title andGtkWidget:(GtkMenuItem*) w;
547 - (void) activate:(id) sender;
550 @implementation GNSMenuItem
551 - (id) initWithTitle:(NSString*) title andGtkWidget:(GtkMenuItem*) w
553 /* All menu items have the action "activate", which will be handled by this child class
556 self = [ super initWithTitle:title action:@selector(activate:) keyEquivalent:@"" ];
559 /* make this handle its own action */
560 [ self setTarget:self ];
566 - (void) activate:(id) sender
568 g_idle_add (idle_call_activate, gtk_menu_item);
572 static void push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
582 find_menu_label (GtkWidget *widget)
584 GtkWidget *label = NULL;
586 if (GTK_IS_LABEL (widget))
589 if (GTK_IS_CONTAINER (widget))
594 children = gtk_container_get_children (GTK_CONTAINER (widget));
596 for (l = children; l; l = l->next)
598 label = find_menu_label ((GtkWidget*) l->data);
603 g_list_free (children);
610 get_menu_label_text (GtkWidget *menu_item,
615 my_label = find_menu_label (menu_item);
620 return gtk_label_get_text (GTK_LABEL (my_label));
626 accel_find_func (GtkAccelKey *key,
630 return (GClosure *) data == closure;
635 * CocoaMenu functions
638 static GQuark cocoa_menu_quark = 0;
641 cocoa_menu_get (GtkWidget *widget)
643 return (NSMenu*) g_object_get_qdata (G_OBJECT (widget), cocoa_menu_quark);
647 cocoa_menu_free (gpointer *ptr)
649 NSMenu* menu = (NSMenu*) ptr;
654 cocoa_menu_connect (GtkWidget *menu,
659 if (cocoa_menu_quark == 0)
660 cocoa_menu_quark = g_quark_from_static_string ("NSMenu");
662 g_object_set_qdata_full (G_OBJECT (menu), cocoa_menu_quark,
664 (GDestroyNotify) cocoa_menu_free);
668 * NSMenuItem functions
671 static GQuark cocoa_menu_item_quark = 0;
672 static void cocoa_menu_item_connect (GtkWidget* menu_item,
673 GNSMenuItem* cocoa_menu_item,
677 cocoa_menu_item_free (gpointer *ptr)
679 GNSMenuItem* item = (GNSMenuItem*) ptr;
684 cocoa_menu_item_get (GtkWidget *widget)
686 return (GNSMenuItem*) g_object_get_qdata (G_OBJECT (widget), cocoa_menu_item_quark);
690 cocoa_menu_item_update_state (NSMenuItem* cocoa_item,
696 g_object_get (widget,
697 "sensitive", &sensitive,
702 [cocoa_item setEnabled:NO];
704 [cocoa_item setEnabled:YES];
707 // requires OS X 10.5 or later
709 [cocoa_item setHidden:YES];
711 [cocoa_item setHidden:NO];
716 cocoa_menu_item_update_active (NSMenuItem *cocoa_item,
721 g_object_get (widget, "active", &active, NULL);
724 [cocoa_item setState:NSOnState];
726 [cocoa_item setState:NSOffState];
730 cocoa_menu_item_update_submenu (NSMenuItem *cocoa_item,
735 g_return_if_fail (cocoa_item != NULL);
736 g_return_if_fail (widget != NULL);
738 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
742 GtkWidget* label = NULL;
743 const gchar *label_text;
744 NSMenu* cocoa_submenu;
746 label_text = get_menu_label_text (widget, &label);
748 /* create a new nsmenu to hold the GTK menu */
751 cocoa_submenu = [ [ NSMenu alloc ] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]];
753 cocoa_submenu = [ [ NSMenu alloc ] initWithTitle:@""];
755 [cocoa_submenu setAutoenablesItems:NO];
756 cocoa_menu_connect (submenu, cocoa_submenu);
758 /* connect the new nsmenu to the passed-in item (which lives in
760 (Note: this will release any pre-existing version of this submenu)
762 [ cocoa_item setSubmenu:cocoa_submenu];
764 /* and push the GTK menu into the submenu */
765 push_menu_shell_to_nsmenu (GTK_MENU_SHELL (submenu), cocoa_submenu, FALSE, FALSE);
767 [ cocoa_submenu release ];
772 cocoa_menu_item_update_label (NSMenuItem *cocoa_item,
775 const gchar *label_text;
777 g_return_if_fail (cocoa_item != NULL);
778 g_return_if_fail (widget != NULL);
780 label_text = get_menu_label_text (widget, NULL);
782 [cocoa_item setTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]];
784 [cocoa_item setTitle:@""];
788 cocoa_menu_item_update_accelerator (NSMenuItem *cocoa_item,
793 g_return_if_fail (cocoa_item != NULL);
794 g_return_if_fail (widget != NULL);
796 /* important note: this function doesn't do anything to actually change
797 key handling. Its goal is to get Cocoa to display the correct
798 accelerator as part of a menu item. Actual accelerator handling
799 is still done by GTK, so this is more cosmetic than it may
803 const gchar* ltxt = get_menu_label_text (widget, &label);
805 if (GTK_IS_ACCEL_LABEL (label) &&
806 GTK_ACCEL_LABEL (label)->accel_closure)
810 key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group,
812 GTK_ACCEL_LABEL (label)->accel_closure);
816 key->accel_flags & GTK_ACCEL_VISIBLE)
819 const gchar* str = NULL;
820 guint actual_key = key->accel_key;
822 if (keyval_is_keypad (actual_key)) {
823 if ((actual_key = keyval_keypad_nonkeypad_equivalent (actual_key)) == GDK_VoidSymbol) {
824 /* GDK_KP_Separator */
825 [cocoa_item setKeyEquivalent:@""];
828 modifiers |= NSNumericPadKeyMask;
831 /* if we somehow got here with GDK_A ... GDK_Z rather than GDK_a ... GDK_z, then take note
832 of that and make sure we use a shift modifier.
835 if (keyval_is_uppercase (actual_key)) {
836 modifiers |= NSShiftKeyMask;
839 str = gdk_quartz_keyval_to_string (actual_key);
842 unichar ukey = str[0];
843 [cocoa_item setKeyEquivalent:[NSString stringWithCharacters:&ukey length:1]];
845 unichar ukey = gdk_quartz_keyval_to_ns_keyval (actual_key);
847 [cocoa_item setKeyEquivalent:[NSString stringWithCharacters:&ukey length:1]];
849 /* cannot map this key to Cocoa key equivalent */
850 [cocoa_item setKeyEquivalent:@""];
855 if (key->accel_mods || modifiers)
857 if (key->accel_mods & GDK_SHIFT_MASK) {
858 modifiers |= NSShiftKeyMask;
861 /* gdk/quartz maps Alt/Option to Mod1 */
863 if (key->accel_mods & (GDK_MOD1_MASK)) {
864 modifiers |= NSAlternateKeyMask;
867 if (key->accel_mods & GDK_CONTROL_MASK) {
868 modifiers |= NSControlKeyMask;
871 /* gdk/quartz maps Command to Meta (XXX check this - it may move to SUPER at some point) */
873 if (key->accel_mods & GDK_META_MASK) {
874 modifiers |= NSCommandKeyMask;
878 [cocoa_item setKeyEquivalentModifierMask:modifiers];
883 /* otherwise, clear the menu shortcut */
884 [cocoa_item setKeyEquivalent:@""];
888 cocoa_menu_item_accel_changed (GtkAccelGroup *accel_group,
890 GdkModifierType modifier,
891 GClosure *accel_closure,
894 GNSMenuItem *cocoa_item = cocoa_menu_item_get (widget);
897 get_menu_label_text (widget, &label);
899 if (GTK_IS_ACCEL_LABEL (label) &&
900 GTK_ACCEL_LABEL (label)->accel_closure == accel_closure)
901 cocoa_menu_item_update_accelerator (cocoa_item, widget);
905 cocoa_menu_item_update_accel_closure (GNSMenuItem *cocoa_item,
908 GtkAccelGroup *group;
911 get_menu_label_text (widget, &label);
913 if (cocoa_item->accel_closure)
915 group = gtk_accel_group_from_accel_closure (cocoa_item->accel_closure);
917 g_signal_handlers_disconnect_by_func (group,
918 (void*) cocoa_menu_item_accel_changed,
921 g_closure_unref (cocoa_item->accel_closure);
922 cocoa_item->accel_closure = NULL;
925 if (GTK_IS_ACCEL_LABEL (label)) {
926 cocoa_item->accel_closure = GTK_ACCEL_LABEL (label)->accel_closure;
929 if (cocoa_item->accel_closure)
931 g_closure_ref (cocoa_item->accel_closure);
933 group = gtk_accel_group_from_accel_closure (cocoa_item->accel_closure);
935 g_signal_connect_object (group, "accel-changed",
936 G_CALLBACK (cocoa_menu_item_accel_changed),
937 widget, (GConnectFlags) 0);
940 cocoa_menu_item_update_accelerator (cocoa_item, widget);
944 cocoa_menu_item_notify_label (GObject *object,
948 GNSMenuItem *cocoa_item = cocoa_menu_item_get (GTK_WIDGET (object));
950 if (!strcmp (pspec->name, "label"))
952 cocoa_menu_item_update_label (cocoa_item,
953 GTK_WIDGET (object));
955 else if (!strcmp (pspec->name, "accel-closure"))
957 cocoa_menu_item_update_accel_closure (cocoa_item,
958 GTK_WIDGET (object));
963 cocoa_menu_item_notify (GObject *object,
965 NSMenuItem *cocoa_item)
967 if (!strcmp (pspec->name, "sensitive") ||
968 !strcmp (pspec->name, "visible"))
970 cocoa_menu_item_update_state (cocoa_item, GTK_WIDGET (object));
972 else if (!strcmp (pspec->name, "active"))
974 cocoa_menu_item_update_active (cocoa_item, GTK_WIDGET (object));
976 else if (!strcmp (pspec->name, "submenu"))
978 cocoa_menu_item_update_submenu (cocoa_item, GTK_WIDGET (object));
983 cocoa_menu_item_connect (GtkWidget* menu_item,
984 GNSMenuItem* cocoa_item,
987 GNSMenuItem* old_item = cocoa_menu_item_get (menu_item);
991 if (cocoa_menu_item_quark == 0)
992 cocoa_menu_item_quark = g_quark_from_static_string ("NSMenuItem");
994 g_object_set_qdata_full (G_OBJECT (menu_item), cocoa_menu_item_quark,
996 (GDestroyNotify) cocoa_menu_item_free);
1000 g_signal_connect (menu_item, "notify",
1001 G_CALLBACK (cocoa_menu_item_notify),
1005 g_signal_connect_swapped (label, "notify::label",
1006 G_CALLBACK (cocoa_menu_item_notify_label),
1012 add_menu_item (NSMenu* cocoa_menu, GtkWidget* menu_item, int index)
1014 GtkWidget* label = NULL;
1015 GNSMenuItem *cocoa_item;
1017 DEBUG ("add %s to menu %s separator ? %d\n", get_menu_label_text (menu_item, NULL),
1018 [[cocoa_menu title] cStringUsingEncoding:NSUTF8StringEncoding],
1019 GTK_IS_SEPARATOR_MENU_ITEM(menu_item));
1021 cocoa_item = cocoa_menu_item_get (menu_item);
1026 if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item)) {
1027 cocoa_item = [NSMenuItem separatorItem];
1028 DEBUG ("\ta separator\n");
1031 if (!GTK_WIDGET_VISIBLE (menu_item)) {
1032 DEBUG ("\tnot visible\n");
1036 const gchar* label_text = get_menu_label_text (menu_item, &label);
1039 cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]
1040 andGtkWidget:(GtkMenuItem*)menu_item];
1042 cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:@"" andGtkWidget:(GtkMenuItem*)menu_item];
1043 DEBUG ("\tan item\n");
1046 /* connect GtkMenuItem and NSMenuItem so that we can notice changes to accel/label/submenu etc. */
1047 cocoa_menu_item_connect (menu_item, (GNSMenuItem*) cocoa_item, label);
1049 [ cocoa_item setEnabled:YES];
1051 [ cocoa_menu insertItem:cocoa_item atIndex:index];
1053 [ cocoa_menu addItem:cocoa_item];
1055 if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
1056 [cocoa_item setState:NSOffState];
1058 #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
1059 if (!GTK_WIDGET_VISIBLE (menu_item))
1060 [cocoa_item setHidden:YES];
1063 if (GTK_IS_CHECK_MENU_ITEM (menu_item))
1064 cocoa_menu_item_update_active (cocoa_item, menu_item);
1066 if (!GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
1067 cocoa_menu_item_update_accel_closure (cocoa_item, menu_item);
1069 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
1070 cocoa_menu_item_update_submenu (cocoa_item, menu_item);
1072 [ cocoa_item release];
1076 push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
1084 children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
1086 for (l = children; l; l = l->next)
1088 GtkWidget *menu_item = (GtkWidget*) l->data;
1090 if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
1093 if (g_object_get_data (G_OBJECT (menu_item), "gtk-empty-menu-item"))
1096 add_menu_item (cocoa_menu, menu_item, -1);
1099 g_list_free (children);
1103 static gulong emission_hook_id = 0;
1106 parent_set_emission_hook (GSignalInvocationHint *ihint,
1107 guint n_param_values,
1108 const GValue *param_values,
1111 GtkWidget *instance = (GtkWidget*) g_value_get_object (param_values);
1113 if (GTK_IS_MENU_ITEM (instance))
1115 GtkWidget *previous_parent = (GtkWidget*) g_value_get_object (param_values + 1);
1116 GtkWidget *menu_shell = NULL;
1118 if (GTK_IS_MENU_SHELL (previous_parent))
1120 menu_shell = previous_parent;
1122 else if (GTK_IS_MENU_SHELL (instance->parent))
1124 menu_shell = instance->parent;
1129 NSMenu *cocoa_menu = cocoa_menu_get (menu_shell);
1133 push_menu_shell_to_nsmenu (GTK_MENU_SHELL (menu_shell),
1135 cocoa_menu == (NSMenu*) data,
1145 parent_set_emission_hook_remove (GtkWidget *widget,
1148 g_signal_remove_emission_hook (g_signal_lookup ("parent-set",
1153 /* Building "standard" Cocoa/OS X menus */
1155 #warning You can safely ignore the next warning about a duplicate interface definition
1156 @interface NSApplication(NSWindowsMenu)
1157 - (void)setAppleMenu:(NSMenu *)aMenu;
1160 static NSMenu* _main_menubar = 0;
1161 static NSMenu* _window_menu = 0;
1162 static NSMenu* _app_menu = 0;
1165 add_to_menubar (NSMenu *menu)
1167 NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
1168 action:nil keyEquivalent:@""];
1169 [dummyItem setSubmenu:menu];
1170 [_main_menubar addItem:dummyItem];
1171 [dummyItem release];
1176 add_to_app_menu (NSMenu *menu)
1178 NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
1179 action:nil keyEquivalent:@""];
1180 [dummyItem setSubmenu:menu];
1181 [_app_menu addItem:dummyItem];
1182 [dummyItem release];
1187 add_to_window_menu (NSMenu *menu)
1189 NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
1190 action:nil keyEquivalent:@""];
1191 [dummyItem setSubmenu:menu];
1192 [_window_menu addItem:dummyItem];
1193 [dummyItem release];
1198 create_apple_menu ()
1200 NSMenuItem *menuitem;
1201 // Create the application (Apple) menu.
1202 _app_menu = [[NSMenu alloc] initWithTitle: @"Apple Menu"];
1204 NSMenu *menuServices = [[NSMenu alloc] initWithTitle: @"Services"];
1205 [NSApp setServicesMenu:menuServices];
1207 [_app_menu addItem: [NSMenuItem separatorItem]];
1208 menuitem = [[NSMenuItem alloc] initWithTitle: @"Services"
1209 action:nil keyEquivalent:@""];
1210 [menuitem setSubmenu:menuServices];
1211 [_app_menu addItem: menuitem];
1213 [_app_menu addItem: [NSMenuItem separatorItem]];
1214 menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide"
1215 action:@selector(hide:) keyEquivalent:@""];
1216 [menuitem setTarget: NSApp];
1217 [_app_menu addItem: menuitem];
1219 menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
1220 action:@selector(hideOtherApplications:) keyEquivalent:@""];
1221 [menuitem setTarget: NSApp];
1222 [_app_menu addItem: menuitem];
1224 menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All"
1225 action:@selector(unhideAllApplications:) keyEquivalent:@""];
1226 [menuitem setTarget: NSApp];
1227 [_app_menu addItem: menuitem];
1229 [_app_menu addItem: [NSMenuItem separatorItem]];
1230 menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit"
1231 action:@selector(terminate:) keyEquivalent:@"q"];
1232 [menuitem setTarget: NSApp];
1233 [_app_menu addItem: menuitem];
1236 [NSApp setAppleMenu:_app_menu];
1237 add_to_menubar (_app_menu);
1243 create_window_menu ()
1245 _window_menu = [[NSMenu alloc] initWithTitle: @"Window"];
1247 [_window_menu addItemWithTitle:@"Minimize"
1248 action:@selector(performMiniaturize:) keyEquivalent:@""];
1249 [_window_menu addItem: [NSMenuItem separatorItem]];
1250 [_window_menu addItemWithTitle:@"Bring All to Front"
1251 action:@selector(arrangeInFront:) keyEquivalent:@""];
1253 [NSApp setWindowsMenu:_window_menu];
1254 add_to_menubar(_window_menu);
1264 gtk_application_set_menu_bar (GtkMenuShell *menu_shell)
1266 NSMenu* cocoa_menubar;
1268 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1270 if (cocoa_menu_quark == 0)
1271 cocoa_menu_quark = g_quark_from_static_string ("NSMenu");
1273 if (cocoa_menu_item_quark == 0)
1274 cocoa_menu_item_quark = g_quark_from_static_string ("NSMenuItem");
1276 cocoa_menubar = [ [ NSApplication sharedApplication] mainMenu];
1278 /* turn off auto-enabling for the menu - its silly and slow and
1279 doesn't really make sense for a Gtk/Cocoa hybrid menu.
1282 [cocoa_menubar setAutoenablesItems:NO];
1285 g_signal_add_emission_hook (g_signal_lookup ("parent-set",
1288 parent_set_emission_hook,
1289 cocoa_menubar, NULL);
1292 g_signal_connect (menu_shell, "destroy",
1293 G_CALLBACK (parent_set_emission_hook_remove),
1296 push_menu_shell_to_nsmenu (menu_shell, cocoa_menubar, TRUE, FALSE);
1300 gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
1301 GtkMenuItem *menu_item)
1303 // we know that the application menu is always the submenu of the first item in the main menu
1306 NSMenuItem *firstItem;
1310 mainMenu = [NSApp mainMenu];
1311 firstItem = [ mainMenu itemAtIndex:0];
1312 appMenu = [ firstItem submenu ];
1314 g_return_if_fail (group != NULL);
1315 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1317 for (list = _gtk_application_menu_groups; list; list = g_list_next (list))
1319 GtkApplicationMenuGroup *list_group = (GtkApplicationMenuGroup*) list->data;
1321 index += g_list_length (list_group->items);
1323 /* adjust index for the separator between groups, but not
1324 * before the first group
1326 if (list_group->items && list->prev)
1329 if (group == list_group)
1331 /* add a separator before adding the first item, but not
1332 * for the first group
1335 if (!group->items && list->prev)
1337 [appMenu insertItem:[NSMenuItem separatorItem] atIndex:index+1];
1340 DEBUG ("Add to APP menu bar %s\n", get_menu_label_text (GTK_WIDGET(menu_item), NULL));
1341 add_menu_item (appMenu, GTK_WIDGET(menu_item), index+1);
1343 group->items = g_list_append (group->items, menu_item);
1344 gtk_widget_hide (GTK_WIDGET (menu_item));
1350 g_warning ("%s: app menu group %p does not exist",
1354 /* application delegate, currently in C++ */
1356 #include <gtkmm2ext/application.h>
1357 #include <glibmm/ustring.h>
1360 namespace Application {
1361 sigc::signal<void,bool> ActivationChanged;
1362 sigc::signal<void,const Glib::ustring&> ShouldLoad;
1363 sigc::signal<void> ShouldQuit;
1367 @interface GtkApplicationNotificationObject : NSObject {}
1368 - (GtkApplicationNotificationObject*) init;
1371 @implementation GtkApplicationNotificationObject
1372 - (GtkApplicationNotificationObject*) init
1374 self = [ super init ];
1377 [[NSNotificationCenter defaultCenter] addObserver:self
1378 selector:@selector(appDidBecomeActive:)
1379 name:NSApplicationDidBecomeActiveNotification
1380 object:[NSApplication sharedApplication]];
1382 [[NSNotificationCenter defaultCenter] addObserver:self
1383 selector:@selector(appDidBecomeInactive:)
1384 name:NSApplicationWillResignActiveNotification
1385 object:[NSApplication sharedApplication]];
1391 - (void)appDidBecomeActive:(NSNotification *)notification
1393 Gtkmm2ext::Application::instance()->ActivationChanged (true);
1396 - (void)appDidBecomeInactive:(NSNotification *)notification
1398 Gtkmm2ext::Application::instance()->ActivationChanged (false);
1403 @interface GtkApplicationDelegate : NSObject {}
1406 @implementation GtkApplicationDelegate
1407 -(BOOL) application:(NSApplication*) theApplication openFile:(NSString*) file
1409 Glib::ustring utf8_path ([file UTF8String]);
1410 Gtkmm2ext::Application::instance()->ShouldLoad (utf8_path);
1413 - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
1415 Gtkmm2ext::Application::instance()->ShouldQuit ();
1416 return NSTerminateCancel;
1424 gtk_application_init ()
1426 _main_menubar = [[NSMenu alloc] initWithTitle: @""];
1431 [NSApp setMainMenu: _main_menubar];
1432 create_apple_menu ();
1433 // create_window_menu ();
1435 /* this will stick around for ever ... is that OK ? */
1437 [ [GtkApplicationNotificationObject alloc] init];
1438 [ NSApp setDelegate: [GtkApplicationDelegate new]];
1444 gtk_application_ready ()
1446 [ NSApp finishLaunching ];
1450 gtk_application_cleanup()
1453 [ _window_menu release ];
1455 [ _app_menu release ];
1457 [ _main_menubar release ];