From 973e32842d0db7a0a18bb03e33a433302808cf54 Mon Sep 17 00:00:00 2001 From: jiho-sf Date: Thu, 10 Jan 2008 23:25:07 +0000 Subject: [PATCH] Added part of a patch by Adam Strzelecki which makes GTK menu migrate to OS X menubar when GTK+quartz is used. This is taken from Gimp and should be safe enough at this stage since it really only affects OS X. --- configure.ac | 15 + src/Makefile_insert | 2 + src/ige-mac-menu.c | 915 ++++++++++++++++++++++++++++++++++++++++++++ src/ige-mac-menu.h | 43 +++ src/interface.cpp | 13 + 5 files changed, 988 insertions(+) create mode 100644 src/ige-mac-menu.c create mode 100644 src/ige-mac-menu.h diff --git a/configure.ac b/configure.ac index 1ec341347..a1f0e4171 100644 --- a/configure.ac +++ b/configure.ac @@ -586,6 +586,21 @@ else fi PKG_CHECK_MODULES(INKSCAPE, gdkmm-2.4 glibmm-2.4 gtkmm-2.4 >= 2.10.0 gtk+-2.0 libxml-2.0 >= 2.6.11 libxslt >= 1.0.15 cairo sigc++-2.0 >= $min_sigc_version $ink_spell_pkg gthread-2.0 >= 2.0 libpng >= 1.2) +# Check for Apple Mac OS X Carbon framework +carbon_ok=no +AC_MSG_CHECKING([for Mac OS X Carbon support]) +AC_TRY_CPP([ +#include +#include +], carbon_ok=yes) +AC_MSG_RESULT($carbon_ok) +if test "x$carbon_ok" = "xyes"; then + AC_DEFINE(HAVE_CARBON, 1, [define to 1 if Carbon is available]) + CARBON_LDFLAGS="-framework Carbon" + AC_SUBST(CARBON_LDFLAGS) +fi +AM_CONDITIONAL(HAVE_CARBON, test "x$carbon_ok" = "xyes") + # Check for some boost header files AC_CHECK_HEADERS([boost/concept_check.hpp], [], AC_MSG_ERROR([You need the boost package (e.g. libboost-dev)])) diff --git a/src/Makefile_insert b/src/Makefile_insert index 695d87f8f..02943a4ef 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -84,6 +84,7 @@ libinkpre_a_SOURCES = \ help.cpp help.h \ helper-fns.h \ icon-size.h \ + ige-mac-menu.h ige-mac-menu.c \ inkscape-stock.cpp inkscape-stock.h\ inkscape.cpp inkscape.h inkscape-private.h \ interface.cpp interface.h \ @@ -374,6 +375,7 @@ all_libs = \ $(FREETYPE_LIBS) \ $(kdeldadd) \ $(win32ldflags) \ + $(CARBON_LDFLAGS) \ $(PERL_LIBS) \ $(PYTHON_LIBS) \ $(INKBOARD_LIBS) \ diff --git a/src/ige-mac-menu.c b/src/ige-mac-menu.c new file mode 100644 index 000000000..29915d980 --- /dev/null +++ b/src/ige-mac-menu.c @@ -0,0 +1,915 @@ +/* GTK+ Integration for the Mac OS X Menubar. + * + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * + * 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; either + * version 2 of the License, or (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef GDK_WINDOWING_QUARTZ + +#include + +#include + +#include "ige-mac-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; + + if (gdk_keymap_get_entries_for_keyval (keymap, key->accel_key, + &keys, &n_keys)) + { + UInt8 modifiers = 0; + + SetMenuItemCommandKey (carbon_item->menu, carbon_item->index, + true, keys[0].keycode); + + g_free (keys); + + if (key->accel_mods) + { + if (key->accel_mods & GDK_SHIFT_MASK) + modifiers |= kMenuShiftModifier; + + if (key->accel_mods & GDK_MOD1_MASK) + modifiers |= kMenuOptionModifier; + } + + if (!(key->accel_mods & GDK_CONTROL_MASK)) + { + 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 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; + + 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)) + { + gtk_menu_item_activate (GTK_MENU_ITEM (widget)); + 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; + } + + return CallNextEventHandler (event_handler_call_ref, event_ref); +} + +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; + guint hook_id; + + 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); +} + +#endif /* GDK_WINDOWING_QUARTZ */ diff --git a/src/ige-mac-menu.h b/src/ige-mac-menu.h new file mode 100644 index 000000000..d192979dc --- /dev/null +++ b/src/ige-mac-menu.h @@ -0,0 +1,43 @@ +/* GTK+ Integration for the Mac OS X Menubar. + * + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * + * 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; either + * version 2 of the License, or (at your option) any later version. + * + * 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 __IGE_MAC_MENU_H__ +#define __IGE_MAC_MENU_H__ + +#include + +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); + +G_END_DECLS + +#endif /* __IGE_MAC_MENU_H__ */ diff --git a/src/interface.cpp b/src/interface.cpp index f7a2bc47f..fbd57cdab 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -68,6 +68,11 @@ #include "event-context.h" #include "gradient-drag.h" +// Include Mac OS X menu synchronization on native OSX build +#ifdef GDK_WINDOWING_QUARTZ +#include "ige-mac-menu.h" +#endif + using Inkscape::IO::StringOutputStream; using Inkscape::IO::Base64OutputStream; @@ -945,9 +950,17 @@ sp_ui_main_menubar(Inkscape::UI::View::View *view) { GtkWidget *mbar = gtk_menu_bar_new(); +#ifdef GDK_WINDOWING_QUARTZ + ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar)); +#endif + sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view); +#ifdef GDK_WINDOWING_QUARTZ + return NULL; +#else return mbar; +#endif } static void leave_group(GtkMenuItem *, SPDesktop *desktop) { -- 2.30.2