summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2010-01-15 01:59:56 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2010-01-15 01:59:56 +0000
commit15bb3b577ec9a8cb50758c4b017718e342a2439c (patch)
tree293407ebe738cfc3378335a82cb69e75751e9e73
parent496a2da48f8a02a4be7761f0da6eb0b3828ee68b (diff)
rework "ige_mac" menubar integration from Carbon to Cocoa; recast as start of GtkApplication object as per discussions on #gtk+; associated changes in ardour; NOT TESTED ON X11, LEOPARD or SNOW LEOPARD
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@6493 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/ardour_ui_ed.cc6
-rw-r--r--gtk2_ardour/cocoacarbon.mm33
-rw-r--r--gtk2_ardour/utils.cc54
-rw-r--r--libs/gtkmm2ext/SConscript13
-rw-r--r--libs/gtkmm2ext/gtk_ui.cc24
-rw-r--r--libs/gtkmm2ext/gtkapplication.c36
-rw-r--r--libs/gtkmm2ext/gtkapplication_quartz.mm1370
-rw-r--r--libs/gtkmm2ext/gtkapplication_x11.c48
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/gtkapplication-private.h (renamed from libs/gtkmm2ext/gtkmm2ext/sync-menu.h)26
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/gtkapplication.h48
-rw-r--r--libs/gtkmm2ext/sync-menu.c981
11 files changed, 1566 insertions, 1073 deletions
diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc
index fc0fddbb8d..b795bc5ae8 100644
--- a/gtk2_ardour/ardour_ui_ed.cc
+++ b/gtk2_ardour/ardour_ui_ed.cc
@@ -41,9 +41,7 @@
#include "mixer_ui.h"
#include "utils.h"
-#ifdef GTKOSX
-#include <gtkmm2ext/sync-menu.h>
-#endif
+#include <gtkmm2ext/gtkapplication.h>
#include <ardour/session.h>
#include <ardour/profile.h>
@@ -854,7 +852,7 @@ void
ARDOUR_UI::use_menubar_as_top_menubar ()
{
#ifdef GTKOSX
- ige_mac_menu_set_menu_bar ((GtkMenuShell*) menu_bar->gobj());
+ gtk_application_set_menu_bar ((GtkMenuShell*) menu_bar->gobj());
// ige_mac_menu_set_quit_menu_item (some_item->gobj());
#endif
}
diff --git a/gtk2_ardour/cocoacarbon.mm b/gtk2_ardour/cocoacarbon.mm
index c39bbe5ed9..ddf1f26f9f 100644
--- a/gtk2_ardour/cocoacarbon.mm
+++ b/gtk2_ardour/cocoacarbon.mm
@@ -16,17 +16,15 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <Carbon/Carbon.h> // bring it in early
-#undef check // nuke this stupidity
-#define APPLE_SAYS_YES YES
-#undef YES // and this
-#undef NO // and this
+#include <gtkmm2ext/gtkapplication.h>
+#include <gdk/gdkquartz.h>
+#undef check
+#undef YES
+#undef NO
#include "ardour_ui.h"
#include "actions.h"
#include "opts.h"
-#include <gtkmm2ext/sync-menu.h>
-#include <gdk/gdkquartz.h>
sigc::signal<void,bool> ApplicationActivationChanged;
@@ -74,7 +72,12 @@ sigc::signal<void,bool> ApplicationActivationChanged;
{
Glib::ustring utf8_path ([file UTF8String]);
ARDOUR_UI::instance()->idle_load (utf8_path);
- return APPLE_SAYS_YES;
+ return 1;
+}
+- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
+{
+ Gtkmm2ext::UI::instance()->quit ();
+ return NSTerminateCancel;
}
@end
@@ -83,23 +86,16 @@ ARDOUR_UI::platform_specific ()
{
Gtk::Widget* widget;
- cerr << "Plaform specific GUI stuff\n";
-
- widget = ActionManager::get_widget ("/ui/Main/Session/Quit");
- if (widget) {
- ige_mac_menu_set_quit_menu_item ((GtkMenuItem*) widget->gobj());
- }
-
- IgeMacMenuGroup* group = ige_mac_menu_add_app_menu_group ();
+ GtkApplicationMenuGroup* group = gtk_application_add_app_menu_group ();
widget = ActionManager::get_widget ("/ui/Main/Help/About");
if (widget) {
- ige_mac_menu_add_app_menu_item (group, (GtkMenuItem*) widget->gobj(), 0);
+ gtk_application_add_app_menu_item (group, (GtkMenuItem*) widget->gobj(), 0);
}
widget = ActionManager::get_widget ("/ui/Main/WindowMenu/ToggleOptionsEditor");
if (widget) {
- ige_mac_menu_add_app_menu_item (group, (GtkMenuItem*) widget->gobj(), 0);
+ gtk_application_add_app_menu_item (group, (GtkMenuItem*) widget->gobj(), 0);
}
[ NSApp finishLaunching ];
@@ -118,7 +114,6 @@ ARDOUR_UI::platform_setup ()
/* this will stick around for ever ... is that OK ? */
[ [AppNotificationObject alloc] init];
-
[ NSApp setDelegate: [ArdourApplicationDelegate new]];
}
diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc
index 49bd38b6f4..22b6cfa9d5 100644
--- a/gtk2_ardour/utils.cc
+++ b/gtk2_ardour/utils.cc
@@ -400,12 +400,6 @@ set_color (Gdk::Color& c, int rgb)
c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
}
-#ifdef GTKOSX
-extern "C" {
- gboolean gdk_quartz_possibly_forward (GdkEvent*);
-}
-#endif
-
bool
relay_key_press (GdkEventKey* ev, Gtk::Window* win)
{
@@ -424,7 +418,7 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
bool special_handling_of_unmodified_accelerators = false;
bool allow_activating = true;
-#undef DEBUG_ACCELERATOR_HANDLING
+#define DEBUG_ACCELERATOR_HANDLING
#ifdef DEBUG_ACCELERATOR_HANDLING
//bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
bool debug=true;
@@ -447,7 +441,13 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
#ifdef DEBUG_ACCELERATOR_HANDLING
if (debug) {
- cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
+ cerr << "Win = " << win << " Key event: code = " << ev->keyval << " name = " << gdk_keyval_name (ev->keyval) << " state = " << hex << ev->state << dec
+ << " ctrl " << ((ev->state & GDK_CONTROL_MASK) ? 1 : 0)
+ << " alt " << ((ev->state & GDK_MOD1_MASK) ? 1 : 0)
+ << " shift " << ((ev->state & GDK_SHIFT_MASK) ? 1 : 0)
+ << " cmd/meta " << ((ev->state & GDK_META_MASK) ? 1 : 0)
+ << " lock " << ((ev->state & GDK_LOCK_MASK) ? 1 : 0)
+ << " special handling ? "
<< special_handling_of_unmodified_accelerators
<< " magic widget focus ? "
<< Keyboard::some_magic_widget_has_focus()
@@ -499,20 +499,19 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
uint32_t fakekey = ev->keyval;
if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
- if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
- return true;
+#ifdef DEBUG_ACCELERATOR_HANDLING
+ if (debug) {
+ cerr << "\tactivate without special handling of unmodified accels\n";
}
-
-#ifdef GTKOSX
- if (allow_activating) {
- int oldval = ev->keyval;
- ev->keyval = fakekey;
- if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
- return true;
+#endif
+ if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+ if (debug) {
+ cerr << "\tactivation handled key\n";
}
- ev->keyval = oldval;
- }
#endif
+ return true;
+ }
}
}
@@ -531,12 +530,13 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
#endif
if (allow_activating) {
-#ifdef GTKOSX
- if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
- return true;
- }
-#endif
if (gtk_window_activate_key (win, ev)) {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+ if (debug) {
+ cerr << "\tactivation handled key\n";
+ }
+#endif
+
return true;
}
}
@@ -564,12 +564,6 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
#endif
if (allow_activating) {
-
-#ifdef GTKOSX
- if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
- return true;
- }
-#endif
return gtk_window_activate_key (win, ev);
}
diff --git a/libs/gtkmm2ext/SConscript b/libs/gtkmm2ext/SConscript
index 84c523a5f7..712c8c1e1e 100644
--- a/libs/gtkmm2ext/SConscript
+++ b/libs/gtkmm2ext/SConscript
@@ -60,16 +60,23 @@ textviewer.cc
utils.cc
version.cc
window_title.cc
+gtkapplication.c
+""")
+
+x11_files=Split("""
+gtkapplication_x11.c
""")
gtkosx_files=Split("""
-sync-menu.c
+gtkapplication_quartz.mm
""")
if gtkmm2ext['GTKOSX']:
extra_sources += gtkosx_files
gtkmm2ext.Append (CCFLAGS="-DTOP_MENUBAR -DGTKOSX")
- gtkmm2ext.Append (LINKFLAGS="-framework Carbon")
+ gtkmm2ext.Append (LINKFLAGS=" -framework AppKit")
+else:
+ extra_sources += x11_files
gtkmm2ext.VersionBuild(['version.cc','gtkmm2ext/version.h'], [])
@@ -88,6 +95,6 @@ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ar
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'i18n.h', 'gettext.h'] +
gtkmm2ext_files +
- gtkosx_files +
+ gtkosx_files + x11_files +
glob.glob('po/*.po') +
glob.glob('gtkmm2ext/*.h')))
diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc
index 33f6bbc864..8064b6cae7 100644
--- a/libs/gtkmm2ext/gtk_ui.cc
+++ b/libs/gtkmm2ext/gtk_ui.cc
@@ -33,6 +33,8 @@
#include <pbd/pthread_utils.h>
#include <pbd/stacktrace.h>
+#include <gtkmm2ext/gtkapplication.h>
+
#include <gtkmm2ext/gtk_ui.h>
#include <gtkmm2ext/textviewer.h>
#include <gtkmm2ext/popup.h>
@@ -103,6 +105,7 @@ UI::UI (string namestr, int *argc, char ***argv)
register_thread (pthread_self(), X_("GUI"));
+ gtk_application_init ();
}
UI::~UI ()
@@ -585,30 +588,12 @@ UI::popup_error (const std::string& text)
}
#ifdef GTKOSX
-extern "C" {
- int gdk_quartz_in_carbon_menu_event_handler ();
-}
+extern int gdk_quartz_in_menu_event_handler ();
#endif
void
UI::flush_pending ()
{
-#ifdef GTKOSX
- /* as of february 11th 2008, gtk/osx has a problem in that mac menu events
- are handled using Carbon with an "internal" event handling system that
- doesn't pass things back to the glib/gtk main loop. this makes
- gtk_main_iteration() block if we call it while in a menu event handler
- because glib gets confused and thinks there are two threads running
- g_main_poll_func().
-
- this hack (relies on code in gtk2_ardour/sync-menu.c) works
- around that.
- */
-
- if (gdk_quartz_in_carbon_menu_event_handler()) {
- return;
- }
-#endif
if (!caller_is_ui_thread()) {
error << "non-UI threads cannot call UI::flush_pending()"
<< endmsg;
@@ -625,7 +610,6 @@ UI::flush_pending ()
bool
UI::just_hide_it (GdkEventAny *ev, Window *win)
{
- cerr << "++++ JUST HIDING " << win->get_window() << endl;
win->hide ();
return true;
}
diff --git a/libs/gtkmm2ext/gtkapplication.c b/libs/gtkmm2ext/gtkapplication.c
new file mode 100644
index 0000000000..c0eb36a07d
--- /dev/null
+++ b/libs/gtkmm2ext/gtkapplication.c
@@ -0,0 +1,36 @@
+/* GTK+ Integration with platform-specific application-wide features
+ * such as the OS X menubar and application delegate concepts.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2009 Paul Davis
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtkmm2ext/gtkapplication.h>
+#include <gtkmm2ext/gtkapplication-private.h>
+
+GList *_gtk_application_menu_groups = NULL;
+
+GtkApplicationMenuGroup *
+gtk_application_add_app_menu_group (void)
+{
+ GtkApplicationMenuGroup *group = g_slice_new0 (GtkApplicationMenuGroup);
+ _gtk_application_menu_groups = g_list_append (_gtk_application_menu_groups, group);
+ return group;
+}
+
diff --git a/libs/gtkmm2ext/gtkapplication_quartz.mm b/libs/gtkmm2ext/gtkapplication_quartz.mm
new file mode 100644
index 0000000000..e5b31530ae
--- /dev/null
+++ b/libs/gtkmm2ext/gtkapplication_quartz.mm
@@ -0,0 +1,1370 @@
+/* GTK+ application-level integration for the Mac OS X/Cocoa
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2009 Paul Davis
+ *
+ * This is a reimplementation in Cocoa of the sync-menu.c concept
+ * from Imendio, although without the "set quit menu" API since
+ * a Cocoa app needs to handle termination anyway.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <AppKit/NSMenu.h>
+#import <AppKit/NSMenuItem.h>
+#import <AppKit/NSCell.h>
+#import <AppKit/NSEvent.h>
+#import <AppKit/NSApplication.h>
+#import <Foundation/NSString.h>
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtkmm2ext/gtkapplication.h>
+#include <gtkmm2ext/gtkapplication-private.h>
+
+/* TODO
+ *
+ * - Sync adding/removing/reordering items
+ * - Create on demand? (can this be done with gtk+? ie fill in menu
+ items when the menu is opened)
+ * - Figure out what to do per app/window...
+ *
+ */
+
+static guint
+gdk_quartz_keyval_to_ns_keyval (guint keyval)
+{
+ switch (keyval) {
+ case GDK_BackSpace:
+ return NSBackspaceCharacter;
+ case GDK_Delete:
+ return NSDeleteFunctionKey;
+ case GDK_Pause:
+ return NSPauseFunctionKey;
+ case GDK_Scroll_Lock:
+ return NSScrollLockFunctionKey;
+ case GDK_Sys_Req:
+ return NSSysReqFunctionKey;
+ case GDK_Home:
+ return NSHomeFunctionKey;
+ case GDK_Left:
+ case GDK_leftarrow:
+ return NSLeftArrowFunctionKey;
+ case GDK_Up:
+ case GDK_uparrow:
+ return NSUpArrowFunctionKey;
+ case GDK_Right:
+ case GDK_rightarrow:
+ return NSRightArrowFunctionKey;
+ case GDK_Down:
+ case GDK_downarrow:
+ return NSDownArrowFunctionKey;
+ case GDK_Page_Up:
+ return NSPageUpFunctionKey;
+ case GDK_Page_Down:
+ return NSPageDownFunctionKey;
+ case GDK_End:
+ return NSEndFunctionKey;
+ case GDK_Begin:
+ return NSBeginFunctionKey;
+ case GDK_Select:
+ return NSSelectFunctionKey;
+ case GDK_Print:
+ return NSPrintFunctionKey;
+ case GDK_Execute:
+ return NSExecuteFunctionKey;
+ case GDK_Insert:
+ return NSInsertFunctionKey;
+ case GDK_Undo:
+ return NSUndoFunctionKey;
+ case GDK_Redo:
+ return NSRedoFunctionKey;
+ case GDK_Menu:
+ return NSMenuFunctionKey;
+ case GDK_Find:
+ return NSFindFunctionKey;
+ case GDK_Help:
+ return NSHelpFunctionKey;
+ case GDK_Break:
+ return NSBreakFunctionKey;
+ case GDK_Mode_switch:
+ return NSModeSwitchFunctionKey;
+ case GDK_F1:
+ return NSF1FunctionKey;
+ case GDK_F2:
+ return NSF2FunctionKey;
+ case GDK_F3:
+ return NSF3FunctionKey;
+ case GDK_F4:
+ return NSF4FunctionKey;
+ case GDK_F5:
+ return NSF5FunctionKey;
+ case GDK_F6:
+ return NSF6FunctionKey;
+ case GDK_F7:
+ return NSF7FunctionKey;
+ case GDK_F8:
+ return NSF8FunctionKey;
+ case GDK_F9:
+ return NSF9FunctionKey;
+ case GDK_F10:
+ return NSF10FunctionKey;
+ case GDK_F11:
+ return NSF11FunctionKey;
+ case GDK_F12:
+ return NSF12FunctionKey;
+ case GDK_F13:
+ return NSF13FunctionKey;
+ case GDK_F14:
+ return NSF14FunctionKey;
+ case GDK_F15:
+ return NSF15FunctionKey;
+ case GDK_F16:
+ return NSF16FunctionKey;
+ case GDK_F17:
+ return NSF17FunctionKey;
+ case GDK_F18:
+ return NSF18FunctionKey;
+ case GDK_F19:
+ return NSF19FunctionKey;
+ case GDK_F20:
+ return NSF20FunctionKey;
+ case GDK_F21:
+ return NSF21FunctionKey;
+ case GDK_F22:
+ return NSF22FunctionKey;
+ case GDK_F23:
+ return NSF23FunctionKey;
+ case GDK_F24:
+ return NSF24FunctionKey;
+ case GDK_F25:
+ return NSF25FunctionKey;
+ case GDK_F26:
+ return NSF26FunctionKey;
+ case GDK_F27:
+ return NSF27FunctionKey;
+ case GDK_F28:
+ return NSF28FunctionKey;
+ case GDK_F29:
+ return NSF29FunctionKey;
+ case GDK_F30:
+ return NSF30FunctionKey;
+ case GDK_F31:
+ return NSF31FunctionKey;
+ case GDK_F32:
+ return NSF32FunctionKey;
+ case GDK_F33:
+ return NSF33FunctionKey;
+ case GDK_F34:
+ return NSF34FunctionKey;
+ case GDK_F35:
+ return NSF35FunctionKey;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static gboolean
+keyval_is_keypad (guint keyval)
+{
+ switch (keyval) {
+ case GDK_KP_F1:
+ case GDK_KP_F2:
+ case GDK_KP_F3:
+ case GDK_KP_F4:
+ case GDK_KP_Home:
+ case GDK_KP_Left:
+ case GDK_KP_Up:
+ case GDK_KP_Right:
+ case GDK_KP_Down:
+ case GDK_KP_Page_Up:
+ case GDK_KP_Page_Down:
+ case GDK_KP_End:
+ case GDK_KP_Begin:
+ case GDK_KP_Insert:
+ case GDK_KP_Delete:
+ case GDK_KP_Equal:
+ case GDK_KP_Multiply:
+ case GDK_KP_Add:
+ case GDK_KP_Separator:
+ case GDK_KP_Subtract:
+ case GDK_KP_Decimal:
+ case GDK_KP_Divide:
+ case GDK_KP_0:
+ case GDK_KP_1:
+ case GDK_KP_2:
+ case GDK_KP_3:
+ case GDK_KP_4:
+ case GDK_KP_5:
+ case GDK_KP_6:
+ case GDK_KP_7:
+ case GDK_KP_8:
+ case GDK_KP_9:
+ return TRUE;
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static guint
+keyval_keypad_nonkeypad_equivalent (guint keyval)
+{
+ switch (keyval) {
+ case GDK_KP_F1:
+ return GDK_F1;
+ case GDK_KP_F2:
+ return GDK_F2;
+ case GDK_KP_F3:
+ return GDK_F3;
+ case GDK_KP_F4:
+ return GDK_F4;
+ case GDK_KP_Home:
+ return GDK_Home;
+ case GDK_KP_Left:
+ return GDK_Left;
+ case GDK_KP_Up:
+ return GDK_Up;
+ case GDK_KP_Right:
+ return GDK_Right;
+ case GDK_KP_Down:
+ return GDK_Down;
+ case GDK_KP_Page_Up:
+ return GDK_Page_Up;
+ case GDK_KP_Page_Down:
+ return GDK_Page_Down;
+ case GDK_KP_End:
+ return GDK_End;
+ case GDK_KP_Begin:
+ return GDK_Begin;
+ case GDK_KP_Insert:
+ return GDK_Insert;
+ case GDK_KP_Delete:
+ return GDK_Delete;
+ case GDK_KP_Equal:
+ return GDK_equal;
+ case GDK_KP_Multiply:
+ return GDK_asterisk;
+ case GDK_KP_Add:
+ return GDK_plus;
+ case GDK_KP_Subtract:
+ return GDK_minus;
+ case GDK_KP_Decimal:
+ return GDK_period;
+ case GDK_KP_Divide:
+ return GDK_slash;
+ case GDK_KP_0:
+ return GDK_0;
+ case GDK_KP_1:
+ return GDK_1;
+ case GDK_KP_2:
+ return GDK_2;
+ case GDK_KP_3:
+ return GDK_3;
+ case GDK_KP_4:
+ return GDK_4;
+ case GDK_KP_5:
+ return GDK_5;
+ case GDK_KP_6:
+ return GDK_6;
+ case GDK_KP_7:
+ return GDK_7;
+ case GDK_KP_8:
+ return GDK_8;
+ case GDK_KP_9:
+ return GDK_9;
+ default:
+ break;
+ }
+
+ return GDK_VoidSymbol;
+}
+
+static const gchar*
+gdk_quartz_keyval_to_string (guint keyval)
+{
+ switch (keyval) {
+ case GDK_space:
+ return " ";
+ case GDK_exclam:
+ return "!";
+ case GDK_quotedbl:
+ return "\"";
+ case GDK_numbersign:
+ return "#";
+ case GDK_dollar:
+ return "$";
+ case GDK_percent:
+ return "%";
+ case GDK_ampersand:
+ return "&";
+ case GDK_apostrophe:
+ return "'";
+ case GDK_parenleft:
+ return "(";
+ case GDK_parenright:
+ return ")";
+ case GDK_asterisk:
+ return "*";
+ case GDK_plus:
+ return "+";
+ case GDK_comma:
+ return ",";
+ case GDK_minus:
+ return "-";
+ case GDK_period:
+ return ".";
+ case GDK_slash:
+ return "/";
+ case GDK_0:
+ return "0";
+ case GDK_1:
+ return "1";
+ case GDK_2:
+ return "2";
+ case GDK_3:
+ return "3";
+ case GDK_4:
+ return "4";
+ case GDK_5:
+ return "5";
+ case GDK_6:
+ return "6";
+ case GDK_7:
+ return "7";
+ case GDK_8:
+ return "8";
+ case GDK_9:
+ return "9";
+ case GDK_colon:
+ return ":";
+ case GDK_semicolon:
+ return ";";
+ case GDK_less:
+ return "<";
+ case GDK_equal:
+ return "=";
+ case GDK_greater:
+ return ">";
+ case GDK_question:
+ return "?";
+ case GDK_at:
+ return "@";
+ case GDK_A:
+ case GDK_a:
+ return "a";
+ case GDK_B:
+ case GDK_b:
+ return "b";
+ case GDK_C:
+ case GDK_c:
+ return "c";
+ case GDK_D:
+ case GDK_d:
+ return "d";
+ case GDK_E:
+ case GDK_e:
+ return "e";
+ case GDK_F:
+ case GDK_f:
+ return "f";
+ case GDK_G:
+ case GDK_g:
+ return "g";
+ case GDK_H:
+ case GDK_h:
+ return "h";
+ case GDK_I:
+ case GDK_i:
+ return "i";
+ case GDK_J:
+ case GDK_j:
+ return "j";
+ case GDK_K:
+ case GDK_k:
+ return "k";
+ case GDK_L:
+ case GDK_l:
+ return "l";
+ case GDK_M:
+ case GDK_m:
+ return "m";
+ case GDK_N:
+ case GDK_n:
+ return "n";
+ case GDK_O:
+ case GDK_o:
+ return "o";
+ case GDK_P:
+ case GDK_p:
+ return "p";
+ case GDK_Q:
+ case GDK_q:
+ return "q";
+ case GDK_R:
+ case GDK_r:
+ return "r";
+ case GDK_S:
+ case GDK_s:
+ return "s";
+ case GDK_T:
+ case GDK_t:
+ return "t";
+ case GDK_U:
+ case GDK_u:
+ return "u";
+ case GDK_V:
+ case GDK_v:
+ return "v";
+ case GDK_W:
+ case GDK_w:
+ return "w";
+ case GDK_X:
+ case GDK_x:
+ return "x";
+ case GDK_Y:
+ case GDK_y:
+ return "y";
+ case GDK_Z:
+ case GDK_z:
+ return "z";
+ case GDK_bracketleft:
+ return "]";
+ case GDK_backslash:
+ return "\\";
+ case GDK_bracketright:
+ return "[";
+ case GDK_asciicircum:
+ return "^";
+ case GDK_underscore:
+ return "_";
+ case GDK_grave:
+ return "`";
+ case GDK_braceleft:
+ return "{";
+ case GDK_bar:
+ return "|";
+ case GDK_braceright:
+ return "}";
+ case GDK_asciitilde:
+ return "~";
+ default:
+ break;
+ }
+ return NULL;
+};
+
+static gboolean
+keyval_is_uppercase (guint keyval)
+{
+ switch (keyval) {
+ case GDK_A:
+ case GDK_B:
+ case GDK_C:
+ case GDK_D:
+ case GDK_E:
+ case GDK_F:
+ case GDK_G:
+ case GDK_H:
+ case GDK_I:
+ case GDK_J:
+ case GDK_K:
+ case GDK_L:
+ case GDK_M:
+ case GDK_N:
+ case GDK_O:
+ case GDK_P:
+ case GDK_Q:
+ case GDK_R:
+ case GDK_S:
+ case GDK_T:
+ case GDK_U:
+ case GDK_V:
+ case GDK_W:
+ case GDK_X:
+ case GDK_Y:
+ case GDK_Z:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ return FALSE;
+}
+
+/* gtk/osx has a problem in that mac main menu events
+ are handled using an "internal" event handling system that
+ doesn't pass things back to the glib/gtk main loop. if we call
+ gtk_main_iteration() block while in a menu event handler, then
+ glib gets confused and thinks there are two threads running
+ g_main_poll_func(). apps call call gdk_quartz_in_menu_event_handler()
+ if they need to check this.
+ */
+
+static int _in_menu_event_handler = 0;
+
+int
+gdk_quartz_in_menu_event_handler ()
+{
+ return _in_menu_event_handler;
+}
+
+static gboolean
+idle_call_activate (gpointer data)
+{
+ gtk_menu_item_activate ((GtkMenuItem*) data);
+ return FALSE;
+}
+
+@interface GNSMenuItem : NSMenuItem
+{
+ @public
+ GtkMenuItem* gtk_menu_item;
+ GClosure *accel_closure;
+}
+- (id) initWithTitle:(NSString*) title andGtkWidget:(GtkMenuItem*) w;
+- (void) activate:(id) sender;
+@end
+
+@implementation GNSMenuItem
+- (id) initWithTitle:(NSString*) title andGtkWidget:(GtkMenuItem*) w
+{
+ /* All menu items have the action "activate", which will be handled by this child class
+ */
+
+ self = [ super initWithTitle:title action:@selector(activate:) keyEquivalent:@"" ];
+
+ if (self) {
+ /* make this handle its own action */
+ [ self setTarget:self ];
+ gtk_menu_item = w;
+ accel_closure = 0;
+ }
+ return self;
+}
+- (void) activate:(id) sender
+{
+ printf ("will call %s\n", [[self title] cStringUsingEncoding:NSUTF8StringEncoding]);
+ g_idle_add (idle_call_activate, gtk_menu_item);
+}
+@end
+
+static void push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
+ NSMenu *menu,
+ gboolean toplevel,
+ gboolean debug);
+
+/*
+ * utility functions
+ */
+
+static GtkWidget *
+find_menu_label (GtkWidget *widget)
+{
+ GtkWidget *label = NULL;
+
+ if (GTK_IS_LABEL (widget))
+ return widget;
+
+ if (GTK_IS_CONTAINER (widget))
+ {
+ GList *children;
+ GList *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (widget));
+
+ for (l = children; l; l = l->next)
+ {
+ label = find_menu_label ((GtkWidget*) l->data);
+ if (label)
+ break;
+ }
+
+ g_list_free (children);
+ }
+
+ return label;
+}
+
+static const gchar *
+get_menu_label_text (GtkWidget *menu_item,
+ GtkWidget **label)
+{
+ GtkWidget *my_label;
+
+ my_label = find_menu_label (menu_item);
+ if (label)
+ *label = my_label;
+
+ if (my_label)
+ return gtk_label_get_text (GTK_LABEL (my_label));
+
+ return NULL;
+}
+
+static gboolean
+accel_find_func (GtkAccelKey *key,
+ GClosure *closure,
+ gpointer data)
+{
+ return (GClosure *) data == closure;
+}
+
+
+/*
+ * CocoaMenu functions
+ */
+
+static GQuark cocoa_menu_quark = 0;
+
+static NSMenu *
+cocoa_menu_get (GtkWidget *widget)
+{
+ return (NSMenu*) g_object_get_qdata (G_OBJECT (widget), cocoa_menu_quark);
+}
+
+static void
+cocoa_menu_free (gpointer *ptr)
+{
+ NSMenu* menu = (NSMenu*) ptr;
+ [menu release];
+}
+
+static void
+cocoa_menu_connect (GtkWidget *menu,
+ NSMenu* cocoa_menu)
+{
+ [cocoa_menu retain];
+ g_object_set_qdata_full (G_OBJECT (menu), cocoa_menu_quark,
+ cocoa_menu,
+ (GDestroyNotify) cocoa_menu_free);
+}
+
+/*
+ * NSMenuItem functions
+ */
+
+static GQuark cocoa_menu_item_quark = 0;
+static void cocoa_menu_item_connect (GtkWidget* menu_item,
+ GNSMenuItem* cocoa_menu_item,
+ GtkWidget *label);
+
+static void
+cocoa_menu_item_free (gpointer *ptr)
+{
+ GNSMenuItem* item = (GNSMenuItem*) ptr;
+ [item release];
+}
+
+static GNSMenuItem *
+cocoa_menu_item_get (GtkWidget *widget)
+{
+ return (GNSMenuItem*) g_object_get_qdata (G_OBJECT (widget), cocoa_menu_item_quark);
+}
+
+static void
+cocoa_menu_item_update_state (NSMenuItem* cocoa_item,
+ GtkWidget *widget)
+{
+ gboolean sensitive;
+ gboolean visible;
+
+ g_object_get (widget,
+ "sensitive", &sensitive,
+ "visible", &visible,
+ NULL);
+
+ if (!sensitive)
+ [cocoa_item setEnabled:NO];
+ else
+ [cocoa_item setEnabled:YES];
+
+#if 0
+ // requires OS X 10.5 or later
+ if (!visible)
+ [cocoa_item setHidden:YES];
+ else
+ [cocoa_item setHidden:NO];
+#endif
+}
+
+static void
+cocoa_menu_item_update_active (NSMenuItem *cocoa_item,
+ GtkWidget *widget)
+{
+ gboolean active;
+
+ g_object_get (widget, "active", &active, NULL);
+
+ if (active)
+ [cocoa_item setState:NSOnState];
+ else
+ [cocoa_item setState:NSOffState];
+}
+
+static void
+cocoa_menu_item_update_submenu (NSMenuItem *cocoa_item,
+ GtkWidget *widget)
+{
+ GtkWidget *submenu;
+
+ g_return_if_fail (cocoa_item != NULL);
+ g_return_if_fail (widget != NULL);
+
+ submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+
+ if (submenu)
+ {
+ GtkWidget* label = NULL;
+ const gchar *label_text;
+ NSMenu* cocoa_submenu;
+
+ label_text = get_menu_label_text (widget, &label);
+
+ /* create a new nsmenu to hold the GTK menu */
+
+ if (label_text)
+ cocoa_submenu = [ [ NSMenu alloc ] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]];
+ else
+ cocoa_submenu = [ [ NSMenu alloc ] initWithTitle:@""];
+
+ [cocoa_submenu setAutoenablesItems:NO];
+ cocoa_menu_connect (submenu, cocoa_submenu);
+
+ /* connect the new nsmenu to the passed-in item (which lives in
+ the parent nsmenu)
+ (Note: this will release any pre-existing version of this submenu)
+ */
+ [ cocoa_item setSubmenu:cocoa_submenu];
+
+ /* and push the GTK menu into the submenu */
+ push_menu_shell_to_nsmenu (GTK_MENU_SHELL (submenu), cocoa_submenu, FALSE, FALSE);
+
+ [ cocoa_submenu release ];
+ }
+}
+
+static void
+cocoa_menu_item_update_label (NSMenuItem *cocoa_item,
+ GtkWidget *widget)
+{
+ const gchar *label_text;
+
+ g_return_if_fail (cocoa_item != NULL);
+ g_return_if_fail (widget != NULL);
+
+ label_text = get_menu_label_text (widget, NULL);
+ if (label_text)
+ [cocoa_item setTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]];
+ else
+ [cocoa_item setTitle:@""];
+}
+
+static void
+cocoa_menu_item_update_accelerator (NSMenuItem *cocoa_item,
+ GtkWidget *widget)
+{
+ GtkWidget *label;
+
+ g_return_if_fail (cocoa_item != NULL);
+ g_return_if_fail (widget != NULL);
+
+ /* important note: this function doesn't do anything to actually change
+ key handling. Its goal is to get Cocoa to display the correct
+ accelerator as part of a menu item. Actual accelerator handling
+ is still done by GTK, so this is more cosmetic than it may
+ appear.
+ */
+
+ get_menu_label_text (widget, &label);
+
+ if (GTK_IS_ACCEL_LABEL (label) &&
+ GTK_ACCEL_LABEL (label)->accel_closure)
+ {
+ GtkAccelKey *key;
+
+ key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group,
+ accel_find_func,
+ GTK_ACCEL_LABEL (label)->accel_closure);
+
+ if (key &&
+ key->accel_key &&
+ key->accel_flags & GTK_ACCEL_VISIBLE)
+ {
+ guint modifiers = 0;
+ const gchar* str = NULL;
+ guint actual_key = key->accel_key;
+
+ if (keyval_is_keypad (actual_key)) {
+ if ((actual_key = keyval_keypad_nonkeypad_equivalent (actual_key)) == GDK_VoidSymbol) {
+ /* GDK_KP_Separator */
+ [cocoa_item setKeyEquivalent:@""];
+ return;
+ }
+ modifiers |= NSNumericPadKeyMask;
+ }
+
+ /* if we somehow got here with GDK_A ... GDK_Z rather than GDK_a ... GDK_z, then take note
+ of that and make sure we use a shift modifier.
+ */
+
+ if (keyval_is_uppercase (actual_key)) {
+ modifiers |= NSShiftKeyMask;
+ }
+
+ str = gdk_quartz_keyval_to_string (actual_key);
+
+ if (str) {
+ unichar ukey = str[0];
+ [cocoa_item setKeyEquivalent:[NSString stringWithCharacters:&ukey length:1]];
+ } else {
+ unichar ukey = gdk_quartz_keyval_to_ns_keyval (actual_key);
+ if (ukey != 0) {
+ [cocoa_item setKeyEquivalent:[NSString stringWithCharacters:&ukey length:1]];
+ } else {
+ /* cannot map this key to Cocoa key equivalent */
+ [cocoa_item setKeyEquivalent:@""];
+ return;
+ }
+ }
+
+ if (key->accel_mods || modifiers)
+ {
+ if (key->accel_mods & GDK_SHIFT_MASK) {
+ modifiers |= NSShiftKeyMask;
+ }
+
+ /* gdk/quartz maps Alt/Option to Mod1 */
+
+ if (key->accel_mods & (GDK_MOD1_MASK)) {
+ modifiers |= NSAlternateKeyMask;
+ }
+
+ if (key->accel_mods & GDK_CONTROL_MASK) {
+ modifiers |= NSControlKeyMask;
+ }
+
+ /* gdk/quartz maps Command to Meta (XXX check this - it may move to SUPER at some point) */
+
+ if (key->accel_mods & GDK_META_MASK) {
+ modifiers |= NSCommandKeyMask;
+ }
+ }
+
+ [cocoa_item setKeyEquivalentModifierMask:modifiers];
+ return;
+ }
+ }
+
+ /* otherwise, clear the menu shortcut */
+ [cocoa_item setKeyEquivalent:@""];
+}
+
+static void
+cocoa_menu_item_accel_changed (GtkAccelGroup *accel_group,
+ guint keyval,
+ GdkModifierType modifier,
+ GClosure *accel_closure,
+ GtkWidget *widget)
+{
+ GNSMenuItem *cocoa_item = cocoa_menu_item_get (widget);
+ GtkWidget *label;
+
+ get_menu_label_text (widget, &label);
+
+ if (GTK_IS_ACCEL_LABEL (label) &&
+ GTK_ACCEL_LABEL (label)->accel_closure == accel_closure)
+ cocoa_menu_item_update_accelerator (cocoa_item, widget);
+}
+
+static void
+cocoa_menu_item_update_accel_closure (GNSMenuItem *cocoa_item,
+ GtkWidget *widget)
+{
+ GtkAccelGroup *group;
+ GtkWidget *label;
+
+ get_menu_label_text (widget, &label);
+
+ if (cocoa_item->accel_closure)
+ {
+ group = gtk_accel_group_from_accel_closure (cocoa_item->accel_closure);
+
+ g_signal_handlers_disconnect_by_func (group,
+ (void*) cocoa_menu_item_accel_changed,
+ widget);
+
+ g_closure_unref (cocoa_item->accel_closure);
+ cocoa_item->accel_closure = NULL;
+ }
+
+ if (GTK_IS_ACCEL_LABEL (label)) {
+ cocoa_item->accel_closure = GTK_ACCEL_LABEL (label)->accel_closure;
+ }
+
+ if (cocoa_item->accel_closure)
+ {
+ g_closure_ref (cocoa_item->accel_closure);
+
+ group = gtk_accel_group_from_accel_closure (cocoa_item->accel_closure);
+
+ g_signal_connect_object (group, "accel-changed",
+ G_CALLBACK (cocoa_menu_item_accel_changed),
+ widget, (GConnectFlags) 0);
+ }
+
+ cocoa_menu_item_update_accelerator (cocoa_item, widget);
+}
+
+static void
+cocoa_menu_item_notify_label (GObject *object,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ GNSMenuItem *cocoa_item = cocoa_menu_item_get (GTK_WIDGET (object));
+
+ if (!strcmp (pspec->name, "label"))
+ {
+ cocoa_menu_item_update_label (cocoa_item,
+ GTK_WIDGET (object));
+ }
+ else if (!strcmp (pspec->name, "accel-closure"))
+ {
+ cocoa_menu_item_update_accel_closure (cocoa_item,
+ GTK_WIDGET (object));
+ }
+}
+
+static void
+cocoa_menu_item_notify (GObject *object,
+ GParamSpec *pspec,
+ NSMenuItem *cocoa_item)
+{
+ if (!strcmp (pspec->name, "sensitive") ||
+ !strcmp (pspec->name, "visible"))
+ {
+ cocoa_menu_item_update_state (cocoa_item, GTK_WIDGET (object));
+ }
+ else if (!strcmp (pspec->name, "active"))
+ {
+ cocoa_menu_item_update_active (cocoa_item, GTK_WIDGET (object));
+ }
+ else if (!strcmp (pspec->name, "submenu"))
+ {
+ cocoa_menu_item_update_submenu (cocoa_item, GTK_WIDGET (object));
+ }
+}
+
+static void
+cocoa_menu_item_connect (GtkWidget* menu_item,
+ GNSMenuItem* cocoa_item,
+ GtkWidget *label)
+{
+ GNSMenuItem* old_item = cocoa_menu_item_get (menu_item);
+
+ [cocoa_item retain];
+
+ g_object_set_qdata_full (G_OBJECT (menu_item), cocoa_menu_item_quark,
+ cocoa_item,
+ (GDestroyNotify) cocoa_menu_item_free);
+
+ if (!old_item) {
+
+ g_signal_connect (menu_item, "notify",
+ G_CALLBACK (cocoa_menu_item_notify),
+ cocoa_item);
+
+ if (label)
+ g_signal_connect_swapped (label, "notify::label",
+ G_CALLBACK (cocoa_menu_item_notify_label),
+ menu_item);
+ }
+
+ gtk_widget_hide (GTK_WIDGET (menu_item));
+}
+
+static void
+sync_menu_item (NSMenuItem* cocoa_item, GtkWidget* menu_item)
+{
+ if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
+ [cocoa_item setState:NSOffState];
+
+#if 0
+// requires OS X 10.5 or later
+ if (!GTK_WIDGET_VISIBLE (menu_item))
+ [cocoa_item setHidden:YES];
+#endif
+
+ if (GTK_IS_CHECK_MENU_ITEM (menu_item))
+ cocoa_menu_item_update_active (cocoa_item, menu_item);
+
+ if (!GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
+ cocoa_menu_item_update_accel_closure (cocoa_item, menu_item);
+
+ if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
+ cocoa_menu_item_update_submenu (cocoa_item, menu_item);
+}
+
+static void
+add_menu_item (NSMenu* cocoa_menu, GtkWidget* menu_item)
+{
+ GtkWidget* label = NULL;
+ GNSMenuItem *cocoa_item;
+
+ if (!GTK_WIDGET_VISIBLE (menu_item))
+ return;
+
+ if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
+ cocoa_item = [NSMenuItem separatorItem];
+ else {
+ const gchar* label_text = get_menu_label_text (menu_item, &label);
+
+ if (label_text)
+ cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:[ [ NSString alloc] initWithCString:label_text encoding:NSUTF8StringEncoding]
+ andGtkWidget:(GtkMenuItem*)menu_item];
+ else
+ cocoa_item = [ [ GNSMenuItem alloc] initWithTitle:@"" andGtkWidget:(GtkMenuItem*)menu_item];
+ }
+
+ /* connect GtkMenuItem and NSMenuItem so that we can notice changes to accel/label/submenu etc. */
+ cocoa_menu_item_connect (menu_item, (GNSMenuItem*) cocoa_item, label);
+
+ [ cocoa_item setEnabled:YES];
+ [ cocoa_menu addItem:cocoa_item];
+
+ sync_menu_item (cocoa_item, menu_item);
+
+ [ cocoa_item release];
+}
+
+static void
+push_menu_shell_to_nsmenu (GtkMenuShell *menu_shell,
+ NSMenu* cocoa_menu,
+ gboolean toplevel,
+ gboolean debug)
+{
+ GList *children;
+ GList *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
+
+ for (l = children; l; l = l->next)
+ {
+ GtkWidget *menu_item = (GtkWidget*) l->data;
+ NSMenuItem* cocoa_item;
+
+ if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
+ continue;
+
+ if (g_object_get_data (G_OBJECT (menu_item), "gtk-empty-menu-item"))
+ continue;
+
+ cocoa_item = cocoa_menu_item_get (menu_item);
+
+ if (!cocoa_item)
+ add_menu_item (cocoa_menu, menu_item);
+ else
+ sync_menu_item (cocoa_item, menu_item);
+ }
+
+ g_list_free (children);
+}
+
+
+static gulong emission_hook_id = 0;
+
+static gboolean
+parent_set_emission_hook (GSignalInvocationHint *ihint,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer data)
+{
+ GtkWidget *instance = (GtkWidget*) g_value_get_object (param_values);
+
+ if (GTK_IS_MENU_ITEM (instance))
+ {
+ GtkWidget *previous_parent = (GtkWidget*) g_value_get_object (param_values + 1);
+ GtkWidget *menu_shell = NULL;
+
+ if (GTK_IS_MENU_SHELL (previous_parent))
+ {
+ menu_shell = previous_parent;
+ }
+ else if (GTK_IS_MENU_SHELL (instance->parent))
+ {
+ menu_shell = instance->parent;
+ }
+
+ if (menu_shell)
+ {
+ NSMenu *cocoa_menu = cocoa_menu_get (menu_shell);
+
+ if (cocoa_menu)
+ {
+#if 0
+ g_printerr ("%s: item %s %p (%s, %s)\n", G_STRFUNC,
+ previous_parent ? "removed from" : "added to",
+ menu_shell,
+ get_menu_label_text (instance, NULL),
+ g_type_name (G_TYPE_FROM_INSTANCE (instance)));
+#endif
+
+ push_menu_shell_to_nsmenu (GTK_MENU_SHELL (menu_shell),
+ cocoa_menu,
+ cocoa_menu == (NSMenu*) data,
+ FALSE);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+parent_set_emission_hook_remove (GtkWidget *widget,
+ gpointer data)
+{
+ g_signal_remove_emission_hook (g_signal_lookup ("parent-set",
+ GTK_TYPE_WIDGET),
+ emission_hook_id);
+}
+
+/* Building "standard" Cocoa/OS X menus */
+
+#warning You can safely ignore the next warning about a duplicate interface definition
+@interface NSApplication(NSWindowsMenu)
+ - (void)setAppleMenu:(NSMenu *)aMenu;
+@end
+
+static NSMenu* _main_menubar = 0;
+static NSMenu* _window_menu = 0;
+static NSMenu* _app_menu = 0;
+
+static int
+add_to_menubar (NSMenu *menu)
+{
+ NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
+ action:nil keyEquivalent:@""];
+ [dummyItem setSubmenu:menu];
+ [_main_menubar addItem:dummyItem];
+ [dummyItem release];
+ return 0;
+}
+
+static int
+add_to_app_menu (NSMenu *menu)
+{
+ NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
+ action:nil keyEquivalent:@""];
+ [dummyItem setSubmenu:menu];
+ [_app_menu addItem:dummyItem];
+ [dummyItem release];
+ return 0;
+}
+
+static int
+add_to_window_menu (NSMenu *menu)
+{
+ NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""
+ action:nil keyEquivalent:@""];
+ [dummyItem setSubmenu:menu];
+ [_window_menu addItem:dummyItem];
+ [dummyItem release];
+ return 0;
+}
+
+static int
+create_apple_menu ()
+{
+ NSMenuItem *menuitem;
+ // Create the application (Apple) menu.
+ _app_menu = [[NSMenu alloc] initWithTitle: @"Apple Menu"];
+
+ NSMenu *menuServices = [[NSMenu alloc] initWithTitle: @"Services"];
+ [NSApp setServicesMenu:menuServices];
+
+ [_app_menu addItem: [NSMenuItem separatorItem]];
+ menuitem = [[NSMenuItem alloc] initWithTitle: @"Services"
+ action:nil keyEquivalent:@""];
+ [menuitem setSubmenu:menuServices];
+ [_app_menu addItem: menuitem];
+ [menuitem release];
+ [_app_menu addItem: [NSMenuItem separatorItem]];
+ menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide"
+ action:@selector(hide:) keyEquivalent:@""];
+ [menuitem setTarget: NSApp];
+ [_app_menu addItem: menuitem];
+ [menuitem release];
+ menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
+ action:@selector(hideOtherApplications:) keyEquivalent:@""];
+ [menuitem setTarget: NSApp];
+ [_app_menu addItem: menuitem];
+ [menuitem release];
+ menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All"
+ action:@selector(unhideAllApplications:) keyEquivalent:@""];
+ [menuitem setTarget: NSApp];
+ [_app_menu addItem: menuitem];
+ [menuitem release];
+ [_app_menu addItem: [NSMenuItem separatorItem]];
+ menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit"
+ action:@selector(terminate:) keyEquivalent:@"q"];
+ [menuitem setTarget: NSApp];
+ [_app_menu addItem: menuitem];
+ [menuitem release];
+
+ [NSApp setAppleMenu:_app_menu];
+ add_to_menubar (_app_menu);
+
+ return 0;
+}
+
+static int
+create_window_menu ()
+{
+ _window_menu = [[NSMenu alloc] initWithTitle: @"Window"];
+
+ [_window_menu addItemWithTitle:@"Minimize"
+ action:@selector(performMiniaturize:) keyEquivalent:@""];
+ [_window_menu addItem: [NSMenuItem separatorItem]];
+ [_window_menu addItemWithTitle:@"Bring All to Front"
+ action:@selector(arrangeInFront:) keyEquivalent:@""];
+
+ [NSApp setWindowsMenu:_window_menu];
+ add_to_menubar(_window_menu);
+
+ return 0;
+}
+
+/*
+ * public functions
+ */
+
+extern "C" void
+gtk_application_set_menu_bar (GtkMenuShell *menu_shell)
+{
+ NSMenu* cocoa_menubar;
+
+ g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+
+ if (cocoa_menu_quark == 0)
+ cocoa_menu_quark = g_quark_from_static_string ("NSMenu");
+
+ if (cocoa_menu_item_quark == 0)
+ cocoa_menu_item_quark = g_quark_from_static_string ("NSMenuItem");
+
+ cocoa_menubar = [ [ NSApplication sharedApplication] mainMenu];
+
+ /* turn off auto-enabling for the menu - its silly and slow and
+ doesn't really make sense for a Gtk/Cocoa hybrid menu.
+ */
+
+ [cocoa_menubar setAutoenablesItems:NO];
+
+ emission_hook_id =
+ g_signal_add_emission_hook (g_signal_lookup ("parent-set",
+ GTK_TYPE_WIDGET),
+ 0,
+ parent_set_emission_hook,
+ cocoa_menubar, NULL);
+
+
+ g_signal_connect (menu_shell, "destroy",
+ G_CALLBACK (parent_set_emission_hook_remove),
+ NULL);
+
+ push_menu_shell_to_nsmenu (menu_shell, cocoa_menubar, TRUE, FALSE);
+}
+
+extern "C" void
+gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
+ GtkMenuItem *menu_item,
+ const gchar *label)
+{
+ // we know that the application menu is always the submenu of the first item in the main menu
+ NSMenu* mainMenu;
+ NSMenu *appMenu;
+ NSMenuItem *firstItem;
+ GList *list;
+ gint index = 0;
+
+ mainMenu = [NSApp mainMenu];
+ firstItem = [ mainMenu itemAtIndex:0];
+ appMenu = [ firstItem submenu ];
+
+ g_return_if_fail (group != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ for (list = _gtk_application_menu_groups; list; list = g_list_next (list))
+ {
+ GtkApplicationMenuGroup *list_group = (GtkApplicationMenuGroup*) list->data;
+
+ index += g_list_length (list_group->items);
+
+ /* adjust index for the separator between groups, but not
+ * before the first group
+ */
+ if (list_group->items && list->prev)
+ index++;
+
+ if (group == list_group)
+ {
+ /* add a separator before adding the first item, but not
+ * for the first group
+ */
+
+ if (!group->items && list->prev)
+ {
+ [appMenu insertItem:[NSMenuItem separatorItem] atIndex:index+1];
+ index++;
+ }
+
+ add_menu_item (appMenu, GTK_WIDGET(menu_item));
+ group->items = g_list_append (group->items, menu_item);
+ return;
+ }
+ }
+
+ if (!list)
+ g_warning ("%s: app menu group %p does not exist",
+ G_STRFUNC, group);
+}
+
+/* Basic setup */
+
+extern "C" int
+gtk_application_init ()
+{
+ _main_menubar = [[NSMenu alloc] initWithTitle: @""];
+ if (_main_menubar) {
+ [NSApp setMainMenu: _main_menubar];
+ create_apple_menu ();
+ create_window_menu ();
+ return 0;
+ }
+ return -1;
+}
+
+extern "C" void
+gtk_application_cleanup()
+{
+ [ _window_menu release ];
+ [ _app_menu release ];
+ [ _main_menubar release ];
+}
diff --git a/libs/gtkmm2ext/gtkapplication_x11.c b/libs/gtkmm2ext/gtkapplication_x11.c
new file mode 100644
index 0000000000..ccd2caa2cb
--- /dev/null
+++ b/libs/gtkmm2ext/gtkapplication_x11.c
@@ -0,0 +1,48 @@
+/* GTK+ Integration with platform-specific application-wide features
+ * such as the OS X menubar and application delegate concepts (for X11)
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2009 Paul Davis
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtkmm2ext/gtkapplication.h>
+
+int
+gtk_application_init ()
+{
+ return 0;
+}
+
+void gtk_application_cleanup ()
+{
+}
+
+void
+gtk_application_set_menu_bar (GtkMenuShell *menu_shell)
+{
+}
+
+void
+gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
+ GtkMenuItem *menu_item,
+ const gchar *label)
+{
+}
+
+
diff --git a/libs/gtkmm2ext/gtkmm2ext/sync-menu.h b/libs/gtkmm2ext/gtkmm2ext/gtkapplication-private.h
index 2be5e71d02..6b3e7850be 100644
--- a/libs/gtkmm2ext/gtkmm2ext/sync-menu.h
+++ b/libs/gtkmm2ext/gtkmm2ext/gtkapplication-private.h
@@ -1,10 +1,9 @@
-/* GTK+ Integration for the Mac OS X Menubar.
+/* GTK+ Integration with platform-specific application-wide features
+ * such as the OS X menubar and application delegate concepts.
*
* Copyright (C) 2007 Pioneer Research Center USA, Inc.
* Copyright (C) 2007 Imendio AB
- *
- * For further information, see:
- * http://developer.imendio.com/projects/gtk-macosx/menubar
+ * Copyright (C) 2009 Paul Davis
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,23 +21,18 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __IGE_MAC_MENU_H__
-#define __IGE_MAC_MENU_H__
+#ifndef __GTK_APPLICATION_PRIVATE_H__
+#define __GTK_APPLICATION_PRIVATE_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
-typedef struct _IgeMacMenuGroup IgeMacMenuGroup;
-
-void ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell);
-void ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item);
-
-IgeMacMenuGroup * ige_mac_menu_add_app_menu_group (void);
-void ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group,
- GtkMenuItem *menu_item,
- const gchar *label);
+struct _GtkApplicationMenuGroup
+{
+ GList *items;
+};
G_END_DECLS
-#endif /* __IGE_MAC_MENU_H__ */
+#endif /* __GTK_APPLICATION_PRIVATE_H__ */
diff --git a/libs/gtkmm2ext/gtkmm2ext/gtkapplication.h b/libs/gtkmm2ext/gtkmm2ext/gtkapplication.h
new file mode 100644
index 0000000000..08c3a84588
--- /dev/null
+++ b/libs/gtkmm2ext/gtkmm2ext/gtkapplication.h
@@ -0,0 +1,48 @@
+/* GTK+ Integration with platform-specific application-wide features
+ * such as the OS X menubar and application delegate concepts.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007 Imendio AB
+ * Copyright (C) 2009 Paul Davis
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_APPLICATION_H__
+#define __GTK_APPLICATION_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkApplicationMenuGroup GtkApplicationMenuGroup;
+
+int gtk_application_init ();
+void gtk_application_cleanup ();
+
+void gtk_application_set_menu_bar (GtkMenuShell *menu_shell);
+GtkApplicationMenuGroup * gtk_application_add_app_menu_group (void);
+void gtk_application_add_app_menu_item (GtkApplicationMenuGroup *group,
+ GtkMenuItem *menu_item,
+ const gchar *label);
+
+/* these are private but here until GtkApplication becomes a GtkObject with an interface */
+
+extern GList *_gtk_application_menu_groups;
+
+G_END_DECLS
+
+#endif /* __GTK_APPLICATION_H__ */
diff --git a/libs/gtkmm2ext/sync-menu.c b/libs/gtkmm2ext/sync-menu.c
deleted file mode 100644
index 3b93436e5d..0000000000
--- a/libs/gtkmm2ext/sync-menu.c
+++ /dev/null
@@ -1,981 +0,0 @@
-/* GTK+ Integration for the Mac OS X Menubar.
- *
- * Copyright (C) 2007 Pioneer Research Center USA, Inc.
- * Copyright (C) 2007 Imendio AB
- *
- * For further information, see:
- * http://developer.imendio.com/projects/gtk-macosx/menubar
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; version 2.1
- * of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-
-#include <Carbon/Carbon.h>
-
-#include <gtkmm2ext/sync-menu.h>
-
-
-/* TODO
- *
- * - Sync adding/removing/reordering items
- * - Create on demand? (can this be done with gtk+? ie fill in menu
- items when the menu is opened)
- * - Figure out what to do per app/window...
- *
- */
-
-#define IGE_QUARTZ_MENU_CREATOR 'IGEC'
-#define IGE_QUARTZ_ITEM_WIDGET 'IWID'
-
-
-static void sync_menu_shell (GtkMenuShell *menu_shell,
- MenuRef carbon_menu,
- gboolean toplevel,
- gboolean debug);
-
-
-/*
- * utility functions
- */
-
-static GtkWidget *
-find_menu_label (GtkWidget *widget)
-{
- GtkWidget *label = NULL;
-
- if (GTK_IS_LABEL (widget))
- return widget;
-
- if (GTK_IS_CONTAINER (widget))
- {
- GList *children;
- GList *l;
-
- children = gtk_container_get_children (GTK_CONTAINER (widget));
-
- for (l = children; l; l = l->next)
- {
- label = find_menu_label (l->data);
- if (label)
- break;
- }
-
- g_list_free (children);
- }
-
- return label;
-}
-
-static const gchar *
-get_menu_label_text (GtkWidget *menu_item,
- GtkWidget **label)
-{
- GtkWidget *my_label;
-
- my_label = find_menu_label (menu_item);
- if (label)
- *label = my_label;
-
- if (my_label)
- return gtk_label_get_text (GTK_LABEL (my_label));
-
- return NULL;
-}
-
-static gboolean
-accel_find_func (GtkAccelKey *key,
- GClosure *closure,
- gpointer data)
-{
- return (GClosure *) data == closure;
-}
-
-
-/*
- * CarbonMenu functions
- */
-
-typedef struct
-{
- MenuRef menu;
-} CarbonMenu;
-
-static GQuark carbon_menu_quark = 0;
-
-static CarbonMenu *
-carbon_menu_new (void)
-{
- return g_slice_new0 (CarbonMenu);
-}
-
-static void
-carbon_menu_free (CarbonMenu *menu)
-{
- g_slice_free (CarbonMenu, menu);
-}
-
-static CarbonMenu *
-carbon_menu_get (GtkWidget *widget)
-{
- return g_object_get_qdata (G_OBJECT (widget), carbon_menu_quark);
-}
-
-static void
-carbon_menu_connect (GtkWidget *menu,
- MenuRef menuRef)
-{
- CarbonMenu *carbon_menu = carbon_menu_get (menu);
-
- if (!carbon_menu)
- {
- carbon_menu = carbon_menu_new ();
-
- g_object_set_qdata_full (G_OBJECT (menu), carbon_menu_quark,
- carbon_menu,
- (GDestroyNotify) carbon_menu_free);
- }
-
- carbon_menu->menu = menuRef;
-}
-
-
-/*
- * CarbonMenuItem functions
- */
-
-typedef struct
-{
- MenuRef menu;
- MenuItemIndex index;
- MenuRef submenu;
- GClosure *accel_closure;
-} CarbonMenuItem;
-
-static GQuark carbon_menu_item_quark = 0;
-
-static CarbonMenuItem *
-carbon_menu_item_new (void)
-{
- return g_slice_new0 (CarbonMenuItem);
-}
-
-static void
-carbon_menu_item_free (CarbonMenuItem *menu_item)
-{
- if (menu_item->accel_closure)
- g_closure_unref (menu_item->accel_closure);
-
- g_slice_free (CarbonMenuItem, menu_item);
-}
-
-static CarbonMenuItem *
-carbon_menu_item_get (GtkWidget *widget)
-{
- return g_object_get_qdata (G_OBJECT (widget), carbon_menu_item_quark);
-}
-
-static void
-carbon_menu_item_update_state (CarbonMenuItem *carbon_item,
- GtkWidget *widget)
-{
- gboolean sensitive;
- gboolean visible;
- UInt32 set_attrs = 0;
- UInt32 clear_attrs = 0;
-
- g_object_get (widget,
- "sensitive", &sensitive,
- "visible", &visible,
- NULL);
-
- if (!sensitive)
- set_attrs |= kMenuItemAttrDisabled;
- else
- clear_attrs |= kMenuItemAttrDisabled;
-
- if (!visible)
- set_attrs |= kMenuItemAttrHidden;
- else
- clear_attrs |= kMenuItemAttrHidden;
-
- ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
- set_attrs, clear_attrs);
-}
-
-static void
-carbon_menu_item_update_active (CarbonMenuItem *carbon_item,
- GtkWidget *widget)
-{
- gboolean active;
-
- g_object_get (widget,
- "active", &active,
- NULL);
-
- CheckMenuItem (carbon_item->menu, carbon_item->index,
- active);
-}
-
-static void
-carbon_menu_item_update_submenu (CarbonMenuItem *carbon_item,
- GtkWidget *widget)
-{
- GtkWidget *submenu;
-
- submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
-
- if (submenu)
- {
- const gchar *label_text;
- CFStringRef cfstr = NULL;
-
- label_text = get_menu_label_text (widget, NULL);
- if (label_text)
- cfstr = CFStringCreateWithCString (NULL, label_text,
- kCFStringEncodingUTF8);
-
- CreateNewMenu (0, 0, &carbon_item->submenu);
- SetMenuTitleWithCFString (carbon_item->submenu, cfstr);
- SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
- carbon_item->submenu);
-
- sync_menu_shell (GTK_MENU_SHELL (submenu), carbon_item->submenu, FALSE, FALSE);
-
- if (cfstr)
- CFRelease (cfstr);
- }
- else
- {
- SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
- NULL);
- carbon_item->submenu = NULL;
- }
-}
-
-static void
-carbon_menu_item_update_label (CarbonMenuItem *carbon_item,
- GtkWidget *widget)
-{
- const gchar *label_text;
- CFStringRef cfstr = NULL;
-
- label_text = get_menu_label_text (widget, NULL);
- if (label_text)
- cfstr = CFStringCreateWithCString (NULL, label_text,
- kCFStringEncodingUTF8);
-
- SetMenuItemTextWithCFString (carbon_item->menu, carbon_item->index,
- cfstr);
-
- if (cfstr)
- CFRelease (cfstr);
-}
-
-static void
-carbon_menu_item_update_accelerator (CarbonMenuItem *carbon_item,
- GtkWidget *widget)
-{
- GtkWidget *label;
-
- get_menu_label_text (widget, &label);
-
- if (GTK_IS_ACCEL_LABEL (label) &&
- GTK_ACCEL_LABEL (label)->accel_closure)
- {
- GtkAccelKey *key;
-
- key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group,
- accel_find_func,
- GTK_ACCEL_LABEL (label)->accel_closure);
-
- if (key &&
- key->accel_key &&
- key->accel_flags & GTK_ACCEL_VISIBLE)
- {
- GdkDisplay *display = gtk_widget_get_display (widget);
- GdkKeymap *keymap = gdk_keymap_get_for_display (display);
- GdkKeymapKey *keys;
- gint n_keys;
- gint use_command;
- gboolean add_modifiers = FALSE;
- UInt8 modifiers = 0; /* implies Command key */
-
- if (gdk_keymap_get_entries_for_keyval (keymap, key->accel_key,
- &keys, &n_keys) == 0)
- {
- gint realkey = -1;
-
- switch (key->accel_key) {
- case GDK_rightarrow:
- case GDK_Right:
- realkey = kRightArrowCharCode;
- break;
- case GDK_leftarrow:
- case GDK_Left:
- realkey = kLeftArrowCharCode;
- break;
- case GDK_uparrow:
- case GDK_Up:
- realkey = kUpArrowCharCode;
- break;
- case GDK_downarrow:
- case GDK_Down:
- realkey = kDownArrowCharCode;
- break;
- default:
- break;
- }
-
- if (realkey != -1) {
- SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
- false, realkey);
- add_modifiers = TRUE;
- }
-
- } else {
-
- SetMenuItemCommandKey (carbon_item->menu, carbon_item->index, true, keys[0].keycode);
- if (keys[0].level == 1)
- {
- /* regular key, but it needs shift to make it work */
- modifiers |= kMenuShiftModifier;
- }
-
- g_free (keys);
- add_modifiers = TRUE;
- }
-
- if (add_modifiers)
- {
- use_command = 0;
-
- if (key->accel_mods)
- {
- if (key->accel_mods & GDK_SHIFT_MASK) {
- modifiers |= kMenuShiftModifier;
- }
-
- /* gdk/quartz maps Alt/Option to Mod1 */
-
- if (key->accel_mods & (GDK_MOD1_MASK)) {
- modifiers |= kMenuOptionModifier;
- }
-
- if (key->accel_mods & GDK_CONTROL_MASK) {
- modifiers |= kMenuControlModifier;
- }
-
- /* gdk/quartz maps Command to Meta */
-
- if (key->accel_mods & GDK_META_MASK) {
- use_command = 1;
- }
- }
-
- if (!use_command)
- modifiers |= kMenuNoCommandModifier;
-
- SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
- modifiers);
- return;
- }
- }
- }
-
- /* otherwise, clear the menu shortcut */
- SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
- kMenuNoModifiers | kMenuNoCommandModifier);
- ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
- 0, kMenuItemAttrUseVirtualKey);
- SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
- false, 0);
-}
-
-static void
-carbon_menu_item_accel_changed (GtkAccelGroup *accel_group,
- guint keyval,
- GdkModifierType modifier,
- GClosure *accel_closure,
- GtkWidget *widget)
-{
- CarbonMenuItem *carbon_item = carbon_menu_item_get (widget);
- GtkWidget *label;
-
- get_menu_label_text (widget, &label);
-
- if (GTK_IS_ACCEL_LABEL (label) &&
- GTK_ACCEL_LABEL (label)->accel_closure == accel_closure)
- carbon_menu_item_update_accelerator (carbon_item, widget);
-}
-
-static void
-carbon_menu_item_update_accel_closure (CarbonMenuItem *carbon_item,
- GtkWidget *widget)
-{
- GtkAccelGroup *group;
- GtkWidget *label;
-
- get_menu_label_text (widget, &label);
-
- if (carbon_item->accel_closure)
- {
- group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
-
- g_signal_handlers_disconnect_by_func (group,
- carbon_menu_item_accel_changed,
- widget);
-
- g_closure_unref (carbon_item->accel_closure);
- carbon_item->accel_closure = NULL;
- }
-
- if (GTK_IS_ACCEL_LABEL (label))
- carbon_item->accel_closure = GTK_ACCEL_LABEL (label)->accel_closure;
-
- if (carbon_item->accel_closure)
- {
- g_closure_ref (carbon_item->accel_closure);
-
- group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
-
- g_signal_connect_object (group, "accel-changed",
- G_CALLBACK (carbon_menu_item_accel_changed),
- widget, 0);
- }
-
- carbon_menu_item_update_accelerator (carbon_item, widget);
-}
-
-static void
-carbon_menu_item_notify (GObject *object,
- GParamSpec *pspec,
- CarbonMenuItem *carbon_item)
-{
- if (!strcmp (pspec->name, "sensitive") ||
- !strcmp (pspec->name, "visible"))
- {
- carbon_menu_item_update_state (carbon_item, GTK_WIDGET (object));
- }
- else if (!strcmp (pspec->name, "active"))
- {
- carbon_menu_item_update_active (carbon_item, GTK_WIDGET (object));
- }
- else if (!strcmp (pspec->name, "submenu"))
- {
- carbon_menu_item_update_submenu (carbon_item, GTK_WIDGET (object));
- }
-}
-
-static void
-carbon_menu_item_notify_label (GObject *object,
- GParamSpec *pspec,
- gpointer data)
-{
- CarbonMenuItem *carbon_item = carbon_menu_item_get (GTK_WIDGET (object));
-
- if (!strcmp (pspec->name, "label"))
- {
- carbon_menu_item_update_label (carbon_item,
- GTK_WIDGET (object));
- }
- else if (!strcmp (pspec->name, "accel-closure"))
- {
- carbon_menu_item_update_accel_closure (carbon_item,
- GTK_WIDGET (object));
- }
-}
-
-static CarbonMenuItem *
-carbon_menu_item_connect (GtkWidget *menu_item,
- GtkWidget *label,
- MenuRef menu,
- MenuItemIndex index)
-{
- CarbonMenuItem *carbon_item = carbon_menu_item_get (menu_item);
-
- if (!carbon_item)
- {
- carbon_item = carbon_menu_item_new ();
-
- g_object_set_qdata_full (G_OBJECT (menu_item), carbon_menu_item_quark,
- carbon_item,
- (GDestroyNotify) carbon_menu_item_free);
-
- g_signal_connect (menu_item, "notify",
- G_CALLBACK (carbon_menu_item_notify),
- carbon_item);
-
- if (label)
- g_signal_connect_swapped (label, "notify::label",
- G_CALLBACK (carbon_menu_item_notify_label),
- menu_item);
- }
-
- carbon_item->menu = menu;
- carbon_item->index = index;
-
- return carbon_item;
-}
-
-
-/*
- * carbon event handler
- */
-
-static int _in_carbon_menu_event_handler = 0;
-
-int
-gdk_quartz_in_carbon_menu_event_handler ()
-{
- return _in_carbon_menu_event_handler;
-}
-
-static gboolean
-dummy_gtk_menu_item_activate (gpointer *arg)
-{
- gtk_menu_item_activate (GTK_MENU_ITEM(arg));
- return FALSE;
-}
-
-static OSStatus
-menu_event_handler_func (EventHandlerCallRef event_handler_call_ref,
- EventRef event_ref,
- void *data)
-{
- UInt32 event_class = GetEventClass (event_ref);
- UInt32 event_kind = GetEventKind (event_ref);
- MenuRef menu_ref;
- OSStatus ret;
-
- _in_carbon_menu_event_handler = 1;
-
- switch (event_class)
- {
- case kEventClassCommand:
- /* This is called when activating (is that the right GTK+ term?)
- * a menu item.
- */
- if (event_kind == kEventCommandProcess)
- {
- HICommand command;
- OSStatus err;
-
- /*g_printerr ("Menu: kEventClassCommand/kEventCommandProcess\n");*/
-
- err = GetEventParameter (event_ref, kEventParamDirectObject,
- typeHICommand, 0,
- sizeof (command), 0, &command);
-
- if (err == noErr)
- {
- GtkWidget *widget = NULL;
-
- /* Get any GtkWidget associated with the item. */
- err = GetMenuItemProperty (command.menu.menuRef,
- command.menu.menuItemIndex,
- IGE_QUARTZ_MENU_CREATOR,
- IGE_QUARTZ_ITEM_WIDGET,
- sizeof (widget), 0, &widget);
- if (err == noErr && GTK_IS_WIDGET (widget))
- {
- g_idle_add ((GSourceFunc) dummy_gtk_menu_item_activate, widget);
- // gtk_menu_item_activate (GTK_MENU_ITEM (widget));
- _in_carbon_menu_event_handler = 0;
- return noErr;
- }
- }
- }
- break;
-
- case kEventClassMenu:
- GetEventParameter (event_ref,
- kEventParamDirectObject,
- typeMenuRef,
- NULL,
- sizeof (menu_ref),
- NULL,
- &menu_ref);
-
- switch (event_kind)
- {
- case kEventMenuTargetItem:
- /* This is called when an item is selected (what is the
- * GTK+ term? prelight?)
- */
- /*g_printerr ("kEventClassMenu/kEventMenuTargetItem\n");*/
- break;
-
- case kEventMenuOpening:
- /* Is it possible to dynamically build the menu here? We
- * can at least set visibility/sensitivity.
- */
- /*g_printerr ("kEventClassMenu/kEventMenuOpening\n");*/
- break;
-
- case kEventMenuClosed:
- /*g_printerr ("kEventClassMenu/kEventMenuClosed\n");*/
- break;
-
- default:
- break;
- }
-
- break;
-
- default:
- break;
- }
-
- ret = CallNextEventHandler (event_handler_call_ref, event_ref);
- _in_carbon_menu_event_handler = 0;
- return ret;
-}
-
-static void
-setup_menu_event_handler (void)
-{
- EventHandlerUPP menu_event_handler_upp;
- EventHandlerRef menu_event_handler_ref;
- const EventTypeSpec menu_events[] = {
- { kEventClassCommand, kEventCommandProcess },
- { kEventClassMenu, kEventMenuTargetItem },
- { kEventClassMenu, kEventMenuOpening },
- { kEventClassMenu, kEventMenuClosed }
- };
-
- /* FIXME: We might have to install one per window? */
-
- menu_event_handler_upp = NewEventHandlerUPP (menu_event_handler_func);
- InstallEventHandler (GetApplicationEventTarget (), menu_event_handler_upp,
- GetEventTypeCount (menu_events), menu_events, 0,
- &menu_event_handler_ref);
-
-#if 0
- /* FIXME: Remove the handler with: */
- RemoveEventHandler(menu_event_handler_ref);
- DisposeEventHandlerUPP(menu_event_handler_upp);
-#endif
-}
-
-static void
-sync_menu_shell (GtkMenuShell *menu_shell,
- MenuRef carbon_menu,
- gboolean toplevel,
- gboolean debug)
-{
- GList *children;
- GList *l;
- MenuItemIndex carbon_index = 1;
-
- if (debug)
- g_printerr ("%s: syncing shell %p\n", G_STRFUNC, menu_shell);
-
- carbon_menu_connect (GTK_WIDGET (menu_shell), carbon_menu);
-
- children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
-
- for (l = children; l; l = l->next)
- {
- GtkWidget *menu_item = l->data;
- CarbonMenuItem *carbon_item;
-
- if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
- continue;
-
- if (toplevel && g_object_get_data (G_OBJECT (menu_item),
- "gtk-empty-menu-item"))
- continue;
-
- carbon_item = carbon_menu_item_get (menu_item);
-
- if (debug)
- g_printerr ("%s: carbon_item %d for menu_item %d (%s, %s)\n",
- G_STRFUNC, carbon_item ? carbon_item->index : -1,
- carbon_index, get_menu_label_text (menu_item, NULL),
- g_type_name (G_TYPE_FROM_INSTANCE (menu_item)));
-
- if (carbon_item && carbon_item->index != carbon_index)
- {
- if (debug)
- g_printerr ("%s: -> not matching, deleting\n", G_STRFUNC);
-
- DeleteMenuItem (carbon_item->menu, carbon_index);
- carbon_item = NULL;
- }
-
- if (!carbon_item)
- {
- GtkWidget *label = NULL;
- const gchar *label_text;
- CFStringRef cfstr = NULL;
- MenuItemAttributes attributes = 0;
-
- if (debug)
- g_printerr ("%s: -> creating new\n", G_STRFUNC);
-
- label_text = get_menu_label_text (menu_item, &label);
- if (label_text)
- cfstr = CFStringCreateWithCString (NULL, label_text,
- kCFStringEncodingUTF8);
-
- if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
- attributes |= kMenuItemAttrSeparator;
-
- if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
- attributes |= kMenuItemAttrDisabled;
-
- if (!GTK_WIDGET_VISIBLE (menu_item))
- attributes |= kMenuItemAttrHidden;
-
- InsertMenuItemTextWithCFString (carbon_menu, cfstr,
- carbon_index - 1,
- attributes, 0);
- SetMenuItemProperty (carbon_menu, carbon_index,
- IGE_QUARTZ_MENU_CREATOR,
- IGE_QUARTZ_ITEM_WIDGET,
- sizeof (menu_item), &menu_item);
-
- if (cfstr)
- CFRelease (cfstr);
-
- carbon_item = carbon_menu_item_connect (menu_item, label,
- carbon_menu,
- carbon_index);
-
- if (GTK_IS_CHECK_MENU_ITEM (menu_item))
- carbon_menu_item_update_active (carbon_item, menu_item);
-
- carbon_menu_item_update_accel_closure (carbon_item, menu_item);
-
- if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
- carbon_menu_item_update_submenu (carbon_item, menu_item);
- }
-
- carbon_index++;
- }
-
- g_list_free (children);
-}
-
-
-static gulong emission_hook_id = 0;
-
-static gboolean
-parent_set_emission_hook (GSignalInvocationHint *ihint,
- guint n_param_values,
- const GValue *param_values,
- gpointer data)
-{
- GtkWidget *instance = g_value_get_object (param_values);
-
- if (GTK_IS_MENU_ITEM (instance))
- {
- GtkWidget *previous_parent = g_value_get_object (param_values + 1);
- GtkWidget *menu_shell = NULL;
-
- if (GTK_IS_MENU_SHELL (previous_parent))
- {
- menu_shell = previous_parent;
- }
- else if (GTK_IS_MENU_SHELL (instance->parent))
- {
- menu_shell = instance->parent;
- }
-
- if (menu_shell)
- {
- CarbonMenu *carbon_menu = carbon_menu_get (menu_shell);
-
- if (carbon_menu)
- {
-#if 0
- g_printerr ("%s: item %s %p (%s, %s)\n", G_STRFUNC,
- previous_parent ? "removed from" : "added to",
- menu_shell,
- get_menu_label_text (instance, NULL),
- g_type_name (G_TYPE_FROM_INSTANCE (instance)));
-#endif
-
- sync_menu_shell (GTK_MENU_SHELL (menu_shell),
- carbon_menu->menu,
- carbon_menu->menu == (MenuRef) data,
- FALSE);
- }
- }
- }
-
- return TRUE;
-}
-
-static void
-parent_set_emission_hook_remove (GtkWidget *widget,
- gpointer data)
-{
- g_signal_remove_emission_hook (g_signal_lookup ("parent-set",
- GTK_TYPE_WIDGET),
- emission_hook_id);
-}
-
-
-/*
- * public functions
- */
-
-void
-ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell)
-{
- MenuRef carbon_menubar;
-
- g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
-
- if (carbon_menu_quark == 0)
- carbon_menu_quark = g_quark_from_static_string ("CarbonMenu");
-
- if (carbon_menu_item_quark == 0)
- carbon_menu_item_quark = g_quark_from_static_string ("CarbonMenuItem");
-
- CreateNewMenu (0 /*id*/, 0 /*options*/, &carbon_menubar);
- SetRootMenu (carbon_menubar);
-
- setup_menu_event_handler ();
-
- emission_hook_id =
- g_signal_add_emission_hook (g_signal_lookup ("parent-set",
- GTK_TYPE_WIDGET),
- 0,
- parent_set_emission_hook,
- carbon_menubar, NULL);
-
- g_signal_connect (menu_shell, "destroy",
- G_CALLBACK (parent_set_emission_hook_remove),
- NULL);
-
- sync_menu_shell (menu_shell, carbon_menubar, TRUE, FALSE);
-}
-
-void
-ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item)
-{
- MenuRef appmenu;
- MenuItemIndex index;
-
- g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
-
- if (GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
- &appmenu, &index) == noErr)
- {
- SetMenuItemCommandID (appmenu, index, 0);
- SetMenuItemProperty (appmenu, index,
- IGE_QUARTZ_MENU_CREATOR,
- IGE_QUARTZ_ITEM_WIDGET,
- sizeof (menu_item), &menu_item);
-
- gtk_widget_hide (GTK_WIDGET (menu_item));
- }
-}
-
-
-struct _IgeMacMenuGroup
-{
- GList *items;
-};
-
-static GList *app_menu_groups = NULL;
-
-IgeMacMenuGroup *
-ige_mac_menu_add_app_menu_group (void)
-{
- IgeMacMenuGroup *group = g_slice_new0 (IgeMacMenuGroup);
-
- app_menu_groups = g_list_append (app_menu_groups, group);
-
- return group;
-}
-
-void
-ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group,
- GtkMenuItem *menu_item,
- const gchar *label)
-{
- MenuRef appmenu;
- GList *list;
- gint index = 0;
-
- g_return_if_fail (group != NULL);
- g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
-
- if (GetIndMenuItemWithCommandID (NULL, kHICommandHide, 1,
- &appmenu, NULL) != noErr)
- {
- g_warning ("%s: retrieving app menu failed",
- G_STRFUNC);
- return;
- }
-
- for (list = app_menu_groups; list; list = g_list_next (list))
- {
- IgeMacMenuGroup *list_group = list->data;
-
- index += g_list_length (list_group->items);
-
- /* adjust index for the separator between groups, but not
- * before the first group
- */
- if (list_group->items && list->prev)
- index++;
-
- if (group == list_group)
- {
- CFStringRef cfstr;
-
- /* add a separator before adding the first item, but not
- * for the first group
- */
- if (!group->items && list->prev)
- {
- InsertMenuItemTextWithCFString (appmenu, NULL, index,
- kMenuItemAttrSeparator, 0);
- index++;
- }
-
- if (!label)
- label = get_menu_label_text (GTK_WIDGET (menu_item), NULL);
-
- cfstr = CFStringCreateWithCString (NULL, label,
- kCFStringEncodingUTF8);
-
- InsertMenuItemTextWithCFString (appmenu, cfstr, index, 0, 0);
- SetMenuItemProperty (appmenu, index + 1,
- IGE_QUARTZ_MENU_CREATOR,
- IGE_QUARTZ_ITEM_WIDGET,
- sizeof (menu_item), &menu_item);
-
- CFRelease (cfstr);
-
- gtk_widget_hide (GTK_WIDGET (menu_item));
-
- group->items = g_list_append (group->items, menu_item);
-
- return;
- }
- }
-
- if (!list)
- g_warning ("%s: app menu group %p does not exist",
- G_STRFUNC, group);
-}