Compare commits
9 Commits
wip/matthi
...
wip/quartz
Author | SHA1 | Date | |
---|---|---|---|
|
f68f2900fa | ||
|
255d3a1826 | ||
|
d8b8da4ad3 | ||
|
4894dccd9c | ||
|
2d7110a2cd | ||
|
0c03793b63 | ||
|
3396c66346 | ||
|
130ff2a3fc | ||
|
e8998c0883 |
@@ -6677,6 +6677,7 @@ gtk_icon_info_set_raw_coordinates
|
||||
gtk_icon_info_get_embedded_rect
|
||||
gtk_icon_info_get_attach_points
|
||||
gtk_icon_info_get_display_name
|
||||
gtk_icon_info_is_symbolic
|
||||
<SUBSECTION Standard>
|
||||
GtkIconThemeClass
|
||||
GTK_ICON_THEME
|
||||
|
@@ -979,8 +979,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
|
||||
@@ -1018,7 +1018,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)
|
||||
@@ -1209,7 +1208,7 @@ gtktypebuiltins.c: $(gtk_public_h_sources) $(deprecated_h_sources) gtktypebuilti
|
||||
gtkresources.h: gtk.gresource.xml
|
||||
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/gtk.gresource.xml \
|
||||
--target=$@ --sourcedir=$(srcdir) --c-name _gtk --generate-header --manual-register
|
||||
gtkresources.c: gtk.gresource.xml gtk-default.css gtk-win32.css gtk-win32-xp.css gtk-win32-base.css gtk-win32-classic.css $(DND_CURSORS) $(COMPOSITE_TEMPLATES) $(template_headers)
|
||||
gtkresources.c: gtk.gresource.xml gtk-default.css gtk-win32.css gtk-win32-xp.css gtk-win32-base.css gtk-win32-classic.css $(DND_CURSORS) $(COMPOSITE_TEMPLATES) $(template_headers) gtkapplication-quartz.ui
|
||||
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/gtk.gresource.xml \
|
||||
--target=$@ --sourcedir=$(srcdir) --c-name _gtk --generate-source --manual-register
|
||||
|
||||
|
@@ -35,5 +35,6 @@
|
||||
<file compressed="true">gtkscalebutton.ui</file>
|
||||
<file compressed="true">gtkstatusbar.ui</file>
|
||||
<file compressed="true">gtkvolumebutton.ui</file>
|
||||
<file compressed="true">gtkapplication-quartz.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
718
gtk/gtkapplication-quartz-menu.c
Normal file
718
gtk/gtkapplication-quartz-menu.c
Normal file
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
* 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 <gdk/quartz/gdkquartz.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include "gtkmenutracker.h"
|
||||
#include "gtkicontheme.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define BUFFER_SIZE 1024
|
||||
#define ICON_SIZE 16
|
||||
|
||||
@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;
|
||||
NSMutableData *iconData;
|
||||
gpointer iconBuffer;
|
||||
GCancellable *cancellable;
|
||||
}
|
||||
|
||||
- (id)initWithTrackerItem:(GtkMenuTrackerItem *)aTrackerItem;
|
||||
|
||||
- (void)didChangeLabel;
|
||||
- (void)didChangeIcon;
|
||||
- (void)didChangeVisible;
|
||||
- (void)didChangeToggled;
|
||||
- (void)didChangeAccel;
|
||||
|
||||
- (void)didSelectItem:(id)sender;
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem;
|
||||
|
||||
@end
|
||||
|
||||
@interface GNSMenuItem ()
|
||||
|
||||
- (void)setImageFromLoadableIcon:(GLoadableIcon *)icon;
|
||||
- (void)readDataFromStream:(GInputStream *)stream;
|
||||
- (void)readDataFromStream:(GInputStream *)stream count:(gssize)count;
|
||||
- (void)reset;
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
* Code for key code conversion
|
||||
*
|
||||
* Copyright (C) 2009 Paul Davis
|
||||
*/
|
||||
static unichar
|
||||
get_key_equivalent (guint key)
|
||||
{
|
||||
if (key >= GDK_KEY_A && key <= GDK_KEY_Z)
|
||||
return key + (GDK_KEY_a - GDK_KEY_A);
|
||||
|
||||
if (key >= GDK_KEY_space && key <= GDK_KEY_asciitilde)
|
||||
return key;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case GDK_KEY_BackSpace:
|
||||
return NSBackspaceCharacter;
|
||||
case GDK_KEY_Delete:
|
||||
return NSDeleteFunctionKey;
|
||||
case GDK_KEY_Pause:
|
||||
return NSPauseFunctionKey;
|
||||
case GDK_KEY_Scroll_Lock:
|
||||
return NSScrollLockFunctionKey;
|
||||
case GDK_KEY_Sys_Req:
|
||||
return NSSysReqFunctionKey;
|
||||
case GDK_KEY_Home:
|
||||
return NSHomeFunctionKey;
|
||||
case GDK_KEY_Left:
|
||||
case GDK_KEY_leftarrow:
|
||||
return NSLeftArrowFunctionKey;
|
||||
case GDK_KEY_Up:
|
||||
case GDK_KEY_uparrow:
|
||||
return NSUpArrowFunctionKey;
|
||||
case GDK_KEY_Right:
|
||||
case GDK_KEY_rightarrow:
|
||||
return NSRightArrowFunctionKey;
|
||||
case GDK_KEY_Down:
|
||||
case GDK_KEY_downarrow:
|
||||
return NSDownArrowFunctionKey;
|
||||
case GDK_KEY_Page_Up:
|
||||
return NSPageUpFunctionKey;
|
||||
case GDK_KEY_Page_Down:
|
||||
return NSPageDownFunctionKey;
|
||||
case GDK_KEY_End:
|
||||
return NSEndFunctionKey;
|
||||
case GDK_KEY_Begin:
|
||||
return NSBeginFunctionKey;
|
||||
case GDK_KEY_Select:
|
||||
return NSSelectFunctionKey;
|
||||
case GDK_KEY_Print:
|
||||
return NSPrintFunctionKey;
|
||||
case GDK_KEY_Execute:
|
||||
return NSExecuteFunctionKey;
|
||||
case GDK_KEY_Insert:
|
||||
return NSInsertFunctionKey;
|
||||
case GDK_KEY_Undo:
|
||||
return NSUndoFunctionKey;
|
||||
case GDK_KEY_Redo:
|
||||
return NSRedoFunctionKey;
|
||||
case GDK_KEY_Menu:
|
||||
return NSMenuFunctionKey;
|
||||
case GDK_KEY_Find:
|
||||
return NSFindFunctionKey;
|
||||
case GDK_KEY_Help:
|
||||
return NSHelpFunctionKey;
|
||||
case GDK_KEY_Break:
|
||||
return NSBreakFunctionKey;
|
||||
case GDK_KEY_Mode_switch:
|
||||
return NSModeSwitchFunctionKey;
|
||||
case GDK_KEY_F1:
|
||||
return NSF1FunctionKey;
|
||||
case GDK_KEY_F2:
|
||||
return NSF2FunctionKey;
|
||||
case GDK_KEY_F3:
|
||||
return NSF3FunctionKey;
|
||||
case GDK_KEY_F4:
|
||||
return NSF4FunctionKey;
|
||||
case GDK_KEY_F5:
|
||||
return NSF5FunctionKey;
|
||||
case GDK_KEY_F6:
|
||||
return NSF6FunctionKey;
|
||||
case GDK_KEY_F7:
|
||||
return NSF7FunctionKey;
|
||||
case GDK_KEY_F8:
|
||||
return NSF8FunctionKey;
|
||||
case GDK_KEY_F9:
|
||||
return NSF9FunctionKey;
|
||||
case GDK_KEY_F10:
|
||||
return NSF10FunctionKey;
|
||||
case GDK_KEY_F11:
|
||||
return NSF11FunctionKey;
|
||||
case GDK_KEY_F12:
|
||||
return NSF12FunctionKey;
|
||||
case GDK_KEY_F13:
|
||||
return NSF13FunctionKey;
|
||||
case GDK_KEY_F14:
|
||||
return NSF14FunctionKey;
|
||||
case GDK_KEY_F15:
|
||||
return NSF15FunctionKey;
|
||||
case GDK_KEY_F16:
|
||||
return NSF16FunctionKey;
|
||||
case GDK_KEY_F17:
|
||||
return NSF17FunctionKey;
|
||||
case GDK_KEY_F18:
|
||||
return NSF18FunctionKey;
|
||||
case GDK_KEY_F19:
|
||||
return NSF19FunctionKey;
|
||||
case GDK_KEY_F20:
|
||||
return NSF20FunctionKey;
|
||||
case GDK_KEY_F21:
|
||||
return NSF21FunctionKey;
|
||||
case GDK_KEY_F22:
|
||||
return NSF22FunctionKey;
|
||||
case GDK_KEY_F23:
|
||||
return NSF23FunctionKey;
|
||||
case GDK_KEY_F24:
|
||||
return NSF24FunctionKey;
|
||||
case GDK_KEY_F25:
|
||||
return NSF25FunctionKey;
|
||||
case GDK_KEY_F26:
|
||||
return NSF26FunctionKey;
|
||||
case GDK_KEY_F27:
|
||||
return NSF27FunctionKey;
|
||||
case GDK_KEY_F28:
|
||||
return NSF28FunctionKey;
|
||||
case GDK_KEY_F29:
|
||||
return NSF29FunctionKey;
|
||||
case GDK_KEY_F30:
|
||||
return NSF30FunctionKey;
|
||||
case GDK_KEY_F31:
|
||||
return NSF31FunctionKey;
|
||||
case GDK_KEY_F32:
|
||||
return NSF32FunctionKey;
|
||||
case GDK_KEY_F33:
|
||||
return NSF33FunctionKey;
|
||||
case GDK_KEY_F34:
|
||||
return NSF34FunctionKey;
|
||||
case GDK_KEY_F35:
|
||||
return NSF35FunctionKey;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return '\0';
|
||||
}
|
||||
|
||||
static void
|
||||
pixbuf_loaded (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkIconInfo *info = GTK_ICON_INFO (object);
|
||||
GNSMenuItem *item = user_data;
|
||||
GError *error = NULL;
|
||||
GdkPixbuf *pixbuf = gtk_icon_info_load_symbolic_finish (info, result, NULL, &error);
|
||||
|
||||
if (pixbuf != NULL)
|
||||
{
|
||||
if (G_IS_LOADABLE_ICON (pixbuf))
|
||||
[item setImageFromLoadableIcon:G_LOADABLE_ICON (pixbuf)];
|
||||
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
|
||||
if (error != NULL)
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
static void
|
||||
input_stream_created (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GLoadableIcon *icon = G_LOADABLE_ICON (object);
|
||||
GNSMenuItem *item = user_data;
|
||||
GError *error = NULL;
|
||||
GInputStream *stream = g_loadable_icon_load_finish (icon, result, NULL, &error);
|
||||
|
||||
if (stream != NULL)
|
||||
{
|
||||
[item readDataFromStream:stream];
|
||||
g_object_unref (stream);
|
||||
}
|
||||
else
|
||||
[item reset];
|
||||
|
||||
if (error != NULL)
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
static void
|
||||
input_stream_read (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GInputStream *stream = G_INPUT_STREAM (object);
|
||||
GNSMenuItem *item = user_data;
|
||||
GError *error = NULL;
|
||||
gssize count = g_input_stream_read_finish (stream, result, &error);
|
||||
|
||||
if (count >= 0)
|
||||
[item readDataFromStream:stream count:count];
|
||||
else
|
||||
[item reset];
|
||||
|
||||
if (error != NULL)
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
static void
|
||||
tracker_item_changed (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
static const gchar *label = NULL;
|
||||
static const gchar *icon = NULL;
|
||||
static const gchar *visible = NULL;
|
||||
static const gchar *toggled = NULL;
|
||||
static const gchar *accel = NULL;
|
||||
|
||||
GNSMenuItem *item = user_data;
|
||||
const gchar *name = g_param_spec_get_name (pspec);
|
||||
|
||||
if (G_UNLIKELY (label == NULL))
|
||||
label = g_intern_static_string ("label");
|
||||
if (G_UNLIKELY (icon == NULL))
|
||||
icon = g_intern_static_string ("icon");
|
||||
if (G_UNLIKELY (visible == NULL))
|
||||
visible = g_intern_static_string ("visible");
|
||||
if (G_UNLIKELY (toggled == NULL))
|
||||
toggled = g_intern_static_string ("toggled");
|
||||
if (G_UNLIKELY (accel == NULL))
|
||||
accel = g_intern_static_string ("accel");
|
||||
|
||||
if (name == label)
|
||||
[item didChangeLabel];
|
||||
else if (name == icon)
|
||||
[item didChangeIcon];
|
||||
else if (name == visible)
|
||||
[item didChangeVisible];
|
||||
else if (name == toggled)
|
||||
[item didChangeToggled];
|
||||
else if (name == accel)
|
||||
[item didChangeAccel];
|
||||
}
|
||||
|
||||
@implementation GNSMenuItem
|
||||
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
|
||||
{
|
||||
return gtk_menu_tracker_item_get_sensitive (trackerItem) ? YES : NO;
|
||||
}
|
||||
|
||||
- (id)initWithTrackerItem:(GtkMenuTrackerItem *)aTrackerItem
|
||||
{
|
||||
if ((self = [super initWithTitle:@""
|
||||
action:@selector(didSelectItem:)
|
||||
keyEquivalent:@""]) != nil)
|
||||
{
|
||||
const gchar *special = gtk_menu_tracker_item_get_special (aTrackerItem);
|
||||
|
||||
if (special && g_str_equal (special, "hide-this"))
|
||||
{
|
||||
[self setAction:@selector(hide:)];
|
||||
[self setTarget:NSApp];
|
||||
}
|
||||
else if (special && g_str_equal (special, "hide-others"))
|
||||
{
|
||||
[self setAction:@selector(hideOtherApplications:)];
|
||||
[self setTarget:NSApp];
|
||||
}
|
||||
else if (special && g_str_equal (special, "show-all"))
|
||||
{
|
||||
[self setAction:@selector(unhideAllApplications:)];
|
||||
[self setTarget:NSApp];
|
||||
}
|
||||
else
|
||||
[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 didChangeVisible];
|
||||
[self didChangeToggled];
|
||||
[self didChangeAccel];
|
||||
|
||||
if (gtk_menu_tracker_item_get_has_submenu (trackerItem))
|
||||
{
|
||||
[self setSubmenu:[[[GNSMenu alloc] initWithTitle:[self title] trackerItem:trackerItem] autorelease]];
|
||||
|
||||
if (special && g_str_equal (special, "services-submenu"))
|
||||
[NSApp setServicesMenu:[self submenu]];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (cancellable != NULL)
|
||||
{
|
||||
g_cancellable_cancel (cancellable);
|
||||
[self reset];
|
||||
}
|
||||
|
||||
g_signal_handler_disconnect (trackerItem, trackerItemChangedHandler);
|
||||
g_object_unref (trackerItem);
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
g_clear_object (&cancellable);
|
||||
g_free (iconBuffer);
|
||||
iconBuffer = NULL;
|
||||
[iconData release];
|
||||
iconData = nil;
|
||||
}
|
||||
|
||||
- (void)readDataFromStream:(GInputStream *)stream
|
||||
{
|
||||
g_return_if_fail (G_IS_INPUT_STREAM (stream));
|
||||
|
||||
g_input_stream_read_async (stream,
|
||||
iconBuffer,
|
||||
BUFFER_SIZE,
|
||||
G_PRIORITY_LOW,
|
||||
cancellable,
|
||||
input_stream_read,
|
||||
self);
|
||||
}
|
||||
|
||||
- (void)readDataFromStream:(GInputStream *)stream count:(gssize)count
|
||||
{
|
||||
g_return_if_fail (G_IS_INPUT_STREAM (stream));
|
||||
g_return_if_fail (count >= 0);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
[iconData appendBytes:iconBuffer length:count];
|
||||
[self readDataFromStream:stream];
|
||||
}
|
||||
else if (count == 0)
|
||||
{
|
||||
NSImage *image = [[[NSImage alloc] initWithData:iconData] autorelease];
|
||||
[image setSize:NSMakeSize(16, 16)];
|
||||
[self setImage:image];
|
||||
[self reset];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setImageFromLoadableIcon:(GLoadableIcon *)icon
|
||||
{
|
||||
if (GDK_IS_PIXBUF (icon))
|
||||
{
|
||||
NSImage *image = gdk_quartz_pixbuf_to_ns_image_libgtk_only (GDK_PIXBUF (icon));
|
||||
[image setSize:NSMakeSize(16, 16)];
|
||||
[self setImage:image];
|
||||
}
|
||||
else
|
||||
{
|
||||
iconData = [[NSMutableData alloc] init];
|
||||
iconBuffer = g_malloc (BUFFER_SIZE);
|
||||
cancellable = g_cancellable_new ();
|
||||
g_loadable_icon_load_async (icon, ICON_SIZE, cancellable, input_stream_created, self);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didChangeLabel
|
||||
{
|
||||
NSString *label = [NSString stringWithUTF8String:gtk_menu_tracker_item_get_label (trackerItem) ? : ""];
|
||||
NSMutableString *title = [NSMutableString stringWithCapacity:[label length]];
|
||||
NSRange range;
|
||||
int i;
|
||||
|
||||
range = [label rangeOfString:@"`gtk-private-appname`"];
|
||||
if (range.location != NSNotFound)
|
||||
label = [label stringByReplacingCharactersInRange:range withString:[[NSProcessInfo processInfo] processName]];
|
||||
|
||||
for (i = 0; i < [label length]; i++)
|
||||
{
|
||||
unichar c = [label characterAtIndex:i];
|
||||
|
||||
if (c == '_')
|
||||
{
|
||||
i++;
|
||||
|
||||
if (i >= [label length])
|
||||
break;
|
||||
|
||||
c = [label characterAtIndex:i];
|
||||
}
|
||||
|
||||
[title appendString:[NSString stringWithCharacters:&c length:1]];
|
||||
}
|
||||
|
||||
[self setTitle:title];
|
||||
}
|
||||
|
||||
- (void)didChangeIcon
|
||||
{
|
||||
GIcon *icon = gtk_menu_tracker_item_get_icon (trackerItem);
|
||||
|
||||
if (cancellable != NULL)
|
||||
{
|
||||
g_cancellable_cancel (cancellable);
|
||||
[self reset];
|
||||
}
|
||||
|
||||
while (G_IS_EMBLEM (icon) || G_IS_EMBLEMED_ICON (icon))
|
||||
{
|
||||
if (G_IS_EMBLEM (icon))
|
||||
icon = g_emblem_get_icon (G_EMBLEM (icon));
|
||||
else
|
||||
icon = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
|
||||
}
|
||||
|
||||
if (G_IS_FILE_ICON (icon))
|
||||
{
|
||||
NSImage *image = nil;
|
||||
GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
|
||||
gchar *path = g_file_get_path (file);
|
||||
|
||||
if (path != NULL)
|
||||
{
|
||||
image = [[[NSImage alloc] initByReferencingFile:[NSString stringWithUTF8String:path]] autorelease];
|
||||
g_free (path);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = g_file_get_uri (file);
|
||||
|
||||
if (path != NULL)
|
||||
{
|
||||
image = [[[NSImage alloc] initByReferencingURL:[NSURL URLWithString:[NSString stringWithUTF8String:path]]] autorelease];
|
||||
g_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
[image setSize:NSMakeSize(16, 16)];
|
||||
[self setImage:image];
|
||||
}
|
||||
else if (G_IS_THEMED_ICON (icon))
|
||||
{
|
||||
GtkIconTheme *theme = gtk_icon_theme_get_default ();
|
||||
|
||||
if (theme != NULL)
|
||||
{
|
||||
GtkIconInfo *info = gtk_icon_theme_lookup_by_gicon (theme, icon, ICON_SIZE, GTK_ICON_LOOKUP_USE_BUILTIN);
|
||||
|
||||
if (info != NULL)
|
||||
{
|
||||
GdkRGBA foreground = { 0.0, 0.0, 0.0, 1.0 };
|
||||
GdkRGBA success = { 0.0, 1.0, 0.0, 1.0 };
|
||||
GdkRGBA warning = { 1.0, 1.0, 0.0, 1.0 };
|
||||
GdkRGBA error = { 1.0, 0.0, 0.0, 1.0 };
|
||||
|
||||
if (gtk_icon_info_is_symbolic (info))
|
||||
{
|
||||
cancellable = g_cancellable_new ();
|
||||
gtk_icon_info_load_symbolic_async (info, &foreground, &success, &warning, &error, cancellable, pixbuf_loaded, self);
|
||||
}
|
||||
else
|
||||
{
|
||||
const gchar *path = gtk_icon_info_get_filename (info);
|
||||
|
||||
if (path != NULL)
|
||||
{
|
||||
NSImage *image = [[[NSImage alloc] initByReferencingFile:[NSString stringWithUTF8String:path]] autorelease];
|
||||
[image setSize:NSMakeSize(16, 16)];
|
||||
[self setImage:image];
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkPixbuf *pixbuf = gtk_icon_info_get_builtin_pixbuf (info);
|
||||
|
||||
if (G_IS_LOADABLE_ICON (pixbuf))
|
||||
[self setImageFromLoadableIcon:G_LOADABLE_ICON (pixbuf)];
|
||||
else
|
||||
{
|
||||
cancellable = g_cancellable_new ();
|
||||
gtk_icon_info_load_symbolic_async (info, &foreground, &success, &warning, &error, cancellable, pixbuf_loaded, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (info);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (G_IS_LOADABLE_ICON (icon))
|
||||
[self setImageFromLoadableIcon:G_LOADABLE_ICON (icon)];
|
||||
else
|
||||
[self setImage:nil];
|
||||
}
|
||||
|
||||
- (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 = 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
|
||||
{
|
||||
if ((self = [super initWithTitle:title]) != nil)
|
||||
{
|
||||
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
|
||||
{
|
||||
if ((self = [super initWithTitle:title]) != nil)
|
||||
{
|
||||
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];
|
||||
}
|
@@ -21,9 +21,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkapplicationprivate.h"
|
||||
#include "gtkmodelmenu-quartz.h"
|
||||
#include "gtkmessagedialog.h"
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include "gtkbuilder.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
typedef struct
|
||||
@@ -48,6 +46,9 @@ typedef struct
|
||||
{
|
||||
GtkApplicationImpl impl;
|
||||
|
||||
GtkActionMuxer *muxer;
|
||||
GMenu *combined;
|
||||
|
||||
GSList *inhibitors;
|
||||
gint quit_inhibit;
|
||||
guint next_cookie;
|
||||
@@ -55,89 +56,76 @@ typedef struct
|
||||
|
||||
G_DEFINE_TYPE (GtkApplicationImplQuartz, gtk_application_impl_quartz, GTK_TYPE_APPLICATION_IMPL)
|
||||
|
||||
/* OS X implementation copied from EggSMClient, but simplified since
|
||||
* it doesn't need to interact with the user.
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
idle_will_quit (gpointer user_data)
|
||||
@interface GtkApplicationQuartzDelegate : NSObject
|
||||
{
|
||||
GtkApplicationImplQuartz *quartz = user_data;
|
||||
|
||||
if (quartz->quit_inhibit == 0)
|
||||
g_application_quit (G_APPLICATION (quartz->impl.application));
|
||||
else
|
||||
{
|
||||
GtkApplicationQuartzInhibitor *inhibitor;
|
||||
GSList *iter;
|
||||
GtkWidget *dialog;
|
||||
|
||||
for (iter = quartz->inhibitors; iter; iter = iter->next)
|
||||
{
|
||||
inhibitor = iter->data;
|
||||
if (inhibitor->flags & GTK_APPLICATION_INHIBIT_LOGOUT)
|
||||
break;
|
||||
}
|
||||
g_assert (inhibitor != NULL);
|
||||
|
||||
dialog = gtk_message_dialog_new (inhibitor->window,
|
||||
GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_OK,
|
||||
_("%s cannot quit at this time:\n\n%s"),
|
||||
g_get_application_name (),
|
||||
inhibitor->reason);
|
||||
g_signal_connect_swapped (dialog,
|
||||
"response",
|
||||
G_CALLBACK (gtk_widget_destroy),
|
||||
dialog);
|
||||
gtk_widget_show_all (dialog);
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
GtkApplicationImplQuartz *quartz;
|
||||
}
|
||||
|
||||
static pascal OSErr
|
||||
quit_requested (const AppleEvent *aevt,
|
||||
AppleEvent *reply,
|
||||
long refcon)
|
||||
{
|
||||
GtkApplicationImplQuartz *quartz = GSIZE_TO_POINTER ((gsize)refcon);
|
||||
- (id)initWithImpl:(GtkApplicationImplQuartz*)impl;
|
||||
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender;
|
||||
@end
|
||||
|
||||
/* Don't emit the "quit" signal immediately, since we're
|
||||
* called from a weird point in the guts of gdkeventloop-quartz.c
|
||||
@implementation GtkApplicationQuartzDelegate
|
||||
-(id)initWithImpl:(GtkApplicationImplQuartz*)impl
|
||||
{
|
||||
quartz = impl;
|
||||
return self;
|
||||
}
|
||||
|
||||
-(NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
/* We have no way to give our message other than to pop up a dialog
|
||||
* ourselves, which we should not do since the OS will already show
|
||||
* one when we return NSTerminateNow.
|
||||
*
|
||||
* Just let the OS show the generic message...
|
||||
*/
|
||||
g_idle_add_full (G_PRIORITY_DEFAULT, idle_will_quit, quartz, NULL);
|
||||
|
||||
return quartz->quit_inhibit == 0 ? noErr : userCanceledErr;
|
||||
}
|
||||
|
||||
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);
|
||||
return quartz->quit_inhibit == 0 ? NSTerminateNow : NSTerminateCancel;
|
||||
}
|
||||
@end
|
||||
|
||||
static void
|
||||
gtk_application_impl_quartz_startup (GtkApplicationImpl *impl,
|
||||
gboolean register_session)
|
||||
{
|
||||
GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
|
||||
GSimpleActionGroup *gtkinternal;
|
||||
GMenuModel *app_menu;
|
||||
|
||||
if (register_session)
|
||||
AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
|
||||
NewAEEventHandlerUPP (quit_requested),
|
||||
(long)GPOINTER_TO_SIZE (quartz), false);
|
||||
[NSApp setDelegate: [[GtkApplicationQuartzDelegate alloc] initWithImpl:quartz]];
|
||||
|
||||
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));
|
||||
|
||||
/* Add the default accels */
|
||||
gtk_application_add_accelerator (impl->application, "<Primary>comma", "app.preferences", NULL);
|
||||
gtk_application_add_accelerator (impl->application, "<Primary><Alt>h", "gtkinternal.hide-others", NULL);
|
||||
gtk_application_add_accelerator (impl->application, "<Primary>h", "gtkinternal.hide", NULL);
|
||||
gtk_application_add_accelerator (impl->application, "<Primary>q", "app.quit", NULL);
|
||||
|
||||
app_menu = gtk_application_get_app_menu (impl->application);
|
||||
if (app_menu == NULL)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
/* If the user didn't fill in their own menu yet, add ours.
|
||||
*
|
||||
* The fact that we do this here ensures that we will always have the
|
||||
* app menu at index 0 in 'combined'.
|
||||
*/
|
||||
builder = gtk_builder_new_from_resource ("/org/gtk/libgtk/gtkapplication-quartz.ui");
|
||||
gtk_application_set_app_menu (impl->application, G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
|
||||
g_object_unref (builder);
|
||||
}
|
||||
else
|
||||
gtk_application_impl_set_app_menu (impl, app_menu);
|
||||
|
||||
/* This may or may not add an item to 'combined' */
|
||||
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];
|
||||
}
|
||||
@@ -147,19 +135,58 @@ 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]];
|
||||
|
||||
g_slist_free_full (quartz->inhibitors, (GDestroyNotify) gtk_application_quartz_inhibitor_free);
|
||||
quartz->inhibitors = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_application_impl_quartz_window_added (GtkApplicationImpl *impl,
|
||||
GtkWindow *window)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_application_impl_quartz_window_removed (GtkApplicationImpl *impl,
|
||||
GtkWindow *window)
|
||||
{
|
||||
}
|
||||
|
||||
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, "`gtk-private-appname`", 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, "`gtk-private-appname`", G_MENU_MODEL (empty));
|
||||
g_object_unref (empty);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -168,7 +195,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
|
||||
@@ -233,6 +265,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
|
||||
@@ -240,7 +273,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);
|
||||
}
|
||||
@@ -252,6 +285,9 @@ gtk_application_impl_quartz_class_init (GtkApplicationImplClass *class)
|
||||
|
||||
class->startup = gtk_application_impl_quartz_startup;
|
||||
class->shutdown = gtk_application_impl_quartz_shutdown;
|
||||
class->window_added = gtk_application_impl_quartz_window_added;
|
||||
class->window_removed = gtk_application_impl_quartz_window_removed;
|
||||
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;
|
||||
|
45
gtk/gtkapplication-quartz.ui
Normal file
45
gtk/gtkapplication-quartz.ui
Normal file
@@ -0,0 +1,45 @@
|
||||
<interface>
|
||||
<menu id='app-menu'>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name='label' translatable='yes'>About `gtk-private-appname`</attribute>
|
||||
<attribute name='action'>app.about</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name='label' translatable='yes'>Preferences</attribute>
|
||||
<attribute name='action'>app.preferences</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<submenu>
|
||||
<attribute name='label' translatable='yes'>Services</attribute>
|
||||
<attribute name='gtk-private-special'>services-submenu</attribute>
|
||||
</submenu>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name='label' translatable='yes'>Hide `gtk-private-appname`</attribute>
|
||||
<attribute name='gtk-private-special'>hide-this</attribute>
|
||||
<attribute name='action'>gtkinternal.hide</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name='label' translatable='yes'>Hide Others</attribute>
|
||||
<attribute name='gtk-private-special'>hide-others</attribute>
|
||||
<attribute name='action'>gtkinternal.hide-others</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name='label' translatable='yes'>Show All</attribute>
|
||||
<attribute name='gtk-private-special'>show-all</attribute>
|
||||
<attribute name='action'>gtkinternal.show-all</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name='label' translatable='yes'>Quit `gtk-private-appname`</attribute>
|
||||
<attribute name='action'>app.quit</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
</interface>
|
@@ -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__ */
|
||||
|
@@ -389,6 +389,10 @@ gtk_application_window_list_actions (GActionGroup *group)
|
||||
{
|
||||
GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
|
||||
|
||||
/* may be NULL after dispose has run */
|
||||
if (!window->priv->actions)
|
||||
return g_new0 (char *, 0 + 1);
|
||||
|
||||
return g_action_group_list_actions (G_ACTION_GROUP (window->priv->actions));
|
||||
}
|
||||
|
||||
@@ -403,6 +407,9 @@ gtk_application_window_query_action (GActionGroup *group,
|
||||
{
|
||||
GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
|
||||
|
||||
if (!window->priv->actions)
|
||||
return FALSE;
|
||||
|
||||
return g_action_group_query_action (G_ACTION_GROUP (window->priv->actions),
|
||||
action_name, enabled, parameter_type, state_type, state_hint, state);
|
||||
}
|
||||
@@ -414,7 +421,10 @@ gtk_application_window_activate_action (GActionGroup *group,
|
||||
{
|
||||
GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
|
||||
|
||||
return g_action_group_activate_action (G_ACTION_GROUP (window->priv->actions), action_name, parameter);
|
||||
if (!window->priv->actions)
|
||||
return;
|
||||
|
||||
g_action_group_activate_action (G_ACTION_GROUP (window->priv->actions), action_name, parameter);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -424,7 +434,10 @@ gtk_application_window_change_action_state (GActionGroup *group,
|
||||
{
|
||||
GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
|
||||
|
||||
return g_action_group_change_action_state (G_ACTION_GROUP (window->priv->actions), action_name, state);
|
||||
if (!window->priv->actions)
|
||||
return;
|
||||
|
||||
g_action_group_change_action_state (G_ACTION_GROUP (window->priv->actions), action_name, state);
|
||||
}
|
||||
|
||||
static GAction *
|
||||
@@ -433,6 +446,9 @@ gtk_application_window_lookup_action (GActionMap *action_map,
|
||||
{
|
||||
GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
|
||||
|
||||
if (!window->priv->actions)
|
||||
return NULL;
|
||||
|
||||
return g_action_map_lookup_action (G_ACTION_MAP (window->priv->actions), action_name);
|
||||
}
|
||||
|
||||
@@ -442,6 +458,9 @@ gtk_application_window_add_action (GActionMap *action_map,
|
||||
{
|
||||
GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
|
||||
|
||||
if (!window->priv->actions)
|
||||
return;
|
||||
|
||||
g_action_map_add_action (G_ACTION_MAP (window->priv->actions), action);
|
||||
}
|
||||
|
||||
@@ -451,6 +470,9 @@ gtk_application_window_remove_action (GActionMap *action_map,
|
||||
{
|
||||
GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
|
||||
|
||||
if (!window->priv->actions)
|
||||
return;
|
||||
|
||||
g_action_map_remove_action (G_ACTION_MAP (window->priv->actions), action_name);
|
||||
}
|
||||
|
||||
@@ -740,10 +762,59 @@ gtk_application_window_dispose (GObject *object)
|
||||
|
||||
g_clear_object (&window->priv->app_menu_section);
|
||||
g_clear_object (&window->priv->menubar_section);
|
||||
g_clear_object (&window->priv->actions);
|
||||
|
||||
G_OBJECT_CLASS (gtk_application_window_parent_class)
|
||||
->dispose (object);
|
||||
|
||||
/* We do this below the chain-up above to give us a chance to be
|
||||
* removed from the GtkApplication (which is done in the dispose
|
||||
* handler of GtkWindow).
|
||||
*
|
||||
* That reduces our chances of being watched as a GActionGroup from a
|
||||
* muxer constructed by GtkApplication. Even still, it's
|
||||
* theoretically possible that someone else could be watching us.
|
||||
* Therefore, we have to take care to ensure that we don't violate our
|
||||
* obligations under the interface of GActionGroup.
|
||||
*
|
||||
* The easiest thing is just for us to act as if all of the actions
|
||||
* suddenly disappeared.
|
||||
*/
|
||||
if (window->priv->actions)
|
||||
{
|
||||
gchar **action_names;
|
||||
guint signal;
|
||||
gint i;
|
||||
|
||||
/* Only send the remove signals if someone is listening */
|
||||
signal = g_signal_lookup ("action-removed", G_TYPE_ACTION_GROUP);
|
||||
if (signal && g_signal_has_handler_pending (window, signal, 0, TRUE))
|
||||
/* need to send a removed signal for each action */
|
||||
action_names = g_action_group_list_actions (G_ACTION_GROUP (window->priv->actions));
|
||||
else
|
||||
/* don't need to send signals: nobody is watching */
|
||||
action_names = NULL;
|
||||
|
||||
/* Free the group before sending the signals for two reasons:
|
||||
*
|
||||
* 1) we want any incoming calls to see an empty group
|
||||
*
|
||||
* 2) we don't want signal handlers that trigger in response to
|
||||
* the action-removed signals that we're firing to attempt to
|
||||
* modify the action group in a way that may cause it to fire
|
||||
* additional signals (which we would then propagate)
|
||||
*/
|
||||
g_object_unref (window->priv->actions);
|
||||
window->priv->actions = NULL;
|
||||
|
||||
/* It's safe to send the signals now, if we need to. */
|
||||
if (action_names)
|
||||
{
|
||||
for (i = 0; action_names[i]; i++)
|
||||
g_action_group_action_removed (G_ACTION_GROUP (window), action_names[i]);
|
||||
|
||||
g_strfreev (action_names);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -3544,6 +3544,34 @@ gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
|
||||
return icon_info->cache_pixbuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_icon_info_is_symbolic:
|
||||
* @icon_info: a #GtkIconInfo structure
|
||||
*
|
||||
* Checks if the icon is symbolic or not.
|
||||
*
|
||||
* Return value: %TRUE if the icon is symbolic, %FALSE otherwise.
|
||||
*
|
||||
* Since: 3.12
|
||||
**/
|
||||
gboolean
|
||||
gtk_icon_info_is_symbolic (GtkIconInfo *icon_info)
|
||||
{
|
||||
gchar *icon_uri;
|
||||
gboolean is_symbolic;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_ICON_INFO (icon_info), FALSE);
|
||||
|
||||
icon_uri = NULL;
|
||||
if (icon_info->icon_file)
|
||||
icon_uri = g_file_get_uri (icon_info->icon_file);
|
||||
|
||||
is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
|
||||
g_free (icon_uri);
|
||||
|
||||
return is_symbolic;
|
||||
}
|
||||
|
||||
static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
|
||||
|
||||
/* Combine the icon with all emblems, the first emblem is placed
|
||||
@@ -4329,18 +4357,12 @@ gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
|
||||
gboolean *was_symbolic,
|
||||
GError **error)
|
||||
{
|
||||
gchar *icon_uri;
|
||||
gboolean is_symbolic;
|
||||
|
||||
g_return_val_if_fail (icon_info != NULL, NULL);
|
||||
g_return_val_if_fail (fg != NULL, NULL);
|
||||
|
||||
icon_uri = NULL;
|
||||
if (icon_info->icon_file)
|
||||
icon_uri = g_file_get_uri (icon_info->icon_file);
|
||||
|
||||
is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
|
||||
g_free (icon_uri);
|
||||
is_symbolic = gtk_icon_info_is_symbolic (icon_info);
|
||||
|
||||
if (was_symbolic)
|
||||
*was_symbolic = is_symbolic;
|
||||
@@ -4396,18 +4418,12 @@ gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
|
||||
GdkRGBA error_color;
|
||||
GdkRGBA *error_colorp;
|
||||
GtkStateFlags state;
|
||||
gchar *icon_uri;
|
||||
gboolean is_symbolic;
|
||||
|
||||
g_return_val_if_fail (icon_info != NULL, NULL);
|
||||
g_return_val_if_fail (context != NULL, NULL);
|
||||
|
||||
icon_uri = NULL;
|
||||
if (icon_info->icon_file)
|
||||
icon_uri = g_file_get_uri (icon_info->icon_file);
|
||||
|
||||
is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
|
||||
g_free (icon_uri);
|
||||
is_symbolic = gtk_icon_info_is_symbolic (icon_info);
|
||||
|
||||
if (was_symbolic)
|
||||
*was_symbolic = is_symbolic;
|
||||
@@ -4542,7 +4558,6 @@ gtk_icon_info_load_symbolic_async (GtkIconInfo *icon_info,
|
||||
{
|
||||
GTask *task;
|
||||
AsyncSymbolicData *data;
|
||||
gchar *icon_uri;
|
||||
SymbolicPixbufCache *symbolic_cache;
|
||||
GdkPixbuf *pixbuf;
|
||||
|
||||
@@ -4554,12 +4569,7 @@ gtk_icon_info_load_symbolic_async (GtkIconInfo *icon_info,
|
||||
data = g_slice_new0 (AsyncSymbolicData);
|
||||
g_task_set_task_data (task, data, (GDestroyNotify) async_symbolic_data_free);
|
||||
|
||||
icon_uri = NULL;
|
||||
if (icon_info->icon_file)
|
||||
icon_uri = g_file_get_uri (icon_info->icon_file);
|
||||
|
||||
data->is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
|
||||
g_free (icon_uri);
|
||||
data->is_symbolic = gtk_icon_info_is_symbolic (icon_info);
|
||||
|
||||
if (!data->is_symbolic)
|
||||
{
|
||||
@@ -4814,18 +4824,12 @@ gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
|
||||
GdkRGBA *warning_colorp;
|
||||
GdkRGBA error_color;
|
||||
GdkRGBA *error_colorp;
|
||||
gchar *icon_uri;
|
||||
gboolean is_symbolic;
|
||||
|
||||
g_return_val_if_fail (icon_info != NULL, NULL);
|
||||
g_return_val_if_fail (style != NULL, NULL);
|
||||
|
||||
icon_uri = NULL;
|
||||
if (icon_info->icon_file)
|
||||
icon_uri = g_file_get_uri (icon_info->icon_file);
|
||||
|
||||
is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
|
||||
g_free (icon_uri);
|
||||
is_symbolic = gtk_icon_info_is_symbolic (icon_info);
|
||||
|
||||
if (was_symbolic)
|
||||
*was_symbolic = is_symbolic;
|
||||
|
@@ -267,6 +267,8 @@ GDK_AVAILABLE_IN_ALL
|
||||
const gchar * gtk_icon_info_get_filename (GtkIconInfo *icon_info);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkPixbuf * gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info);
|
||||
GDK_AVAILABLE_IN_3_12
|
||||
gboolean gtk_icon_info_is_symbolic (GtkIconInfo *icon_info);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkPixbuf * gtk_icon_info_load_icon (GtkIconInfo *icon_info,
|
||||
GError **error);
|
||||
|
@@ -575,6 +575,16 @@ gtk_menu_tracker_item_get_accel (GtkMenuTrackerItem *self)
|
||||
return gtk_action_muxer_get_primary_accel (GTK_ACTION_MUXER (self->observable), self->action_and_target);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gtk_menu_tracker_item_get_special (GtkMenuTrackerItem *self)
|
||||
{
|
||||
const gchar *special = NULL;;
|
||||
|
||||
g_menu_item_get_attribute (self->item, "gtk-private-special", "&s", &special);
|
||||
|
||||
return special;
|
||||
}
|
||||
|
||||
GMenuModel *
|
||||
_gtk_menu_tracker_item_get_submenu (GtkMenuTrackerItem *self)
|
||||
{
|
||||
|
@@ -48,6 +48,8 @@ GtkMenuTrackerItem * _gtk_menu_tracker_item_new (GtkActi
|
||||
const gchar *action_namespace,
|
||||
gboolean is_separator);
|
||||
|
||||
const gchar * gtk_menu_tracker_item_get_special (GtkMenuTrackerItem *self);
|
||||
|
||||
GtkActionObservable * _gtk_menu_tracker_item_get_observable (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_is_separator (GtkMenuTrackerItem *self);
|
||||
|
@@ -1,519 +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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: William Hua <william@attente.ca>
|
||||
* Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "gtkmodelmenu-quartz.h"
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include "gtkaccelmapprivate.h"
|
||||
#include "gtkactionhelper.h"
|
||||
#include "../gdk/quartz/gdkquartz.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
/*
|
||||
* Code for key code conversion
|
||||
*
|
||||
* Copyright (C) 2009 Paul Davis
|
||||
*/
|
||||
static unichar
|
||||
gtk_quartz_model_menu_get_unichar (gint key)
|
||||
{
|
||||
if (key >= GDK_KEY_A && key <= GDK_KEY_Z)
|
||||
return key + (GDK_KEY_a - GDK_KEY_A);
|
||||
|
||||
if (key >= GDK_KEY_space && key <= GDK_KEY_asciitilde)
|
||||
return key;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case GDK_KEY_BackSpace:
|
||||
return NSBackspaceCharacter;
|
||||
case GDK_KEY_Delete:
|
||||
return NSDeleteFunctionKey;
|
||||
case GDK_KEY_Pause:
|
||||
return NSPauseFunctionKey;
|
||||
case GDK_KEY_Scroll_Lock:
|
||||
return NSScrollLockFunctionKey;
|
||||
case GDK_KEY_Sys_Req:
|
||||
return NSSysReqFunctionKey;
|
||||
case GDK_KEY_Home:
|
||||
return NSHomeFunctionKey;
|
||||
case GDK_KEY_Left:
|
||||
case GDK_KEY_leftarrow:
|
||||
return NSLeftArrowFunctionKey;
|
||||
case GDK_KEY_Up:
|
||||
case GDK_KEY_uparrow:
|
||||
return NSUpArrowFunctionKey;
|
||||
case GDK_KEY_Right:
|
||||
case GDK_KEY_rightarrow:
|
||||
return NSRightArrowFunctionKey;
|
||||
case GDK_KEY_Down:
|
||||
case GDK_KEY_downarrow:
|
||||
return NSDownArrowFunctionKey;
|
||||
case GDK_KEY_Page_Up:
|
||||
return NSPageUpFunctionKey;
|
||||
case GDK_KEY_Page_Down:
|
||||
return NSPageDownFunctionKey;
|
||||
case GDK_KEY_End:
|
||||
return NSEndFunctionKey;
|
||||
case GDK_KEY_Begin:
|
||||
return NSBeginFunctionKey;
|
||||
case GDK_KEY_Select:
|
||||
return NSSelectFunctionKey;
|
||||
case GDK_KEY_Print:
|
||||
return NSPrintFunctionKey;
|
||||
case GDK_KEY_Execute:
|
||||
return NSExecuteFunctionKey;
|
||||
case GDK_KEY_Insert:
|
||||
return NSInsertFunctionKey;
|
||||
case GDK_KEY_Undo:
|
||||
return NSUndoFunctionKey;
|
||||
case GDK_KEY_Redo:
|
||||
return NSRedoFunctionKey;
|
||||
case GDK_KEY_Menu:
|
||||
return NSMenuFunctionKey;
|
||||
case GDK_KEY_Find:
|
||||
return NSFindFunctionKey;
|
||||
case GDK_KEY_Help:
|
||||
return NSHelpFunctionKey;
|
||||
case GDK_KEY_Break:
|
||||
return NSBreakFunctionKey;
|
||||
case GDK_KEY_Mode_switch:
|
||||
return NSModeSwitchFunctionKey;
|
||||
case GDK_KEY_F1:
|
||||
return NSF1FunctionKey;
|
||||
case GDK_KEY_F2:
|
||||
return NSF2FunctionKey;
|
||||
case GDK_KEY_F3:
|
||||
return NSF3FunctionKey;
|
||||
case GDK_KEY_F4:
|
||||
return NSF4FunctionKey;
|
||||
case GDK_KEY_F5:
|
||||
return NSF5FunctionKey;
|
||||
case GDK_KEY_F6:
|
||||
return NSF6FunctionKey;
|
||||
case GDK_KEY_F7:
|
||||
return NSF7FunctionKey;
|
||||
case GDK_KEY_F8:
|
||||
return NSF8FunctionKey;
|
||||
case GDK_KEY_F9:
|
||||
return NSF9FunctionKey;
|
||||
case GDK_KEY_F10:
|
||||
return NSF10FunctionKey;
|
||||
case GDK_KEY_F11:
|
||||
return NSF11FunctionKey;
|
||||
case GDK_KEY_F12:
|
||||
return NSF12FunctionKey;
|
||||
case GDK_KEY_F13:
|
||||
return NSF13FunctionKey;
|
||||
case GDK_KEY_F14:
|
||||
return NSF14FunctionKey;
|
||||
case GDK_KEY_F15:
|
||||
return NSF15FunctionKey;
|
||||
case GDK_KEY_F16:
|
||||
return NSF16FunctionKey;
|
||||
case GDK_KEY_F17:
|
||||
return NSF17FunctionKey;
|
||||
case GDK_KEY_F18:
|
||||
return NSF18FunctionKey;
|
||||
case GDK_KEY_F19:
|
||||
return NSF19FunctionKey;
|
||||
case GDK_KEY_F20:
|
||||
return NSF20FunctionKey;
|
||||
case GDK_KEY_F21:
|
||||
return NSF21FunctionKey;
|
||||
case GDK_KEY_F22:
|
||||
return NSF22FunctionKey;
|
||||
case GDK_KEY_F23:
|
||||
return NSF23FunctionKey;
|
||||
case GDK_KEY_F24:
|
||||
return NSF24FunctionKey;
|
||||
case GDK_KEY_F25:
|
||||
return NSF25FunctionKey;
|
||||
case GDK_KEY_F26:
|
||||
return NSF26FunctionKey;
|
||||
case GDK_KEY_F27:
|
||||
return NSF27FunctionKey;
|
||||
case GDK_KEY_F28:
|
||||
return NSF28FunctionKey;
|
||||
case GDK_KEY_F29:
|
||||
return NSF29FunctionKey;
|
||||
case GDK_KEY_F30:
|
||||
return NSF30FunctionKey;
|
||||
case GDK_KEY_F31:
|
||||
return NSF31FunctionKey;
|
||||
case GDK_KEY_F32:
|
||||
return NSF32FunctionKey;
|
||||
case GDK_KEY_F33:
|
||||
return NSF33FunctionKey;
|
||||
case GDK_KEY_F34:
|
||||
return NSF34FunctionKey;
|
||||
case GDK_KEY_F35:
|
||||
return NSF35FunctionKey;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return '\0';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@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 = gtk_quartz_model_menu_get_unichar (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
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: William Hua <william@attente.ca>
|
||||
*/
|
||||
|
||||
#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__ */
|
Reference in New Issue
Block a user