diff options
-rw-r--r-- | gtk2_ardour/ardour_ui_ed.cc | 6 | ||||
-rw-r--r-- | gtk2_ardour/cocoacarbon.mm | 33 | ||||
-rw-r--r-- | gtk2_ardour/utils.cc | 54 | ||||
-rw-r--r-- | libs/gtkmm2ext/SConscript | 13 | ||||
-rw-r--r-- | libs/gtkmm2ext/gtk_ui.cc | 24 | ||||
-rw-r--r-- | libs/gtkmm2ext/gtkapplication.c | 36 | ||||
-rw-r--r-- | libs/gtkmm2ext/gtkapplication_quartz.mm | 1370 | ||||
-rw-r--r-- | libs/gtkmm2ext/gtkapplication_x11.c | 48 | ||||
-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.h | 48 | ||||
-rw-r--r-- | libs/gtkmm2ext/sync-menu.c | 981 |
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); -} |