diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2007-08-15 13:38:23 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2007-08-15 13:38:23 +0000 |
commit | a4c84de53c3855b52955df5633e2b1a4347667f8 (patch) | |
tree | 760d1a67e3c35154e987ecd171696b2b7a4c942d | |
parent | a8fc5f180de26ddfbc94687739fbad84a66b711c (diff) |
changes specific to GTK-OSX build
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2303 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | SConstruct | 9 | ||||
-rw-r--r-- | gtk2_ardour/SConscript | 72 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui.h | 2 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui_ed.cc | 15 | ||||
-rw-r--r-- | gtk2_ardour/sync-menu.c | 795 | ||||
-rw-r--r-- | gtk2_ardour/sync-menu.h | 27 |
6 files changed, 887 insertions, 33 deletions
diff --git a/SConstruct b/SConstruct index b3cada1303..1844969b89 100644 --- a/SConstruct +++ b/SConstruct @@ -29,6 +29,7 @@ opts.AddOptions( ('ARCH', 'Set architecture-specific compilation flags by hand (all flags as 1 argument)',''), BoolOption('AUDIOUNITS', 'Compile with Apple\'s AudioUnit library. (experimental)', 0), BoolOption('COREAUDIO', 'Compile with Apple\'s CoreAudio library', 0), + BoolOption('GTKOSX', 'Compile for use with GTK-OSX, not GTK-X11', 0), BoolOption('DEBUG', 'Set to build with debugging information and no optimizations', 0), PathOption('DESTDIR', 'Set the intermediate install "prefix"', '/'), EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'none' ), ignorecase=2), @@ -816,7 +817,13 @@ if conf.CheckCHeader('alsa/asoundlib.h'): subst_dict['%MIDITYPE%'] = "alsa/sequencer" elif conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'): # this line is needed because scons can't handle -framework in ParseConfig() yet. - libraries['sysmidi'] = LibraryInfo (LINKFLAGS= '-framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -bind_at_load') + if env['GTKOSX']: + # We need Carbon as well as the rest + libraries['sysmidi'] = LibraryInfo ( + LINKFLAGS = ' -framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -framework Carbon -bind_at_load' ) + else: + libraries['sysmidi'] = LibraryInfo ( + LINKFLAGS = ' -framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -bind_at_load' ) env['SYSMIDI'] = 'CoreMIDI' subst_dict['%MIDITAG%'] = "ardour" subst_dict['%MIDITYPE%'] = "coremidi" diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index b58b94433c..8a34bf53f9 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -80,6 +80,10 @@ audiounit_files=Split(""" au_pluginui.cc """) +gtkosx_files=Split(""" +sync-menu.c +""") + gtkardour_files=Split(""" about.cc actions.cc @@ -247,10 +251,13 @@ if env['VST']: extra_sources += vst_files gtkardour.Append (CCFLAGS="-DVST_SUPPORT", CPPPATH="#libs/fst") +if gtkardour['GTKOSX']: + extra_sources += gtkosx_files + gtkardour.Append (CCFLAGS="-DTOP_MENUBAR -DGTKOSX") + if gtkardour['AUDIOUNITS']: extra_sources += audiounit_files gtkardour.Append(CCFLAGS='-DHAVE_AUDIOUNITS') - gtkardour.Append(LINKFLAGS='-framework Carbon') gtkardour.Merge([libraries['appleutility']]) if env['FFT_ANALYSIS']: @@ -281,37 +288,37 @@ if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger': # # OS X font rendering is different even with X11 # - my_font_dict['%FONT_TINY%'] = 'sans 7' - my_font_dict['%FONT_SMALLERER%'] = 'sans 8' - my_font_dict['%FONT_SMALLER%'] = 'sans 9' - my_font_dict['%FONT_SMALL%'] = 'sans 10' - my_font_dict['%FONT_NORMAL%'] = 'sans 11' - my_font_dict['%FONT_BIG%'] = 'sans 12' - my_font_dict['%FONT_BIGGER%'] = 'sans 14' - my_font_dict['%FONT_LARGE%'] = 'sans 18' - my_font_dict['%FONT_LARGER%'] = 'sans 28' - my_font_dict['%FONT_HUGER%'] = 'sans 36' - my_font_dict['%FONT_MASSIVE%'] = 'sans 60' - my_font_dict['%FONT_BOLD_TINY%'] = 'sans bold 7' - my_font_dict['%FONT_BOLD_SMALLER%'] = 'sans bold 9' - my_font_dict['%FONT_BOLD_SMALL%'] = 'sans bold 10' - my_font_dict['%FONT_BOLD_NORMAL%'] = 'sans bold 11' - my_font_dict['%FONT_BOLD_BIG%'] = 'sans bold 13' - my_font_dict['%FONT_BOLD_BIGGER%'] = 'sans bold 14' - my_font_dict['%FONT_BOLD_LARGE%'] = 'sans bold 20' - my_font_dict['%FONT_BOLD_LARGER%'] = 'sans bold 25' - my_font_dict['%FONT_BOLD_HUGER%'] = 'sans bold 36' - my_font_dict['%FONT_BOLD_MASSIVE%'] = 'sans bold 60' - my_font_dict['%FONT_ITALIC_TINY%'] = 'sans italic 7' - my_font_dict['%FONT_ITALIC_SMALLER%'] = 'sans italic 9' - my_font_dict['%FONT_ITALIC_SMALL%'] = 'sans italic 10' - my_font_dict['%FONT_ITALIC_NORMAL%'] = 'sans italic 11' - my_font_dict['%FONT_ITALIC_BIG%'] = 'sans italic 15' - my_font_dict['%FONT_ITALIC_BIGGER%'] = 'sans italic 16' - my_font_dict['%FONT_ITALIC_LARGE%'] = 'sans italic 20' - my_font_dict['%FONT_ITALIC_LARGER%'] = 'sans italic 28' - my_font_dict['%FONT_ITALIC_HUGER%'] = 'sans italic 36' - my_font_dict['%FONT_ITALIC_MASSIVE%'] = 'sans italic 60' + my_font_dict['%FONT_TINY%'] = 'Lucida Grande 7' + my_font_dict['%FONT_SMALLERER%'] = 'Lucida Grande 8' + my_font_dict['%FONT_SMALLER%'] = 'Lucida Grande 9' + my_font_dict['%FONT_SMALL%'] = 'Lucida Grande 10' + my_font_dict['%FONT_NORMAL%'] = 'Lucida Grande 11' + my_font_dict['%FONT_BIG%'] = 'Lucida Grande 12' + my_font_dict['%FONT_BIGGER%'] = 'Lucida Grande 14' + my_font_dict['%FONT_LARGE%'] = 'Lucida Grande 18' + my_font_dict['%FONT_LARGER%'] = 'Lucida Grande 28' + my_font_dict['%FONT_HUGER%'] = 'Lucida Grande 36' + my_font_dict['%FONT_MASSIVE%'] = 'Lucida Grande 60' + my_font_dict['%FONT_BOLD_TINY%'] = 'Lucida Grande bold 7' + my_font_dict['%FONT_BOLD_SMALLER%'] = 'Lucida Grande bold 9' + my_font_dict['%FONT_BOLD_SMALL%'] = 'Lucida Grande bold 10' + my_font_dict['%FONT_BOLD_NORMAL%'] = 'Lucida Grande bold 11' + my_font_dict['%FONT_BOLD_BIG%'] = 'Lucida Grande bold 13' + my_font_dict['%FONT_BOLD_BIGGER%'] = 'Lucida Grande bold 14' + my_font_dict['%FONT_BOLD_LARGE%'] = 'Lucida Grande bold 20' + my_font_dict['%FONT_BOLD_LARGER%'] = 'Lucida Grande bold 25' + my_font_dict['%FONT_BOLD_HUGER%'] = 'Lucida Grande bold 36' + my_font_dict['%FONT_BOLD_MASSIVE%'] = 'Lucida Grande bold 60' + my_font_dict['%FONT_ITALIC_TINY%'] = 'Lucida Grande italic 7' + my_font_dict['%FONT_ITALIC_SMALLER%'] = 'Lucida Grande italic 9' + my_font_dict['%FONT_ITALIC_SMALL%'] = 'Lucida Grande italic 10' + my_font_dict['%FONT_ITALIC_NORMAL%'] = 'Lucida Grande italic 11' + my_font_dict['%FONT_ITALIC_BIG%'] = 'Lucida Grande italic 15' + my_font_dict['%FONT_ITALIC_BIGGER%'] = 'Lucida Grande italic 16' + my_font_dict['%FONT_ITALIC_LARGE%'] = 'Lucida Grande italic 20' + my_font_dict['%FONT_ITALIC_LARGER%'] = 'Lucida Grande italic 28' + my_font_dict['%FONT_ITALIC_HUGER%'] = 'Lucida Grande italic 36' + my_font_dict['%FONT_ITALIC_MASSIVE%'] = 'Lucida Grande italic 60' else: # # Linux/X11 font rendering @@ -428,6 +435,7 @@ env.Alias ('tarball', env.Distribute (env['DISTTREE'], icon_files + skipped_files + audiounit_files + + gtkosx_files + fft_analysis_files + glob.glob('po/*.po') + glob.glob('*.h'))) diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 4f2d78c736..e712ced9e0 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -505,6 +505,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI Gtk::EventBox menu_bar_base; Gtk::HBox menu_hbox; + void use_menubar_as_top_menubar (); + void build_menu_bar (); void build_control_surface_menu (); diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index c04608825e..602fe260ea 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -37,6 +37,7 @@ #include "engine_dialog.h" #include "editor.h" #include "actions.h" +#include "sync-menu.h" #include <ardour/session.h> #include <ardour/profile.h> @@ -735,7 +736,9 @@ ARDOUR_UI::build_menu_bar () sample_rate_box.set_name ("SampleRate"); sample_rate_label.set_name ("SampleRate"); +#ifndef TOP_MENUBAR menu_hbox.pack_start (*menu_bar, true, true); +#endif if (!Profile->get_small_screen()) { menu_hbox.pack_end (wall_clock_box, false, false, 2); menu_hbox.pack_end (disk_space_box, false, false, 4); @@ -744,11 +747,23 @@ ARDOUR_UI::build_menu_bar () menu_hbox.pack_end (buffer_load_box, false, false, 4); menu_hbox.pack_end (sample_rate_box, false, false, 4); +#ifdef TOP_MENUBAR + use_menubar_as_top_menubar (); +#endif + menu_bar_base.set_name ("MainMenuBar"); menu_bar_base.add (menu_hbox); } void +ARDOUR_UI::use_menubar_as_top_menubar () +{ +#ifdef GTKOSX + sync_menu_takeover_menu ((GtkMenuShell*) menu_bar->gobj()); +#endif +} + +void ARDOUR_UI::setup_clock () { ARDOUR_UI::Clock.connect (bind (mem_fun (big_clock, &AudioClock::set), false)); diff --git a/gtk2_ardour/sync-menu.c b/gtk2_ardour/sync-menu.c new file mode 100644 index 0000000000..1f06939ff5 --- /dev/null +++ b/gtk2_ardour/sync-menu.c @@ -0,0 +1,795 @@ +/* GTK+ Integration for the Mac OS X Menubar. + * + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * + * 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. + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <Carbon/Carbon.h> + +#include "sync-menu.h" + +#define CARBON_MENU 1 + +#ifndef CARBON_MENU +#import <AppKit/AppKit.h> +#endif + +/* TODO + * + * - Setup shortcuts, possibly transforming ctrl->cmd + * - Sync menus + * - 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... + * - Toggle/radio items + * + */ + +#define GTK_QUARTZ_MENU_CREATOR 'GTKC' +#define GTK_QUARTZ_ITEM_WIDGET 'GWID' + + +static void sync_menu_shell (GtkMenuShell *menu_shell, + MenuRef carbon_menu); + + +/* + * 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) +{ + *label = find_menu_label (menu_item); + if (!*label) + return NULL; + + return gtk_label_get_text (GTK_LABEL (*label)); +} + +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) + { + GtkWidget *label = NULL; + const gchar *label_text; + CFStringRef cfstr = NULL; + + label_text = get_menu_label_text (widget, &label); + 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); + + 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) +{ + GtkWidget *label; + const gchar *label_text; + CFStringRef cfstr = NULL; + + label_text = get_menu_label_text (widget, &label); + 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_print ("Menu: kEventClassCommand/kEventCommandProcess\n"); + + err = GetEventParameter (event_ref, kEventParamDirectObject, + typeHICommand, 0, + sizeof (command), 0, &command); + + if (err == noErr) + { + GtkWidget *widget = NULL; + + if (command.commandID == kHICommandQuit) + { + gtk_main_quit (); /* Just testing... */ + return noErr; + } + + /* Get any GtkWidget associated with the item. */ + err = GetMenuItemProperty (command.menu.menuRef, + command.menu.menuItemIndex, + GTK_QUARTZ_MENU_CREATOR, + GTK_QUARTZ_ITEM_WIDGET, + sizeof (widget), 0, &widget); + if (err == noErr && 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_print ("kEventClassMenu/kEventMenuTargetItem\n"); + break; + + case kEventMenuOpening: + /* Is it possible to dynamically build the menu here? We + * can at least set visibility/sensitivity. + */ + //g_print ("kEventClassMenu/kEventMenuOpening\n"); + break; + + case kEventMenuClosed: + //g_print ("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 +} + +#ifdef CARBON_MENU + +static void +sync_menu_shell (GtkMenuShell *menu_shell, + MenuRef carbon_menu) +{ + GList *children; + GList *l; + MenuItemIndex carbon_index = 1; + + 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; + + carbon_item = carbon_menu_item_get (menu_item); + + if (carbon_item && carbon_item->index != carbon_index) + { + DeleteMenuItem (carbon_item->menu, + carbon_item->index); + carbon_item = NULL; + } + + if (!carbon_item) + { + GtkWidget *label = NULL; + const gchar *label_text; + CFStringRef cfstr = NULL; + MenuItemAttributes attributes = 0; + + 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, + GTK_QUARTZ_MENU_CREATOR, + GTK_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); +} + +void +sync_menu_takeover_menu (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 (); + + sync_menu_shell (menu_shell, carbon_menubar); +} + +#else /* !CARBON_MENU */ + +static void +nsmenu_from_menushell (GtkMenuShell *menu_shell, + NSMenu *nsMenu) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + GList *children; + GList *list; + + children = gtk_container_get_children (GTK_CONTAINER (menu_shell)); + + for (list = children; list; list = list->next) + { + GtkMenuItem *menu_item = list->data; + GtkLabel *label; + NSMenuItem *menuItem; + const gchar *menu_label; + NSString *menuLabel; + GtkWidget *submenu; + + if (GTK_IS_TEAROFF_MENU_ITEM (menu_item)) + continue; + + menu_label = get_menu_label_text (menu_item, &label); + if (menu_label) + menuLabel = [NSString stringWithUTF8String:menu_label]; + else + menuLabel = NULL; + + if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item)) + menuItem = [NSMenuItem separatorItem]; + else + menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] init]; + + if (menuLabel) + [menuItem setTitle:menuLabel]; + + if (!GTK_WIDGET_IS_SENSITIVE (menu_item)) + [menuItem setEnabled:NO]; + +#if 0 + /* FIXME ??? */ + if (!GTK_WIDGET_VISIBLE (menu_item)) + /* ??? */; +#endif + + [nsMenu addItem:menuItem]; + + submenu = gtk_menu_item_get_submenu (menu_item); + if (submenu) + { + NSMenu *subMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:menuLabel]; + + [menuItem setSubmenu:subMenu]; + + nsmenu_from_menushell (GTK_MENU_SHELL (submenu), subMenu); + } + +#if 0 + g_signal_connect (menu_item, "notify", + G_CALLBACK (menu_item_notify_cb), + NULL); +#endif + } + + g_list_free (children); + + [pool release]; +} + +void +sync_menu_takeover_menu (GtkMenuShell *menu_shell) +{ + NSMenu *mainMenu; + + g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); + + mainMenu = [[NSMenu alloc] initWithTitle:@""]; + + // [mainMenu initWithTitle:[NSString stringWithUTF8String:"Foo"]]; + // [mainMenu setAutoenablesItems:NO]; + + // mainMenu = [NSApp mainMenu]; + + nsmenu_from_menushell (menu_shell, mainMenu); + + // [NSApp setMainMenu:mainMenu]; +} + +#endif /* CARBON_MENU */ diff --git a/gtk2_ardour/sync-menu.h b/gtk2_ardour/sync-menu.h new file mode 100644 index 0000000000..0715a31e29 --- /dev/null +++ b/gtk2_ardour/sync-menu.h @@ -0,0 +1,27 @@ +/* GTK+ Integration for the Mac OS X Menubar. + * + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * + * 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. + */ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +void sync_menu_takeover_menu (GtkMenuShell *menu_shell); + +G_END_DECLS |