[gtk+] Use GtkMenuTracker for Quartz backend.
- From: Ryan Lortie <desrt src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] Use GtkMenuTracker for Quartz backend.
- Date: Wed, 8 Jan 2014 23:16:19 +0000 (UTC)
commit 649ff84d91a29ce049001882c251cae1950e2eac
Author: William Hua <william attente ca>
Date: Wed Jan 8 17:27:43 2014 -0500
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(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 9a33f28..6e71fba 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 0000000..0e00ba5
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: William Hua <william attente ca>
+ * Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gtkapplicationprivate.h"
+#include "gtkmenutracker.h"
+#include "gtkicontheme.h"
+#include "gtktoolbar.h"
+#include "gtkquartz.h"
+
+#include <gdk/quartz/gdkquartz.h>
+
+#import <Cocoa/Cocoa.h>
+
+#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 9d9f897..4527f62 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 <Cocoa/Cocoa.h>
typedef struct
@@ -46,6 +45,9 @@ typedef struct
{
GtkApplicationImpl impl;
+ GtkActionMuxer *muxer;
+ GMenu *combined;
+
GSList *inhibitors;
gint quit_inhibit;
guint next_cookie;
@@ -84,20 +86,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)
{
@@ -132,12 +131,38 @@ gtk_application_impl_quartz_shutdown (GtkApplicationImpl *impl)
}
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 db2fa00..7847fcf 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__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]