[gtk+] begin GtkApplication menu support for Mac OS
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] begin GtkApplication menu support for Mac OS
- Date: Mon, 19 Dec 2011 18:03:23 +0000 (UTC)
commit 8bc7513a7b7f9a487cfb0b84cecdc9b973dd1b07
Author: William Hua <william attente ca>
Date: Sat Dec 10 18:51:30 2011 -0500
begin GtkApplication menu support for Mac OS
gtk/Makefile.am | 3 +
gtk/gtkapplication.c | 72 +++++++++++
gtk/gtkquartz-menu.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++
gtk/gtkquartz-menu.h | 30 +++++
4 files changed, 441 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index c5cd39a..a5fb322 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -805,6 +805,8 @@ gtk_use_win32_c_sources = \
gtk_use_quartz_c_sources = \
gtksearchenginequartz.c \
gtkmountoperation-stub.c \
+ gtkquartz-menu.h \
+ gtkquartz-menu.c \
gtkquartz.c
gtk_use_stub_c_sources = \
gtkmountoperation-stub.c
@@ -825,6 +827,7 @@ else
if USE_QUARTZ
gtk_private_h_sources += \
gtksearchenginequartz.h \
+ gtkmenuquartz.h \
gtkquartz.h
gtk_c_sources += $(gtk_use_quartz_c_sources)
libgtk_3_la_CFLAGS = "-xobjective-c"
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index 097009a..8c0deba 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -32,6 +32,12 @@
#include "gtkmain.h"
#include "gtkapplicationwindow.h"
#include "gtkaccelmapprivate.h"
+#include "gactionmuxer.h"
+
+#ifdef GDK_WINDOWING_QUARTZ
+#include "gtkquartz-menu.h"
+#import <Cocoa/Cocoa.h>
+#endif
#include <gdk/gdk.h>
#ifdef GDK_WINDOWING_X11
@@ -83,6 +89,11 @@ struct _GtkApplicationPrivate
gchar *window_prefix;
guint next_id;
#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+ GActionMuxer *muxer;
+ GMenu *combined;
+#endif
};
#ifdef GDK_WINDOWING_X11
@@ -213,6 +224,55 @@ gtk_application_shutdown_x11 (GtkApplication *application)
}
#endif
+#ifdef GDK_WINDOWING_QUARTZ
+static void
+gtk_application_menu_changed_quartz (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkApplication *application = GTK_APPLICATION (object);
+ GMenu *combined;
+
+ combined = g_menu_new ();
+ g_menu_append_submenu (combined, "Application", g_application_get_app_menu (G_APPLICATION (object)));
+ g_menu_append_section (combined, NULL, g_application_get_menubar (G_APPLICATION (object)));
+
+ gtk_quartz_set_main_menu (G_MENU_MODEL (combined), G_ACTION_OBSERVABLE (application->priv->muxer));
+}
+
+static void
+gtk_application_startup_quartz (GtkApplication *application)
+{
+ [NSApp finishLaunching];
+
+ application->priv->muxer = g_action_muxer_new ();
+ g_action_muxer_insert (application->priv->muxer, "app", G_ACTION_GROUP (application));
+
+ g_signal_connect (application, "notify::app-menu", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
+ g_signal_connect (application, "notify::menubar", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
+ gtk_application_menu_changed_quartz (G_OBJECT (application), NULL, NULL);
+}
+
+static void
+gtk_application_shutdown_quartz (GtkApplication *application)
+{
+ g_signal_handlers_disconnect_by_func (application, gtk_application_menu_changed_quartz, NULL);
+
+ g_object_unref (application->priv->muxer);
+ application->priv->muxer = NULL;
+}
+
+static void
+gtk_application_focus_changed (GtkApplication *application,
+ GtkWindow *window)
+{
+ if (G_IS_ACTION_GROUP (window))
+ g_action_muxer_insert (application->priv->muxer, "win", G_ACTION_GROUP (window));
+ else
+ g_action_muxer_remove (application->priv->muxer, "win");
+}
+#endif
+
static gboolean
gtk_application_focus_in_event_cb (GtkWindow *window,
GdkEventFocus *event,
@@ -229,6 +289,10 @@ gtk_application_focus_in_event_cb (GtkWindow *window,
priv->windows = g_list_concat (link, priv->windows);
}
+#ifdef GDK_WINDOWING_QUARTZ
+ gtk_application_focus_changed (application, window);
+#endif
+
return FALSE;
}
@@ -243,6 +307,10 @@ gtk_application_startup (GApplication *application)
#ifdef GDK_WINDOWING_X11
gtk_application_startup_x11 (GTK_APPLICATION (application));
#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+ gtk_application_startup_quartz (GTK_APPLICATION (application));
+#endif
}
static void
@@ -252,6 +320,10 @@ gtk_application_shutdown (GApplication *application)
gtk_application_shutdown_x11 (GTK_APPLICATION (application));
#endif
+#ifdef GDK_WINDOWING_QUARTZ
+ gtk_application_shutdown_quartz (GTK_APPLICATION (application));
+#endif
+
G_APPLICATION_CLASS (gtk_application_parent_class)
->shutdown (application);
}
diff --git a/gtk/gtkquartz-menu.c b/gtk/gtkquartz-menu.c
new file mode 100644
index 0000000..3a6e900
--- /dev/null
+++ b/gtk/gtkquartz-menu.c
@@ -0,0 +1,336 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: William Hua <william attente ca>
+ */
+
+#include "gtkquartz-menu.h"
+
+#import <Cocoa/Cocoa.h>
+
+typedef struct _GtkQuartzActionObserver GtkQuartzActionObserver;
+
+ interface GNSMenuItem : NSMenuItem
+{
+ gchar *action;
+ GVariant *target;
+ BOOL canActivate;
+ GActionGroup *actions;
+ GtkQuartzActionObserver *observer;
+}
+
+- (id)initWithModel:(GMenuModel *)model index:(NSInteger)index observable:(GActionObservable *)observable;
+
+
+
+- (void)observableActionAddedWithParameterType:(const GVariantType *)parameterType enabled:(BOOL)enabled state:(GVariant *)state;
+- (void)observableActionEnabledChangedTo:(BOOL)enabled;
+- (void)observableActionStateChangedTo:(GVariant *)state;
+- (void)observableActionRemoved;
+
+- (void)didSelectItem:(id)sender;
+
+ end
+
+
+
+struct _GtkQuartzActionObserver
+{
+ GObject parent_instance;
+
+ GNSMenuItem *item;
+};
+
+
+typedef GObjectClass GtkQuartzActionObserverClass;
+
+static void gtk_quartz_action_observer_observer_iface_init (GActionObserverInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GtkQuartzActionObserver, gtk_quartz_action_observer, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, gtk_quartz_action_observer_observer_iface_init))
+
+static void
+gtk_quartz_action_observer_action_added (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name,
+ const GVariantType *parameter_type,
+ gboolean enabled,
+ GVariant *state)
+{
+ GtkQuartzActionObserver *qao = (GtkQuartzActionObserver *) observer;
+
+ [qao->item observableActionAddedWithParameterType:parameter_type enabled:enabled state:state];
+}
+
+static void
+gtk_quartz_action_observer_action_enabled_changed (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name,
+ gboolean enabled)
+{
+ GtkQuartzActionObserver *qao = (GtkQuartzActionObserver *) observer;
+
+ [qao->item observableActionEnabledChangedTo:enabled];
+}
+
+static void
+gtk_quartz_action_observer_action_state_changed (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name,
+ GVariant *state)
+{
+ GtkQuartzActionObserver *qao = (GtkQuartzActionObserver *) observer;
+
+ [qao->item observableActionStateChangedTo:state];
+}
+
+static void
+gtk_quartz_action_observer_action_removed (GActionObserver *observer,
+ GActionObservable *observable,
+ const gchar *action_name)
+{
+ GtkQuartzActionObserver *qao = (GtkQuartzActionObserver *) observer;
+
+ [qao->item observableActionRemoved];
+}
+
+static void
+gtk_quartz_action_observer_init (GtkQuartzActionObserver *item)
+{
+}
+
+static void
+gtk_quartz_action_observer_observer_iface_init (GActionObserverInterface *iface)
+{
+ iface->action_added = gtk_quartz_action_observer_action_added;
+ iface->action_enabled_changed = gtk_quartz_action_observer_action_enabled_changed;
+ iface->action_state_changed = gtk_quartz_action_observer_action_state_changed;
+ iface->action_removed = gtk_quartz_action_observer_action_removed;
+}
+
+static void
+gtk_quartz_action_observer_class_init (GtkQuartzActionObserverClass *class)
+{
+}
+
+static GtkQuartzActionObserver *
+gtk_quartz_action_observer_new (GNSMenuItem *item)
+{
+ GtkQuartzActionObserver *observer;
+
+ observer = g_object_new (gtk_quartz_action_observer_get_type (), NULL);
+ observer->item = item;
+
+ return observer;
+}
+
+NSMenu *
+gtk_menu_quartz_create_menu (const gchar *title,
+ GMenuModel *model,
+ GActionObservable *observable)
+{
+ if (model == NULL)
+ return nil;
+
+ NSMenu *menu = [[[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:title ? : ""]] autorelease];
+
+ [menu setAutoenablesItems:NO];
+
+ gint n = g_menu_model_get_n_items (model);
+ gint i;
+
+ for (i = 0; i < n; i++)
+ {
+ gchar *label = NULL;
+
+ if (g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label))
+ {
+ gchar *from, *to;
+
+ to = from = label;
+
+ while (*from)
+ {
+ if (*from == '_' && from[1])
+ from++;
+
+ *to++ = *from++;
+ }
+
+ *to = '\0';
+ }
+
+ NSString *text = [NSString stringWithUTF8String:label ? : ""];
+
+ NSMenu *section = gtk_menu_quartz_create_menu (label, g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION), observable);
+ NSMenu *submenu = gtk_menu_quartz_create_menu (label, g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU), observable);
+
+ if (section != nil)
+ {
+ if ([menu numberOfItems] > 0)
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ if ([text length] > 0)
+ {
+ NSMenuItem *header = [[[NSMenuItem alloc] initWithTitle:text action:NULL keyEquivalent:@""] autorelease];
+
+ [header setEnabled:NO];
+
+ [menu addItem:header];
+ }
+
+ for (NSMenuItem *item in [section itemArray])
+ {
+ [item retain];
+ [[item menu] removeItem:item];
+ [menu addItem:item];
+ [item release];
+ }
+ }
+ else if (submenu != nil)
+ {
+ NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:text action:NULL keyEquivalent:@""] autorelease];
+
+ [item setSubmenu:submenu];
+
+ [menu addItem:item];
+ }
+ else
+ [menu addItem:[[[GNSMenuItem alloc] initWithModel:model index:i observable:observable] autorelease]];
+ }
+
+ return menu;
+}
+
+void
+gtk_quartz_set_main_menu (GMenuModel *model,
+ GActionObservable *observable)
+{
+ [NSApp setMainMenu:gtk_menu_quartz_create_menu ("Main Menu", model, observable)];
+}
+
+
+
+ implementation GNSMenuItem
+
+- (id)initWithModel:(GMenuModel *)model index:(NSInteger)index observable:(GActionObservable *)observable
+{
+ 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)
+ {
+ 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);
+ actions = g_object_ref (observable);
+ observer = gtk_quartz_action_observer_new (self);
+
+ if (action != NULL)
+ {
+ g_action_observable_register_observer (observable, action, G_ACTION_OBSERVER (observer));
+
+ [self setTarget:self];
+
+ gboolean enabled;
+ const GVariantType *parameterType;
+ GVariant *state;
+
+ if (g_action_group_query_action (G_ACTION_GROUP (actions), action, &enabled, ¶meterType, NULL, NULL, &state))
+ [self observableActionAddedWithParameterType:parameterType enabled:enabled state:state];
+ else
+ [self setEnabled:NO];
+ }
+ }
+
+ g_free (title);
+
+ return self;
+}
+
+- (void)observableActionAddedWithParameterType:(const GVariantType *)parameterType enabled:(BOOL)enabled state:(GVariant *)state
+{
+ canActivate = (target == NULL && parameterType == NULL) ||
+ (target != NULL && parameterType != NULL &&
+ g_variant_is_of_type (target, parameterType));
+
+ if (canActivate)
+ {
+ if (target != NULL && state != NULL)
+ {
+ [self setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]];
+ [self setState:g_variant_equal (state, target) ? NSOnState : NSOffState];
+ }
+ else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ {
+ [self setOnStateImage:[NSImage imageNamed:@"NSMenuCheckmark"]];
+ [self setState:g_variant_get_boolean (state) ? NSOnState : NSOffState];
+ }
+ else
+ [self setState:NSOffState];
+
+ [self setEnabled:enabled];
+ }
+ else
+ [self setEnabled:NO];
+}
+
+- (void)observableActionEnabledChangedTo:(BOOL)enabled
+{
+ if (canActivate)
+ [self setEnabled:enabled];
+}
+
+- (void)observableActionStateChangedTo:(GVariant *)state
+{
+ if (canActivate)
+ {
+ if (target != NULL)
+ [self setState:g_variant_equal (state, target) ? NSOnState : NSOffState];
+ else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ [self setState:g_variant_get_boolean (state) ? NSOnState : NSOffState];
+ }
+}
+
+- (void)observableActionRemoved
+{
+ if (canActivate)
+ [self setEnabled:NO];
+}
+
+- (void)didSelectItem:(id)sender
+{
+ if (canActivate)
+ g_action_group_activate_action (actions, action, target);
+}
+
+ end
diff --git a/gtk/gtkquartz-menu.h b/gtk/gtkquartz-menu.h
new file mode 100644
index 0000000..5e1c2d7
--- /dev/null
+++ b/gtk/gtkquartz-menu.h
@@ -0,0 +1,30 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: William Hua <william attente ca>
+ */
+
+#ifndef __GTK_QUARTZ_MENU_H__
+#define __GTK_QUARTZ_MENU_H__
+
+#include "gactionobservable.h"
+
+void gtk_quartz_set_main_menu (GMenuModel *model,
+ GActionObservable *observable);
+
+#endif /* __GTK_QUARTZ_MENU_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]