forked from AuroraMiddleware/gtk
begin GtkApplication menu support for Mac OS
This commit is contained in:
parent
8a21201e2c
commit
8bc7513a7b
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
336
gtk/gtkquartz-menu.c
Normal file
336
gtk/gtkquartz-menu.c
Normal file
@ -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
|
30
gtk/gtkquartz-menu.h
Normal file
30
gtk/gtkquartz-menu.h
Normal file
@ -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__ */
|
Loading…
Reference in New Issue
Block a user