From 649ff84d91a29ce049001882c251cae1950e2eac Mon Sep 17 00:00:00 2001 From: William Hua Date: Wed, 8 Jan 2014 17:27:43 -0500 Subject: [PATCH] Use GtkMenuTracker for Quartz backend. https://bugzilla.gnome.org/show_bug.cgi?id=710351 --- gtk/Makefile.am | 3 +- gtk/gtkapplication-quartz-menu.c | 377 +++++++++++++++++++++++++++++++ gtk/gtkapplication-quartz.c | 72 ++++-- gtk/gtkapplicationprivate.h | 4 + gtk/gtkmodelmenu-quartz.c | 372 ------------------------------ gtk/gtkmodelmenu-quartz.h | 30 --- 6 files changed, 434 insertions(+), 424 deletions(-) create mode 100644 gtk/gtkapplication-quartz-menu.c delete mode 100644 gtk/gtkmodelmenu-quartz.c delete mode 100644 gtk/gtkmodelmenu-quartz.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 9a33f2867d..6e71fbade6 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -966,8 +966,8 @@ gtk_use_win32_c_sources = \ gtk_use_quartz_c_sources = \ gtksearchenginequartz.c \ gtkmountoperation-stub.c \ - gtkmodelmenu-quartz.c \ gtkapplication-quartz.c \ + gtkapplication-quartz-menu.c \ gtkquartz.c gtk_use_stub_c_sources = \ gtkmountoperation-stub.c @@ -1005,7 +1005,6 @@ endif gtk_use_quartz_private_h_sources = \ gtksearchenginequartz.h \ - gtkmodelmenu-quartz.h \ gtkquartz.h if USE_QUARTZ gtk_c_sources += $(gtk_use_quartz_c_sources) diff --git a/gtk/gtkapplication-quartz-menu.c b/gtk/gtkapplication-quartz-menu.c new file mode 100644 index 0000000000..0e00ba5255 --- /dev/null +++ b/gtk/gtkapplication-quartz-menu.c @@ -0,0 +1,377 @@ +/* + * Copyright © 2011 William Hua, Ryan Lortie + * + * 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 licence, 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, see . + * + * Author: William Hua + * Ryan Lortie + */ + +#include "config.h" + +#include "gtkapplicationprivate.h" +#include "gtkmenutracker.h" +#include "gtkicontheme.h" +#include "gtktoolbar.h" +#include "gtkquartz.h" + +#include + +#import + +#define ICON_SIZE 16 + +#define BLACK "#000000" +#define TANGO_CHAMELEON_3 "#4e9a06" +#define TANGO_ORANGE_2 "#f57900" +#define TANGO_SCARLET_RED_2 "#cc0000" + +@interface GNSMenu : NSMenu +{ + GtkMenuTracker *tracker; +} + +- (id)initWithTitle:(NSString *)title model:(GMenuModel *)model observable:(GtkActionObservable *)observable; + +- (id)initWithTitle:(NSString *)title trackerItem:(GtkMenuTrackerItem *)trackerItem; + +@end + +@interface NSMenuItem (GtkMenuTrackerItem) + ++ (id)menuItemForTrackerItem:(GtkMenuTrackerItem *)trackerItem; + +@end + +@interface GNSMenuItem : NSMenuItem +{ + GtkMenuTrackerItem *trackerItem; + gulong trackerItemChangedHandler; + GCancellable *cancellable; +} + +- (id)initWithTrackerItem:(GtkMenuTrackerItem *)aTrackerItem; + +- (void)didChangeLabel; +- (void)didChangeIcon; +- (void)didChangeSensitive; +- (void)didChangeVisible; +- (void)didChangeToggled; +- (void)didChangeAccel; + +- (void)didSelectItem:(id)sender; + +@end + +static void +tracker_item_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GNSMenuItem *item = user_data; + const gchar *name = g_param_spec_get_name (pspec); + + if (name != NULL) + { + if (g_str_equal (name, "label")) + [item didChangeLabel]; + else if (g_str_equal (name, "icon")) + [item didChangeIcon]; + else if (g_str_equal (name, "visible")) + [item didChangeVisible]; + else if (g_str_equal (name, "toggled")) + [item didChangeToggled]; + else if (g_str_equal (name, "accel")) + [item didChangeAccel]; + } +} + +static void +icon_loaded (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GtkIconInfo *info = GTK_ICON_INFO (object); + GNSMenuItem *item = user_data; + GError *error = NULL; + GdkPixbuf *pixbuf; + + pixbuf = gtk_icon_info_load_symbolic_finish (info, result, NULL, &error); + + if (pixbuf != NULL) + { + [item setImage:_gtk_quartz_create_image_from_pixbuf (pixbuf)]; + g_object_unref (pixbuf); + } + else + { + /* on failure to load, clear the old icon */ + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + [item setImage:nil]; + + g_error_free (error); + } +} + +@implementation GNSMenuItem + +- (id)initWithTrackerItem:(GtkMenuTrackerItem *)aTrackerItem +{ + self = [super initWithTitle:@"" + action:@selector(didSelectItem:) + keyEquivalent:@""]; + + if (self != nil) + { + [self setTarget:self]; + + trackerItem = g_object_ref (aTrackerItem); + trackerItemChangedHandler = g_signal_connect (trackerItem, "notify", G_CALLBACK (tracker_item_changed), self); + + [self didChangeLabel]; + [self didChangeIcon]; + [self didChangeSensitive]; + [self didChangeVisible]; + [self didChangeToggled]; + [self didChangeAccel]; + + if (gtk_menu_tracker_item_get_has_submenu (trackerItem)) + [self setSubmenu:[[[GNSMenu alloc] initWithTitle:[self title] trackerItem:trackerItem] autorelease]]; + } + + return self; +} + +- (void)dealloc +{ + if (cancellable != NULL) + { + g_cancellable_cancel (cancellable); + g_clear_object (&cancellable); + } + + g_signal_handler_disconnect (trackerItem, trackerItemChangedHandler); + g_object_unref (trackerItem); + + [super dealloc]; +} + +- (void)didChangeLabel +{ + gchar *label = _gtk_toolbar_elide_underscores (gtk_menu_tracker_item_get_label (trackerItem)); + + [self setTitle:[NSString stringWithUTF8String:label ? : ""]]; + + g_free (label); +} + +- (void)didChangeIcon +{ + GIcon *icon = gtk_menu_tracker_item_get_icon (trackerItem); + + if (cancellable != NULL) + { + g_cancellable_cancel (cancellable); + g_clear_object (&cancellable); + } + + if (icon != NULL) + { + static gboolean parsed; + + static GdkRGBA foreground; + static GdkRGBA success; + static GdkRGBA warning; + static GdkRGBA error; + + GtkIconTheme *theme; + GtkIconInfo *info; + gint scale; + + if (!parsed) + { + gdk_rgba_parse (&foreground, BLACK); + gdk_rgba_parse (&success, TANGO_CHAMELEON_3); + gdk_rgba_parse (&warning, TANGO_ORANGE_2); + gdk_rgba_parse (&error, TANGO_SCARLET_RED_2); + + parsed = TRUE; + } + + theme = gtk_icon_theme_get_default (); + scale = roundf ([[NSScreen mainScreen] backingScaleFactor]); + info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, ICON_SIZE, scale, GTK_ICON_LOOKUP_USE_BUILTIN); + + if (info != NULL) + { + cancellable = g_cancellable_new (); + gtk_icon_info_load_symbolic_async (info, &foreground, &success, &warning, &error, + cancellable, icon_loaded, self); + g_object_unref (info); + return; + } + } + + [self setImage:nil]; +} + +- (void)didChangeSensitive +{ + [self setEnabled:gtk_menu_tracker_item_get_sensitive (trackerItem) ? YES : NO]; +} + +- (void)didChangeVisible +{ + [self setHidden:gtk_menu_tracker_item_get_visible (trackerItem) ? NO : YES]; +} + +- (void)didChangeToggled +{ + [self setState:gtk_menu_tracker_item_get_toggled (trackerItem) ? NSOnState : NSOffState]; +} + +- (void)didChangeAccel +{ + const gchar *accel = gtk_menu_tracker_item_get_accel (trackerItem); + + if (accel != NULL) + { + guint key; + GdkModifierType mask; + unichar character; + NSUInteger modifiers; + + gtk_accelerator_parse (accel, &key, &mask); + + character = gdk_quartz_get_key_equivalent (key); + [self setKeyEquivalent:[NSString stringWithCharacters:&character length:1]]; + + modifiers = 0; + if (mask & GDK_SHIFT_MASK) + modifiers |= NSShiftKeyMask; + if (mask & GDK_CONTROL_MASK) + modifiers |= NSControlKeyMask; + if (mask & GDK_MOD1_MASK) + modifiers |= NSAlternateKeyMask; + if (mask & GDK_META_MASK) + modifiers |= NSCommandKeyMask; + [self setKeyEquivalentModifierMask:modifiers]; + } + else + { + [self setKeyEquivalent:@""]; + [self setKeyEquivalentModifierMask:0]; + } +} + +- (void)didSelectItem:(id)sender +{ + gtk_menu_tracker_item_activated (trackerItem); +} + +@end + +@implementation NSMenuItem (GtkMenuTrackerItem) + ++ (id)menuItemForTrackerItem:(GtkMenuTrackerItem *)trackerItem +{ + if (gtk_menu_tracker_item_get_is_separator (trackerItem)) + return [NSMenuItem separatorItem]; + + return [[[GNSMenuItem alloc] initWithTrackerItem:trackerItem] autorelease]; +} + +@end + +static void +menu_item_inserted (GtkMenuTrackerItem *item, + gint position, + gpointer user_data) +{ + GNSMenu *menu = user_data; + + [menu insertItem:[NSMenuItem menuItemForTrackerItem:item] atIndex:position]; +} + +static void +menu_item_removed (gint position, + gpointer user_data) +{ + GNSMenu *menu = user_data; + + [menu removeItemAtIndex:position]; +} + +@implementation GNSMenu + +- (id)initWithTitle:(NSString *)title model:(GMenuModel *)model observable:(GtkActionObservable *)observable +{ + self = [super initWithTitle:title]; + + if (self != nil) + { + [self setAutoenablesItems:NO]; + + tracker = gtk_menu_tracker_new (observable, + model, + NO, + NULL, + menu_item_inserted, + menu_item_removed, + self); + } + + return self; +} + +- (id)initWithTitle:(NSString *)title trackerItem:(GtkMenuTrackerItem *)trackerItem +{ + self = [super initWithTitle:title]; + + if (self != nil) + { + [self setAutoenablesItems:NO]; + + tracker = gtk_menu_tracker_new_for_item_submenu (trackerItem, + menu_item_inserted, + menu_item_removed, + self); + } + + return self; +} + +- (void)dealloc +{ + gtk_menu_tracker_free (tracker); + + [super dealloc]; +} + +@end + +void +gtk_application_impl_quartz_setup_menu (GMenuModel *model, + GtkActionMuxer *muxer) +{ + NSMenu *menu; + + if (model != NULL) + menu = [[GNSMenu alloc] initWithTitle:@"Main Menu" model:model observable:GTK_ACTION_OBSERVABLE (muxer)]; + else + menu = [[NSMenu alloc] init]; + + [NSApp setMainMenu:menu]; + [menu release]; +} diff --git a/gtk/gtkapplication-quartz.c b/gtk/gtkapplication-quartz.c index 9d9f89707a..4527f62ece 100644 --- a/gtk/gtkapplication-quartz.c +++ b/gtk/gtkapplication-quartz.c @@ -21,7 +21,6 @@ #include "config.h" #include "gtkapplicationprivate.h" -#include "gtkmodelmenu-quartz.h" #import typedef struct @@ -46,6 +45,9 @@ typedef struct { GtkApplicationImpl impl; + GtkActionMuxer *muxer; + GMenu *combined; + GSList *inhibitors; gint quit_inhibit; guint next_cookie; @@ -83,20 +85,6 @@ G_DEFINE_TYPE (GtkApplicationImplQuartz, gtk_application_impl_quartz, GTK_TYPE_A } @end -static void -gtk_application_impl_quartz_menu_changed (GtkApplicationImplQuartz *quartz) -{ - GMenu *combined; - - combined = g_menu_new (); - g_menu_append_submenu (combined, "Application", gtk_application_get_app_menu (quartz->impl.application)); - g_menu_append_section (combined, NULL, gtk_application_get_menubar (quartz->impl.application)); - - gtk_quartz_set_main_menu (G_MENU_MODEL (combined), quartz->impl.application); - - g_object_unref (combined); -} - static void gtk_application_impl_quartz_startup (GtkApplicationImpl *impl, gboolean register_session) @@ -109,7 +97,17 @@ gtk_application_impl_quartz_startup (GtkApplicationImpl *impl, [NSApp setDelegate: quartz->delegate]; } - gtk_application_impl_quartz_menu_changed (quartz); + quartz->muxer = gtk_action_muxer_new (); + gtk_action_muxer_set_parent (quartz->muxer, gtk_application_get_action_muxer (impl->application)); + + /* app menu must come first so that we always see index '0' in + * 'combined' as being the app menu. + */ + gtk_application_impl_set_app_menu (impl, gtk_application_get_app_menu (impl->application)); + gtk_application_impl_set_menubar (impl, gtk_application_get_menubar (impl->application)); + + /* OK. Now put it in the menu. */ + gtk_application_impl_quartz_setup_menu (G_MENU_MODEL (quartz->combined), quartz->muxer); [NSApp finishLaunching]; } @@ -119,7 +117,8 @@ gtk_application_impl_quartz_shutdown (GtkApplicationImpl *impl) { GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl; - gtk_quartz_clear_main_menu (); + /* destroy our custom menubar */ + [NSApp setMainMenu:[[[NSMenu alloc] init] autorelease]]; if (quartz->delegate) { @@ -131,13 +130,39 @@ gtk_application_impl_quartz_shutdown (GtkApplicationImpl *impl) quartz->inhibitors = NULL; } +static void +gtk_application_impl_quartz_active_window_changed (GtkApplicationImpl *impl, + GtkWindow *window) +{ + GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl; + + gtk_action_muxer_remove (quartz->muxer, "win"); + + if (G_IS_ACTION_GROUP (window)) + gtk_action_muxer_insert (quartz->muxer, "win", G_ACTION_GROUP (window)); +} + static void gtk_application_impl_quartz_set_app_menu (GtkApplicationImpl *impl, GMenuModel *app_menu) { GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl; - gtk_application_impl_quartz_menu_changed (quartz); + /* If there are any items at all, then the first one is the app menu */ + if (g_menu_model_get_n_items (G_MENU_MODEL (quartz->combined))) + g_menu_remove (quartz->combined, 0); + + if (app_menu) + g_menu_prepend_submenu (quartz->combined, "Application", app_menu); + else + { + GMenu *empty; + + /* We must preserve the rule that index 0 is the app menu */ + empty = g_menu_new (); + g_menu_prepend_submenu (quartz->combined, "Application", G_MENU_MODEL (empty)); + g_object_unref (empty); + } } static void @@ -146,7 +171,12 @@ gtk_application_impl_quartz_set_menubar (GtkApplicationImpl *impl, { GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl; - gtk_application_impl_quartz_menu_changed (quartz); + /* If we have the menubar, it is a section at index '1' */ + if (g_menu_model_get_n_items (G_MENU_MODEL (quartz->combined)) > 1) + g_menu_remove (quartz->combined, 1); + + if (menubar) + g_menu_append_section (quartz->combined, NULL, menubar); } static guint @@ -211,6 +241,7 @@ gtk_application_impl_quartz_is_inhibited (GtkApplicationImpl *impl, static void gtk_application_impl_quartz_init (GtkApplicationImplQuartz *quartz) { + quartz->combined = g_menu_new (); } static void @@ -218,7 +249,7 @@ gtk_application_impl_quartz_finalize (GObject *object) { GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) object; - g_slist_free_full (quartz->inhibitors, (GDestroyNotify) gtk_application_quartz_inhibitor_free); + g_clear_object (&quartz->combined); G_OBJECT_CLASS (gtk_application_impl_quartz_parent_class)->finalize (object); } @@ -230,6 +261,7 @@ gtk_application_impl_quartz_class_init (GtkApplicationImplClass *class) class->startup = gtk_application_impl_quartz_startup; class->shutdown = gtk_application_impl_quartz_shutdown; + class->active_window_changed = gtk_application_impl_quartz_active_window_changed; class->set_app_menu = gtk_application_impl_quartz_set_app_menu; class->set_menubar = gtk_application_impl_quartz_set_menubar; class->inhibit = gtk_application_impl_quartz_inhibit; diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h index db2fa0086c..7847fcfb22 100644 --- a/gtk/gtkapplicationprivate.h +++ b/gtk/gtkapplicationprivate.h @@ -208,4 +208,8 @@ G_GNUC_INTERNAL gchar * gtk_application_impl_dbus_get_window_path (GtkApplicationImplDBus *dbus, GtkWindow *window); +G_GNUC_INTERNAL +void gtk_application_impl_quartz_setup_menu (GMenuModel *model, + GtkActionMuxer *muxer); + #endif /* __GTK_APPLICATION_PRIVATE_H__ */ diff --git a/gtk/gtkmodelmenu-quartz.c b/gtk/gtkmodelmenu-quartz.c deleted file mode 100644 index b099831412..0000000000 --- a/gtk/gtkmodelmenu-quartz.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright © 2011 William Hua, Ryan Lortie - * - * 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 licence, 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, see . - * - * Author: William Hua - * Ryan Lortie - */ - -#include "gtkmodelmenu-quartz.h" - -#include -#include "gtkaccelmapprivate.h" -#include "gtkactionhelper.h" -#include "../gdk/quartz/gdkquartz.h" - -#import - - - -@interface GNSMenu : NSMenu -{ - GtkApplication *application; - GMenuModel *model; - guint update_idle; - GSList *connected; - gboolean with_separators; -} - -- (id)initWithTitle:(NSString *)title model:(GMenuModel *)aModel application:(GtkApplication *)application hasSeparators:(BOOL)hasSeparators; - -- (void)model:(GMenuModel *)model didChangeAtPosition:(NSInteger)position removed:(NSInteger)removed added:(NSInteger)added; - -- (gboolean)handleChanges; - -@end - - - -@interface GNSMenuItem : NSMenuItem -{ - GtkActionHelper *helper; -} - -- (id)initWithModel:(GMenuModel *)model index:(NSInteger)index application:(GtkApplication *)application; - -- (void)didSelectItem:(id)sender; - -- (void)helperChanged; - -@end - - - -static gboolean -gtk_quartz_model_menu_handle_changes (gpointer user_data) -{ - GNSMenu *menu = user_data; - - return [menu handleChanges]; -} - -static void -gtk_quartz_model_menu_items_changed (GMenuModel *model, - gint position, - gint removed, - gint added, - gpointer user_data) -{ - GNSMenu *menu = user_data; - - [menu model:model didChangeAtPosition:position removed:removed added:added]; -} - -void -gtk_quartz_set_main_menu (GMenuModel *model, - GtkApplication *application) -{ - [NSApp setMainMenu:[[[GNSMenu alloc] initWithTitle:@"Main Menu" model:model application:application hasSeparators:NO] autorelease]]; -} - -void -gtk_quartz_clear_main_menu (void) -{ - // ensure that we drop all GNSMenuItem (to ensure 'application' has no extra references) - [NSApp setMainMenu:[[[NSMenu alloc] init] autorelease]]; -} - -@interface GNSMenu () - -- (void)appendFromModel:(GMenuModel *)aModel withSeparators:(BOOL)withSeparators; - -@end - - - -@implementation GNSMenu - -- (void)model:(GMenuModel *)model didChangeAtPosition:(NSInteger)position removed:(NSInteger)removed added:(NSInteger)added -{ - if (update_idle == 0) - update_idle = gdk_threads_add_idle (gtk_quartz_model_menu_handle_changes, self); -} - -- (void)appendItemFromModel:(GMenuModel *)aModel atIndex:(gint)index withHeading:(gchar **)heading -{ - GMenuModel *section; - - if ((section = g_menu_model_get_item_link (aModel, index, G_MENU_LINK_SECTION))) - { - g_menu_model_get_item_attribute (aModel, index, G_MENU_ATTRIBUTE_LABEL, "s", heading); - [self appendFromModel:section withSeparators:NO]; - g_object_unref (section); - } - else - [self addItem:[[[GNSMenuItem alloc] initWithModel:aModel index:index application:application] autorelease]]; -} - -- (void)appendFromModel:(GMenuModel *)aModel withSeparators:(BOOL)withSeparators -{ - gint n, i; - - g_signal_connect (aModel, "items-changed", G_CALLBACK (gtk_quartz_model_menu_items_changed), self); - connected = g_slist_prepend (connected, g_object_ref (aModel)); - - n = g_menu_model_get_n_items (aModel); - - for (i = 0; i < n; i++) - { - NSInteger ourPosition = [self numberOfItems]; - gchar *heading = NULL; - - [self appendItemFromModel:aModel atIndex:i withHeading:&heading]; - - if (withSeparators && ourPosition < [self numberOfItems]) - { - NSMenuItem *separator = nil; - - if (heading) - { - separator = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:heading] action:NULL keyEquivalent:@""] autorelease]; - - [separator setEnabled:NO]; - } - else if (ourPosition > 0) - separator = [NSMenuItem separatorItem]; - - if (separator != nil) - [self insertItem:separator atIndex:ourPosition]; - } - - g_free (heading); - } -} - -- (void)populate -{ - /* removeAllItems is available only in 10.6 and later, but it's more - efficient than iterating over the array of - NSMenuItems. performSelector: suppresses a compiler warning when - building on earlier OSX versions. */ - if ([self respondsToSelector: @selector (removeAllItems)]) - [self performSelector: @selector (removeAllItems)]; - else - { - /* Iterate from the bottom up to save reindexing the NSArray. */ - int i; - for (i = [self numberOfItems]; i > 0; i--) - [self removeItemAtIndex: i]; - } - - [self appendFromModel:model withSeparators:with_separators]; -} - -- (gboolean)handleChanges -{ - while (connected) - { - g_signal_handlers_disconnect_by_func (connected->data, gtk_quartz_model_menu_items_changed, self); - g_object_unref (connected->data); - - connected = g_slist_delete_link (connected, connected); - } - - [self populate]; - - update_idle = 0; - - return G_SOURCE_REMOVE; -} - -- (id)initWithTitle:(NSString *)title model:(GMenuModel *)aModel application:(GtkApplication *)anApplication hasSeparators:(BOOL)hasSeparators -{ - if((self = [super initWithTitle:title]) != nil) - { - [self setAutoenablesItems:NO]; - - model = g_object_ref (aModel); - application = g_object_ref (anApplication); - with_separators = hasSeparators; - - [self populate]; - } - - return self; -} - -- (void)dealloc -{ - while (connected) - { - g_signal_handlers_disconnect_by_func (connected->data, gtk_quartz_model_menu_items_changed, self); - g_object_unref (connected->data); - - connected = g_slist_delete_link (connected, connected); - } - - g_object_unref (application); - g_object_unref (model); - - [super dealloc]; -} - -@end - - - -static void -gtk_quartz_action_helper_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - GNSMenuItem *item = user_data; - - [item helperChanged]; -} - -@implementation GNSMenuItem - -- (id)initWithModel:(GMenuModel *)model index:(NSInteger)index application:(GtkApplication *)application -{ - gchar *title = NULL; - - if (g_menu_model_get_item_attribute (model, index, G_MENU_ATTRIBUTE_LABEL, "s", &title)) - { - gchar *from, *to; - - to = from = title; - - while (*from) - { - if (*from == '_' && from[1]) - from++; - - *to++ = *from++; - } - - *to = '\0'; - } - - if ((self = [super initWithTitle:[NSString stringWithUTF8String:title ? : ""] action:@selector(didSelectItem:) keyEquivalent:@""]) != nil) - { - GMenuModel *submenu; - gchar *action; - GVariant *target; - - action = NULL; - g_menu_model_get_item_attribute (model, index, G_MENU_ATTRIBUTE_ACTION, "s", &action); - target = g_menu_model_get_item_attribute_value (model, index, G_MENU_ATTRIBUTE_TARGET, NULL); - - if ((submenu = g_menu_model_get_item_link (model, index, G_MENU_LINK_SUBMENU))) - { - [self setSubmenu:[[[GNSMenu alloc] initWithTitle:[NSString stringWithUTF8String:title] model:submenu application:application hasSeparators:YES] autorelease]]; - g_object_unref (submenu); - } - - else if (action != NULL) - { - GtkAccelKey key; - gchar *path; - - helper = gtk_action_helper_new_with_application (application); - gtk_action_helper_set_action_name (helper, action); - gtk_action_helper_set_action_target_value (helper, target); - - g_signal_connect (helper, "notify", G_CALLBACK (gtk_quartz_action_helper_changed), self); - - [self helperChanged]; - - path = _gtk_accel_path_for_action (action, target); - if (gtk_accel_map_lookup_entry (path, &key)) - { - unichar character = gdk_quartz_get_key_equivalent (key.accel_key); - - if (character) - { - NSUInteger modifiers = 0; - - if (key.accel_mods & GDK_SHIFT_MASK) - modifiers |= NSShiftKeyMask; - - if (key.accel_mods & GDK_MOD1_MASK) - modifiers |= NSAlternateKeyMask; - - if (key.accel_mods & GDK_CONTROL_MASK) - modifiers |= NSControlKeyMask; - - if (key.accel_mods & GDK_META_MASK) - modifiers |= NSCommandKeyMask; - - [self setKeyEquivalent:[NSString stringWithCharacters:&character length:1]]; - [self setKeyEquivalentModifierMask:modifiers]; - } - } - - g_free (path); - - [self setTarget:self]; - } - } - - g_free (title); - - return self; -} - -- (void)dealloc -{ - if (helper != NULL) - g_object_unref (helper); - - [super dealloc]; -} - -- (void)didSelectItem:(id)sender -{ - gtk_action_helper_activate (helper); -} - -- (void)helperChanged -{ - [self setEnabled:gtk_action_helper_get_enabled (helper)]; - [self setState:gtk_action_helper_get_active (helper)]; - - switch (gtk_action_helper_get_role (helper)) - { - case GTK_ACTION_HELPER_ROLE_NORMAL: - [self setOnStateImage:nil]; - break; - case GTK_ACTION_HELPER_ROLE_TOGGLE: - [self setOnStateImage:[NSImage imageNamed:@"NSMenuCheckmark"]]; - break; - case GTK_ACTION_HELPER_ROLE_RADIO: - [self setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]]; - break; - default: - g_assert_not_reached (); - } -} - -@end diff --git a/gtk/gtkmodelmenu-quartz.h b/gtk/gtkmodelmenu-quartz.h deleted file mode 100644 index 1be9220363..0000000000 --- a/gtk/gtkmodelmenu-quartz.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2011 William Hua - * - * 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 licence, 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, see . - * - * Author: William Hua - */ - -#ifndef __GTK_MODELMENU_QUARTZ_H__ -#define __GTK_MODELMENU_QUARTZ_H__ - -#include "gtkapplication.h" - -void gtk_quartz_set_main_menu (GMenuModel *model, - GtkApplication *application); - -void gtk_quartz_clear_main_menu (void); - -#endif /* __GTK_MODELMENU_QUARTZ_H__ */