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 #include <sigc++/signal.h>
28 #include <sigc++/slot.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gtkmm2ext/gtkapplication.h>
34 #include <gtkmm2ext/gtkapplication-private.h>
36 #import <AppKit/NSMenu.h>
37 #import <AppKit/NSMenuItem.h>
38 #import <AppKit/NSCell.h>
39 #import <AppKit/NSEvent.h>
40 #import <AppKit/NSApplication.h>
41 #import <Foundation/NSString.h>
42 #import <Foundation/NSNotification.h>
44 #define UNUSED_PARAMETER(a) (void) (a)
46 // #define DEBUG(format, ...) g_printerr ("%s: " format, G_STRFUNC, ## __VA_ARGS__)
47 #define DEBUG(format, ...)
51 * - Sync adding/removing/reordering items
52 * - Create on demand? (can this be done with gtk+? ie fill in menu
53 items when the menu is opened)
54 * - Figure out what to do per app/window...
58 static gint _exiting = 0;
61 gdk_quartz_keyval_to_ns_keyval (guint keyval)
65 return NSBackspaceCharacter;
67 return NSDeleteFunctionKey;
69 return NSPauseFunctionKey;
71 return NSScrollLockFunctionKey;
73 return NSSysReqFunctionKey;
75 return NSHomeFunctionKey;
78 return NSLeftArrowFunctionKey;
81 return NSUpArrowFunctionKey;
84 return NSRightArrowFunctionKey;
87 return NSDownArrowFunctionKey;
89 return NSPageUpFunctionKey;
91 return NSPageDownFunctionKey;
93 return NSEndFunctionKey;
95 return NSBeginFunctionKey;
97 return NSSelectFunctionKey;
99 return NSPrintFunctionKey;
101 return NSExecuteFunctionKey;
103 return NSInsertFunctionKey;
105 return NSUndoFunctionKey;
107 return NSRedoFunctionKey;
109 return NSMenuFunctionKey;
111 return NSFindFunctionKey;
113 return NSHelpFunctionKey;
115 return NSBreakFunctionKey;
116 case GDK_Mode_switch:
117 return NSModeSwitchFunctionKey;
119 return NSF1FunctionKey;
121 return NSF2FunctionKey;
123 return NSF3FunctionKey;
125 return NSF4FunctionKey;
127 return NSF5FunctionKey;
129 return NSF6FunctionKey;
131 return NSF7FunctionKey;
133 return NSF8FunctionKey;
135 return NSF9FunctionKey;
137 return NSF10FunctionKey;
139 return NSF11FunctionKey;
141 return NSF12FunctionKey;
143 return NSF13FunctionKey;
145 return NSF14FunctionKey;
147 return NSF15FunctionKey;
149 return NSF16FunctionKey;
151 return NSF17FunctionKey;
153 return NSF18FunctionKey;
155 return NSF19FunctionKey;
157 return NSF20FunctionKey;
159 return NSF21FunctionKey;
161 return NSF22FunctionKey;
163 return NSF23FunctionKey;
165 return NSF24FunctionKey;
167 return NSF25FunctionKey;
169 return NSF26FunctionKey;
171 return NSF27FunctionKey;
173 return NSF28FunctionKey;
175 return NSF29FunctionKey;
177 return NSF30FunctionKey;
179 return NSF31FunctionKey;
181 return NSF32FunctionKey;
183 return NSF33FunctionKey;
185 return NSF34FunctionKey;
187 return NSF35FunctionKey;
196 keyval_is_keypad (guint keyval)
209 case GDK_KP_Page_Down:
215 case GDK_KP_Multiply:
217 case GDK_KP_Separator:
218 case GDK_KP_Subtract:
240 keyval_keypad_nonkeypad_equivalent (guint keyval)
263 case GDK_KP_Page_Down:
264 return GDK_Page_Down;
275 case GDK_KP_Multiply:
279 case GDK_KP_Subtract:
309 return GDK_VoidSymbol;
313 gdk_quartz_keyval_to_string (guint keyval)
460 case GDK_bracketleft:
464 case GDK_bracketright:
466 case GDK_asciicircum:
487 keyval_is_uppercase (guint keyval)
523 /* gtk/osx has a problem in that mac main menu events
524 are handled using an "internal" event handling system that
525 doesn't pass things back to the glib/gtk main loop. if we call
526 gtk_main_iteration() block while in a menu event handler, then
527 glib gets confused and thinks there are two threads running
528 g_main_poll_func(). apps call call gdk_quartz_in_menu_event_handler()
529 if they need to check this.
532 static int _in_menu_event_handler = 0;
535 gdk_quartz_in_menu_event_handler ()
537 return _in_menu_event_handler;
541 idle_call_activate (gpointer data)
543 gtk_menu_item_activate ((GtkMenuItem*) data);
547 @interface GNSMenuItem : NSMenuItem
550 GtkMenuItem* gtk_menu_item;
551 GClosure *accel_closure;
553 - (id) initWithTitle:(NSString*) title andGtkWidget:(GtkMenuItem*) w;
554 - (void) activate:(id) sender;
557 @implementation GNSMenuItem
558 - (id) initWithTitle:(NSString*) title andGtkWidget:(GtkMenuItem*) w
560 /* All menu items have the action "activate", which will be handled by this child class
563 self = [ super initWithTitle:title action:@selector(activate:) keyEquivalent:@"" ];
566 /* make this handle its own action */
567 [ self setTarget:self ];
573 - (void) activate:(id) sender
575 UNUSED_PARAMETER(sender);
576 #ifdef USE_TRACKS_CODE_FEATURES
577 // Hot Fix. Increase Priority.
578 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_call_activate, gtk_menu_item, NULL);
580 g_idle_add (idle_call_activate, gtk_menu_item);
585 static void push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
595 find_menu_label (GtkWidget *widget)
597 GtkWidget *label = NULL;
599 if (GTK_IS_LABEL (widget))
602 if (GTK_IS_CONTAINER (widget))
607 children = gtk_container_get_children (GTK_CONTAINER (widget));
609 for (l = children; l; l = l->next)
611 label = find_menu_label ((GtkWidget*) l->data);
616 g_list_free (children);
623 get_menu_label_text (GtkWidget *menu_item,
628 my_label = find_menu_label (menu_item);
633 return gtk_label_get_text (GTK_LABEL (my_label));
639 accel_find_func (GtkAccelKey * /*key*/,
643 return (GClosure *) data == closure;
648 * CocoaMenu functions
651 static GQuark cocoa_menu_quark = 0;
654 cocoa_menu_get (GtkWidget *widget)
656 return (NSMenu*) g_object_get_qdata (G_OBJECT (widget), cocoa_menu_quark);
660 cocoa_menu_free (gpointer *ptr)
662 NSMenu* menu = (NSMenu*) ptr;
667 cocoa_menu_connect (GtkWidget *menu,
672 if (cocoa_menu_quark == 0)
673 cocoa_menu_quark = g_quark_from_static_string ("NSMenu");
675 g_object_set_qdata_full (G_OBJECT (menu), cocoa_menu_quark,
677 (GDestroyNotify) cocoa_menu_free);
681 * NSMenuItem functions
684 static GQuark cocoa_menu_item_quark = 0;
685 static void cocoa_menu_item_connect (GtkWidget* menu_item,
686 GNSMenuItem* cocoa_menu_item,
690 cocoa_menu_item_free (gpointer *ptr)
692 GNSMenuItem* item = (GNSMenuItem*) ptr;
697 cocoa_menu_item_get (GtkWidget *widget)
699 return (GNSMenuItem*) g_object_get_qdata (G_OBJECT (widget), cocoa_menu_item_quark);
703 cocoa_menu_item_update_state (NSMenuItem* cocoa_item,
709 g_object_get (widget,
710 "sensitive", &sensitive,
715 [cocoa_item setEnabled:NO];
717 [cocoa_item setEnabled:YES];
720 // requires OS X 10.5 or later
722 [cocoa_item setHidden:YES];
724 [cocoa_item setHidden:NO];
729 cocoa_menu_item_update_active (NSMenuItem *cocoa_item,
734 g_object_get (widget, "active", &active, NULL);
737 [cocoa_item setState:NSOnState];
739 [cocoa_item setState:NSOffState];
743 cocoa_menu_item_update_submenu (NSMenuItem *cocoa_item,
748 g_return_if_fail (cocoa_item != NULL);
749 g_return_if_fail (widget != NULL);
751 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
755 GtkWidget* label = NULL;
756 const gchar *label_text;
757 NSMenu* cocoa_submenu;
759 label_text = get_menu_label_text (widget, &label);
761 /* create a new nsmenu to hold the GTK menu */
764 cocoa_submenu = [ [ NSMenu alloc ] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]];
766 cocoa_submenu = [ [ NSMenu alloc ] initWithTitle:@""];
768 [cocoa_submenu setAutoenablesItems:NO];
769 cocoa_menu_connect (submenu, cocoa_submenu);
771 /* connect the new nsmenu to the passed-in item (which lives in
773 (Note: this will release any pre-existing version of this submenu)
775 [ cocoa_item setSubmenu:cocoa_submenu];
777 /* and push the GTK menu into the submenu */
778 push_menu_shell_to_nsmenu (GTK_MENU_SHELL (submenu), cocoa_submenu, FALSE, FALSE);
780 [ cocoa_submenu release ];
785 cocoa_menu_item_update_label (NSMenuItem *cocoa_item,
788 const gchar *label_text;
790 g_return_if_fail (cocoa_item != NULL);
791 g_return_if_fail (widget != NULL);
793 label_text = get_menu_label_text (widget, NULL);
795 [cocoa_item setTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]];
797 [cocoa_item setTitle:@""];
801 cocoa_menu_item_update_accelerator (NSMenuItem *cocoa_item,
806 g_return_if_fail (cocoa_item != NULL);
807 g_return_if_fail (widget != NULL);
809 /* important note: this function doesn't do anything to actually change
810 key handling. Its goal is to get Cocoa to display the correct
811 accelerator as part of a menu item. Actual accelerator handling
812 is still done by GTK, so this is more cosmetic than it may
816 get_menu_label_text (widget, &label);
818 if (GTK_IS_ACCEL_LABEL (label) &&
819 GTK_ACCEL_LABEL (label)->accel_closure)
823 key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group,
825 GTK_ACCEL_LABEL (label)->accel_closure);
829 key->accel_flags & GTK_ACCEL_VISIBLE)
832 const gchar* str = NULL;
833 guint actual_key = key->accel_key;
835 if (keyval_is_keypad (actual_key)) {
836 if ((actual_key = keyval_keypad_nonkeypad_equivalent (actual_key)) == GDK_VoidSymbol) {
837 /* GDK_KP_Separator */
838 [cocoa_item setKeyEquivalent:@""];
841 modifiers |= NSNumericPadKeyMask;
844 /* if we somehow got here with GDK_A ... GDK_Z rather than GDK_a ... GDK_z, then take note
845 of that and make sure we use a shift modifier.
848 if (keyval_is_uppercase (actual_key)) {
849 modifiers |= NSShiftKeyMask;
852 str = gdk_quartz_keyval_to_string (actual_key);
855 unichar ukey = str[0];
856 [cocoa_item setKeyEquivalent:[NSString stringWithCharacters:&ukey length:1]];
858 unichar ukey = gdk_quartz_keyval_to_ns_keyval (actual_key);
860 [cocoa_item setKeyEquivalent:[NSString stringWithCharacters:&ukey length:1]];
862 /* cannot map this key to Cocoa key equivalent */
863 [cocoa_item setKeyEquivalent:@""];
868 if (key->accel_mods || modifiers)
870 if (key->accel_mods & GDK_SHIFT_MASK) {
871 modifiers |= NSShiftKeyMask;
874 /* gdk/quartz maps Alt/Option to Mod1 */
876 if (key->accel_mods & (GDK_MOD1_MASK)) {
877 modifiers |= NSAlternateKeyMask;
880 if (key->accel_mods & GDK_CONTROL_MASK) {
881 modifiers |= NSControlKeyMask;
884 /* gdk/quartz maps Command to Meta (XXX check this - it may move to SUPER at some point) */
886 if (key->accel_mods & GDK_META_MASK) {
887 modifiers |= NSCommandKeyMask;
891 [cocoa_item setKeyEquivalentModifierMask:modifiers];
896 /* otherwise, clear the menu shortcut */
897 [cocoa_item setKeyEquivalent:@""];
901 cocoa_menu_item_accel_changed (GtkAccelGroup* /*accel_group*/,
903 GdkModifierType /*modifier*/,
904 GClosure *accel_closure,
907 GNSMenuItem *cocoa_item;
913 cocoa_item = cocoa_menu_item_get (widget);
914 get_menu_label_text (widget, &label);
916 if (GTK_IS_ACCEL_LABEL (label) &&
917 GTK_ACCEL_LABEL (label)->accel_closure == accel_closure)
918 cocoa_menu_item_update_accelerator (cocoa_item, widget);
922 cocoa_menu_item_update_accel_closure (GNSMenuItem *cocoa_item,
925 GtkAccelGroup *group;
928 get_menu_label_text (widget, &label);
930 if (cocoa_item->accel_closure)
932 group = gtk_accel_group_from_accel_closure (cocoa_item->accel_closure);
934 g_signal_handlers_disconnect_by_func (group,
935 (void*) cocoa_menu_item_accel_changed,
938 g_closure_unref (cocoa_item->accel_closure);
939 cocoa_item->accel_closure = NULL;
942 if (GTK_IS_ACCEL_LABEL (label)) {
943 cocoa_item->accel_closure = GTK_ACCEL_LABEL (label)->accel_closure;
946 if (cocoa_item->accel_closure)
948 g_closure_ref (cocoa_item->accel_closure);
950 group = gtk_accel_group_from_accel_closure (cocoa_item->accel_closure);
952 g_signal_connect_object (group, "accel-changed",
953 G_CALLBACK (cocoa_menu_item_accel_changed),
954 widget, (GConnectFlags) 0);
957 cocoa_menu_item_update_accelerator (cocoa_item, widget);
961 cocoa_menu_item_notify_label (GObject *object,
965 GNSMenuItem *cocoa_item;
970 cocoa_item = cocoa_menu_item_get (GTK_WIDGET (object));
972 if (!strcmp (pspec->name, "label"))
974 cocoa_menu_item_update_label (cocoa_item,
975 GTK_WIDGET (object));
977 else if (!strcmp (pspec->name, "accel-closure"))
979 cocoa_menu_item_update_accel_closure (cocoa_item,
980 GTK_WIDGET (object));
985 cocoa_menu_item_notify (GObject *object,
987 NSMenuItem *cocoa_item)
992 if (!strcmp (pspec->name, "sensitive") ||
993 !strcmp (pspec->name, "visible"))
995 cocoa_menu_item_update_state (cocoa_item, GTK_WIDGET (object));
997 else if (!strcmp (pspec->name, "active"))
999 cocoa_menu_item_update_active (cocoa_item, GTK_WIDGET (object));
1001 else if (!strcmp (pspec->name, "submenu"))
1003 cocoa_menu_item_update_submenu (cocoa_item, GTK_WIDGET (object));
1008 cocoa_menu_item_connect (GtkWidget* menu_item,
1009 GNSMenuItem* cocoa_item,
1012 GNSMenuItem* old_item = cocoa_menu_item_get (menu_item);
1014 [cocoa_item retain];
1016 if (cocoa_menu_item_quark == 0)
1017 cocoa_menu_item_quark = g_quark_from_static_string ("NSMenuItem");
1019 g_object_set_qdata_full (G_OBJECT (menu_item), cocoa_menu_item_quark,
1021 (GDestroyNotify) cocoa_menu_item_free);
1025 g_signal_connect (menu_item, "notify",
1026 G_CALLBACK (cocoa_menu_item_notify),
1030 g_signal_connect_swapped (label, "notify::label",
1031 G_CALLBACK (cocoa_menu_item_notify_label),
1037 add_menu_item (NSMenu* cocoa_menu, GtkWidget* menu_item, int index)
1039 GtkWidget* label = NULL;
1040 GNSMenuItem *cocoa_item;
1042 DEBUG ("add %s to menu %s separator ? %d\n", get_menu_label_text (menu_item, NULL),
1043 [[cocoa_menu title] cStringUsingEncoding:NSUTF8StringEncoding],
1044 GTK_IS_SEPARATOR_MENU_ITEM(menu_item));
1046 cocoa_item = cocoa_menu_item_get (menu_item);
1051 if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item)) {
1052 cocoa_item = [NSMenuItem separatorItem];
1053 DEBUG ("\ta separator\n");
1056 if (!GTK_WIDGET_VISIBLE (menu_item)) {
1057 DEBUG ("\tnot visible\n");
1061 const gchar* label_text = get_menu_label_text (menu_item, &label);
1064 cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]
1065 andGtkWidget:(GtkMenuItem*)menu_item];
1067 cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:@"" andGtkWidget:(GtkMenuItem*)menu_item];
1068 DEBUG ("\tan item\n");
1071 /* connect GtkMenuItem and NSMenuItem so that we can notice changes to accel/label/submenu etc. */
1072 cocoa_menu_item_connect (menu_item, (GNSMenuItem*) cocoa_item, label);
1074 [ cocoa_item setEnabled:YES];
1076 [ cocoa_menu insertItem:cocoa_item atIndex:index];
1078 [ cocoa_menu addItem:cocoa_item];
1080 if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
1081 [cocoa_item setState:NSOffState];
1083 #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
1084 if (!GTK_WIDGET_VISIBLE (menu_item))
1085 [cocoa_item setHidden:YES];
1088 if (GTK_IS_CHECK_MENU_ITEM (menu_item))
1089 cocoa_menu_item_update_active (cocoa_item, menu_item);
1091 if (!GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
1092 cocoa_menu_item_update_accel_closure (cocoa_item, menu_item);
1094 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
1095 cocoa_menu_item_update_submenu (cocoa_item, menu_item);
1097 [ cocoa_item release];
1101 push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
1103 gboolean /*toplevel*/,
1109 children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
1111 for (l = children; l; l = l->next)
1113 GtkWidget *menu_item = (GtkWidget*) l->data;
1115 if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
1118 if (g_object_get_data (G_OBJECT (menu_item), "gtk-empty-menu-item"))
1121 add_menu_item (cocoa_menu, menu_item, -1);
1124 g_list_free (children);
1128 static gulong emission_hook_id = 0;
1131 parent_set_emission_hook (GSignalInvocationHint* /*ihint*/,
1132 guint /*n_param_values*/,
1133 const GValue* param_values,
1136 GtkWidget *instance = (GtkWidget*) g_value_get_object (param_values);
1138 if (GTK_IS_MENU_ITEM (instance))
1140 GtkWidget *previous_parent = (GtkWidget*) g_value_get_object (param_values + 1);
1141 GtkWidget *menu_shell = NULL;
1143 if (GTK_IS_MENU_SHELL (previous_parent))
1145 menu_shell = previous_parent;
1147 else if (GTK_IS_MENU_SHELL (instance->parent))
1149 menu_shell = instance->parent;
1154 NSMenu *cocoa_menu = cocoa_menu_get (menu_shell);
1158 push_menu_shell_to_nsmenu (GTK_MENU_SHELL (menu_shell),
1160 cocoa_menu == (NSMenu*) data,
1170 parent_set_emission_hook_remove (GtkWidget*, gpointer)
1172 g_signal_remove_emission_hook (g_signal_lookup ("parent-set", GTK_TYPE_WIDGET),
1176 /* Building "standard" Cocoa/OS X menus */
1178 #warning You can safely ignore the next warning about a duplicate interface definition
1179 @interface NSApplication(NSWindowsMenu)
1180 - (void)setAppleMenu:(NSMenu *)aMenu;
1183 static NSMenu* _main_menubar = 0;
1184 static NSMenu* _window_menu = 0;
1185 static NSMenu* _app_menu = 0;
1188 add_to_menubar (NSMenu *menu)
1190 NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
1191 action:nil keyEquivalent:@""];
1192 [dummyItem setSubmenu:menu];
1193 [_main_menubar addItem:dummyItem];
1194 [dummyItem release];
1200 add_to_app_menu (NSMenu *menu)
1202 NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
1203 action:nil keyEquivalent:@""];
1204 [dummyItem setSubmenu:menu];
1205 [_app_menu addItem:dummyItem];
1206 [dummyItem release];
1212 create_apple_menu ()
1214 NSMenuItem *menuitem;
1215 // Create the application (Apple) menu.
1216 _app_menu = [[NSMenu alloc] initWithTitle: @"Apple Menu"];
1218 NSMenu *menuServices = [[NSMenu alloc] initWithTitle: @"Services"];
1219 [NSApp setServicesMenu:menuServices];
1221 [_app_menu addItem: [NSMenuItem separatorItem]];
1222 menuitem = [[NSMenuItem alloc] initWithTitle: @"Services"
1223 action:nil keyEquivalent:@""];
1224 [menuitem setSubmenu:menuServices];
1225 [_app_menu addItem: menuitem];
1227 [_app_menu addItem: [NSMenuItem separatorItem]];
1228 menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide"
1229 action:@selector(hide:) keyEquivalent:@"h"];
1230 [menuitem setTarget: NSApp];
1231 [_app_menu addItem: menuitem];
1233 menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
1234 action:@selector(hideOtherApplications:) keyEquivalent:@""];
1235 [menuitem setTarget: NSApp];
1236 [_app_menu addItem: menuitem];
1238 menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All"
1239 action:@selector(unhideAllApplications:) keyEquivalent:@""];
1240 [menuitem setTarget: NSApp];
1241 [_app_menu addItem: menuitem];
1243 [_app_menu addItem: [NSMenuItem separatorItem]];
1244 menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit"
1245 action:@selector(terminate:) keyEquivalent:@"q"];
1246 [menuitem setTarget: NSApp];
1247 [_app_menu addItem: menuitem];
1250 [NSApp setAppleMenu:_app_menu];
1251 add_to_menubar (_app_menu);
1258 add_to_window_menu (NSMenu *menu)
1260 NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
1261 action:nil keyEquivalent:@""];
1262 [dummyItem setSubmenu:menu];
1263 [_window_menu addItem:dummyItem];
1264 [dummyItem release];
1269 create_window_menu ()
1271 _window_menu = [[NSMenu alloc] initWithTitle: @"Window"];
1273 [_window_menu addItemWithTitle:@"Minimize"
1274 action:@selector(performMiniaturize:) keyEquivalent:@""];
1275 [_window_menu addItem: [NSMenuItem separatorItem]];
1276 [_window_menu addItemWithTitle:@"Bring All to Front"
1277 action:@selector(arrangeInFront:) keyEquivalent:@""];
1279 [NSApp setWindowsMenu:_window_menu];
1280 add_to_menubar(_window_menu);
1291 gtk_application_set_menu_bar (GtkMenuShell *menu_shell)
1293 NSMenu* cocoa_menubar;
1295 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1297 if (cocoa_menu_quark == 0)
1298 cocoa_menu_quark = g_quark_from_static_string ("NSMenu");
1300 if (cocoa_menu_item_quark == 0)
1301 cocoa_menu_item_quark = g_quark_from_static_string ("NSMenuItem");
1303 cocoa_menubar = [ [ NSApplication sharedApplication] mainMenu];
1305 /* turn off auto-enabling for the menu - its silly and slow and
1306 doesn't really make sense for a Gtk/Cocoa hybrid menu.
1309 [cocoa_menubar setAutoenablesItems:NO];
1312 g_signal_add_emission_hook (g_signal_lookup ("parent-set",
1315 parent_set_emission_hook,
1316 cocoa_menubar, NULL);
1319 g_signal_connect (menu_shell, "destroy",
1320 G_CALLBACK (parent_set_emission_hook_remove),
1323 push_menu_shell_to_nsmenu (menu_shell, cocoa_menubar, TRUE, FALSE);
1327 gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
1328 GtkMenuItem *menu_item)
1330 // we know that the application menu is always the submenu of the first item in the main menu
1333 NSMenuItem *firstItem;
1337 mainMenu = [NSApp mainMenu];
1338 firstItem = [ mainMenu itemAtIndex:0];
1339 appMenu = [ firstItem submenu ];
1341 g_return_if_fail (group != NULL);
1342 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1344 for (list = _gtk_application_menu_groups; list; list = g_list_next (list))
1346 GtkApplicationMenuGroup *list_group = (GtkApplicationMenuGroup*) list->data;
1348 index += g_list_length (list_group->items);
1350 /* adjust index for the separator between groups, but not
1351 * before the first group
1353 if (list_group->items && list->prev)
1356 if (group == list_group)
1358 /* add a separator before adding the first item, but not
1359 * for the first group
1362 if (!group->items && list->prev)
1364 [appMenu insertItem:[NSMenuItem separatorItem] atIndex:index+1];
1367 DEBUG ("Add to APP menu bar %s\n", get_menu_label_text (GTK_WIDGET(menu_item), NULL));
1368 add_menu_item (appMenu, GTK_WIDGET(menu_item), index+1);
1370 group->items = g_list_append (group->items, menu_item);
1371 gtk_widget_hide (GTK_WIDGET (menu_item));
1377 g_warning ("%s: app menu group %p does not exist",
1381 /* application delegate, currently in C++ */
1383 #include <gtkmm2ext/application.h>
1384 #include <glibmm/ustring.h>
1387 namespace Application {
1388 sigc::signal<void,bool> ActivationChanged;
1389 sigc::signal<void,const Glib::ustring&> ShouldLoad;
1390 sigc::signal<void> ShouldQuit;
1394 @interface GtkApplicationNotificationObject : NSObject {}
1395 - (GtkApplicationNotificationObject*) init;
1398 @implementation GtkApplicationNotificationObject
1399 - (GtkApplicationNotificationObject*) init
1401 self = [ super init ];
1404 [[NSNotificationCenter defaultCenter] addObserver:self
1405 selector:@selector(appDidBecomeActive:)
1406 name:NSApplicationDidBecomeActiveNotification
1407 object:[NSApplication sharedApplication]];
1409 [[NSNotificationCenter defaultCenter] addObserver:self
1410 selector:@selector(appDidBecomeInactive:)
1411 name:NSApplicationWillResignActiveNotification
1412 object:[NSApplication sharedApplication]];
1418 - (void)appDidBecomeActive:(NSNotification *) notification
1420 UNUSED_PARAMETER(notification);
1421 Gtkmm2ext::Application::instance()->ActivationChanged (true);
1424 - (void)appDidBecomeInactive:(NSNotification *) notification
1426 UNUSED_PARAMETER(notification);
1427 Gtkmm2ext::Application::instance()->ActivationChanged (false);
1432 @interface GtkApplicationDelegate : NSObject
1433 -(BOOL) application:(NSApplication*) app openFile:(NSString*) file;
1434 - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *) app;
1438 @implementation GtkApplicationDelegate
1439 -(BOOL) application:(NSApplication*) app openFile:(NSString*) file
1441 UNUSED_PARAMETER(app);
1442 Glib::ustring utf8_path ([file UTF8String]);
1443 Gtkmm2ext::Application::instance()->ShouldLoad (utf8_path);
1446 - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *) app
1448 UNUSED_PARAMETER(app);
1449 Gtkmm2ext::Application::instance()->ShouldQuit ();
1450 return NSTerminateCancel;
1458 gtk_application_init ()
1460 _main_menubar = [[NSMenu alloc] initWithTitle: @""];
1465 [NSApp setMainMenu: _main_menubar];
1466 create_apple_menu ();
1467 // create_window_menu ();
1469 /* this will stick around for ever ... is that OK ? */
1471 [ [GtkApplicationNotificationObject alloc] init];
1472 [ NSApp setDelegate: [GtkApplicationDelegate new]];
1478 gtk_application_ready ()
1480 [ NSApp finishLaunching ];
1481 [[NSApplication sharedApplication] activateIgnoringOtherApps : YES];
1485 gtk_application_hide ()
1487 [NSApp performSelector:@selector(hide:)];
1491 gtk_application_cleanup()
1496 [ _window_menu release ];
1500 [ _app_menu release ];
1503 if (_main_menubar) {
1504 [ _main_menubar release ];