[gtk-mac-integration] Restore ige-mac-foo files
- From: John Ralls <jralls src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk-mac-integration] Restore ige-mac-foo files
- Date: Mon, 3 Oct 2011 21:50:01 +0000 (UTC)
commit 75ca11c68a7db76d5a5ae49dc87c68553482f375
Author: John Ralls <jralls ceridwen us>
Date: Mon Oct 3 12:41:33 2011 -0700
Restore ige-mac-foo files
This partially reverts commit da09e56a88102521a72dafb85797e068287da37b.
These files were deleted instead of being renamed
bindings/python/igemacintegration/Makefile.am | 45 +
bindings/python/igemacintegration/__init__.py | 48 +
.../igemacintegration/igemacintegration-module.c | 24 +
.../igemacintegration/igemacintegration.override | 59 +
.../python/igemacintegration/test-integration.py | 87 ++
data/ige-mac-integration.pc.in | 11 +
docs/reference/ige-mac-integration-docs.sgml | 33 +
src/ige-mac-bundle.c | 380 ++++++
src/ige-mac-bundle.h | 64 +
src/ige-mac-dock.c | 467 ++++++++
src/ige-mac-dock.h | 88 ++
src/ige-mac-image-utils.c | 62 +
src/ige-mac-image-utils.h | 32 +
src/ige-mac-integration.h | 30 +
src/ige-mac-menu.c | 1260 ++++++++++++++++++++
src/ige-mac-menu.h | 50 +
src/ige-mac-private.h | 34 +
17 files changed, 2774 insertions(+), 0 deletions(-)
---
diff --git a/bindings/python/igemacintegration/Makefile.am b/bindings/python/igemacintegration/Makefile.am
new file mode 100644
index 0000000..13649bf
--- /dev/null
+++ b/bindings/python/igemacintegration/Makefile.am
@@ -0,0 +1,45 @@
+INCLUDES = -I$(top_srcdir)/src $(PYTHON_INCLUDES)
+
+igemacintegrationdir = $(libdir)/python$(PYTHON_VERSION)/site-packages/igemacintegration
+
+igemacintegration_PYTHON = __init__.py
+igemacintegration_LTLIBRARIES = _igemacintegration.la
+
+headers = \
+ $(top_srcdir)/src/ige-mac-menu.h \
+ $(top_srcdir)/src/ige-mac-dock.h \
+ $(top_srcdir)/src/ige-mac-bundle.h
+
+igemacintegration.defs: $(headers)
+ $(PYTHON) $(datadir)/pygobject/2.0/codegen/h2def.py $(headers) > $@
+
+igemacintegration.c: igemacintegration.defs igemacintegration.override
+
+_igemacintegration_la_CFLAGS = \
+ $(MAC_CFLAGS) \
+ $(PYGTK_CFLAGS)
+_igemacintegration_la_LDFLAGS = -module -avoid-version -export-symbols-regex init_igemacintegration
+_igemacintegration_la_LIBADD = \
+ $(MAC_LIBS) \
+ $(PYGTK_LIBS) \
+ $(top_builddir)/src/libigemacintegration.la
+_igemacintegration_la_SOURCES = \
+ igemacintegration.c \
+ igemacintegration-module.c
+
+.defs.c:
+ ($(PYGOBJECT_CODEGEN) \
+ --register $(PYGTK_DEFSDIR)/gdk-types.defs \
+ --register $(PYGTK_DEFSDIR)/gtk-types.defs \
+ --override $*.override \
+ --prefix $* $<) > gen-$*.c \
+ && cp gen-$*.c $*.c \
+ && rm -f gen-$*.c
+
+CLEANFILES = \
+ igemacintegration.defs \
+ igemacintegration.c
+
+EXTRA_DIST = \
+ igemacintegration.defs \
+ igemacintegration.override
diff --git a/bindings/python/igemacintegration/__init__.py b/bindings/python/igemacintegration/__init__.py
new file mode 100644
index 0000000..abf047f
--- /dev/null
+++ b/bindings/python/igemacintegration/__init__.py
@@ -0,0 +1,48 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+
+# Copyright (C) 2007 Imendio AB
+#
+# 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; version 2.1
+# of the License.
+#
+# 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
+
+import sys
+
+if 'igemacintegration._igemacintegration' in sys.modules:
+ _igemacintegration = sys.modules['igemacintegration._igemacintegration']
+else:
+ from igemacintegration import _igemacintegration
+
+from igemacintegration._igemacintegration import *
+
+# Wrapper class for the menu
+class MacMenu:
+ def set_menu_bar(self, menubar):
+ ige_mac_menu_set_menu_bar(menubar)
+
+ def set_quit_menu_item(self, item):
+ ige_mac_menu_set_quit_menu_item(item)
+
+ def add_app_menu_group(self):
+ return MacMenuAppGroup(ige_mac_menu_add_app_menu_group())
+
+ def sync(self, menubar):
+ return ige_mac_menu_sync(menubar)
+
+class MacMenuAppGroup:
+ def __init__(self, group):
+ self._group = group
+
+ def add_app_menu_item(self, item, label=None):
+ ige_mac_menu_add_app_menu_item(self._group, item, label)
diff --git a/bindings/python/igemacintegration/igemacintegration-module.c b/bindings/python/igemacintegration/igemacintegration-module.c
new file mode 100644
index 0000000..8b41d40
--- /dev/null
+++ b/bindings/python/igemacintegration/igemacintegration-module.c
@@ -0,0 +1,24 @@
+#include <pygobject.h>
+
+void igemacintegration_register_classes (PyObject *d);
+
+extern PyMethodDef igemacintegration_functions[];
+
+DL_EXPORT(void)
+init_igemacintegration (void)
+{
+ PyObject *m, *d;
+
+ init_pygobject ();
+
+ m = Py_InitModule ("igemacintegration._igemacintegration",
+ igemacintegration_functions);
+ d = PyModule_GetDict (m);
+
+ igemacintegration_register_classes (d);
+
+ if (PyErr_Occurred ()) {
+ PyErr_Print();
+ Py_FatalError ("can't initialize module igemacintegration:");
+ }
+}
diff --git a/bindings/python/igemacintegration/igemacintegration.override b/bindings/python/igemacintegration/igemacintegration.override
new file mode 100644
index 0000000..d476517
--- /dev/null
+++ b/bindings/python/igemacintegration/igemacintegration.override
@@ -0,0 +1,59 @@
+/* -*- Mode: C; c-basic-offset: 4 -*- */
+%%
+headers
+#include <Python.h>
+#include <pygtk/pygtk.h>
+#include <ige-mac-menu.h>
+#include <ige-mac-dock.h>
+#include <ige-mac-bundle.h>
+%%
+modulename igemacintegration
+%%
+import gobject.GObject as PyGObject_Type
+import gtk.gdk.Pixbuf as PyGdkPixbuf_Type
+import gtk.MenuShell as PyGtkMenuShell_Type
+import gtk.MenuItem as PyGtkMenuItem_Type
+import gtk.Window as PyGtkWindow_Type
+%%
+ignore-glob
+ *_get_type
+%%
+override ige_mac_menu_add_app_menu_group noargs
+static PyObject*
+_wrap_ige_mac_menu_add_app_menu_group (PyGObject *self)
+{
+ IgeMacMenuGroup *group;
+ PyObject *ret;
+
+ group = ige_mac_menu_add_app_menu_group ();
+
+ ret = PyCObject_FromVoidPtr ((void *) group, NULL);
+
+ return ret;
+}
+%%
+override ige_mac_menu_add_app_menu_item kwargs
+static PyObject*
+_wrap_ige_mac_menu_add_app_menu_item (PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "group", "item", "label", NULL };
+ PyObject *pygroup;
+ PyObject *pyitem;
+ char *label = NULL;
+ IgeMacMenuGroup *group;
+ GtkMenuItem *item;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO!z:add_app_menu_item", kwlist,
+ &pygroup,
+ &PyGtkMenuItem_Type, &pyitem,
+ &label))
+ return NULL;
+
+ group = PyCObject_AsVoidPtr (pygroup);
+ item = GTK_MENU_ITEM (pygobject_get (pyitem));
+
+ ige_mac_menu_add_app_menu_item (group, item, label);
+
+ Py_INCREF (Py_None);
+ return Py_None;
+}
diff --git a/bindings/python/igemacintegration/test-integration.py b/bindings/python/igemacintegration/test-integration.py
new file mode 100755
index 0000000..0799c90
--- /dev/null
+++ b/bindings/python/igemacintegration/test-integration.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+import gtk
+from igemacintegration import *
+
+class MainWindow(gtk.Window):
+ def __init__(self):
+ gtk.Window.__init__(self)
+
+ self.set_default_size(400, 300)
+
+ vbox = gtk.VBox(False, 0)
+ self.add(vbox)
+
+ vbox.pack_start(gtk.Label("Some content here"), True, True, 0)
+
+ # Setup a menu bar with GTK+
+ menubar = gtk.MenuBar()
+
+ menu = gtk.Menu()
+ item = gtk.MenuItem("Open")
+ item.connect("activate", self.activate_cb)
+ menu.add(item)
+
+ item = gtk.MenuItem("Save")
+ item.connect("activate", self.activate_cb)
+ menu.add(item)
+
+ quit_item = gtk.MenuItem("Quit")
+ quit_item.connect("activate", lambda d: gtk.main_quit())
+ menu.add(quit_item)
+
+ item = gtk.MenuItem("File")
+ item.set_submenu(menu)
+ menubar.add(item)
+
+ menubar.show_all()
+
+ vbox.pack_start(menubar)
+ menubar.hide()
+
+ # Set up the menu bar integration
+ macmenu = MacMenu()
+ macmenu.set_menu_bar(menubar)
+
+ # Take care of the Quit item, the integration code will put it
+ # in the standard place
+ macmenu.set_quit_menu_item(quit_item)
+
+ # Add two groups with items in the application menu
+ group = macmenu.add_app_menu_group()
+ item = gtk.MenuItem("About")
+ item.connect("activate", self.activate_cb)
+ group.add_app_menu_item(item, None)
+ item = gtk.MenuItem("Check for updates...")
+ item.connect("activate", self.activate_cb)
+ group.add_app_menu_item(item, None)
+
+ group = macmenu.add_app_menu_group()
+ item = gtk.MenuItem("Preferences")
+ item.connect("activate", self.activate_cb)
+ group.add_app_menu_item(item, None)
+
+ # Set up the dock integration
+ macdock = MacDock()
+ macdock.connect('quit-activate', lambda d: gtk.main_quit())
+ macdock.connect('clicked', self.dock_clicked_cb)
+
+ # Keep the reference so it's not GC:ed.
+ self.macdock = macdock
+
+ def dock_clicked_cb(self, dock):
+ print "Dock clicked"
+
+ def activate_cb(self, widget):
+ try:
+ print widget.child.get_text()
+ except:
+ print widget
+
+if __name__ == '__main__':
+ window = MainWindow()
+ window.connect("destroy", gtk.main_quit)
+ window.show()
+
+ gtk.main()
+
diff --git a/data/ige-mac-integration.pc.in b/data/ige-mac-integration.pc.in
new file mode 100644
index 0000000..8cdc030
--- /dev/null
+++ b/data/ige-mac-integration.pc.in
@@ -0,0 +1,11 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: ige-mac-integration
+Description: Mac menu bar and dock integration for GTK+
+Requires: @GTK_MAJOR@
+Version: @VERSION@
+Libs: -L${libdir} -ligemacintegration
+Cflags: -I${includedir}/igemacintegration -DMAC_INTEGRATION
diff --git a/docs/reference/ige-mac-integration-docs.sgml b/docs/reference/ige-mac-integration-docs.sgml
new file mode 100644
index 0000000..80c4afa
--- /dev/null
+++ b/docs/reference/ige-mac-integration-docs.sgml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+ <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+]>
+<book id="index">
+ <bookinfo>
+ <title>Gtk-OSX Integration Reference Manual</title>
+ <releaseinfo>
+ for ige-mac-integration 0.9
+ The latest version of this documentation can be found on-line at
+ <ulink role="online-location" url="http://gtk-osx.sourceforge.net/ige-mac-integration/index.html">http://gtk-osx.sourceforge.net/ige-mac-integration/</ulink>.
+ </releaseinfo>
+ </bookinfo>
+
+ <chapter>
+ <title>GtkOSXApplication Reference</title>
+ <xi:include href="xml/gtkosxapplication.xml"/>
+ <xi:include href="xml/GNSMenuItem.xml"/>
+
+ </chapter>
+ <chapter id="object-tree">
+ <title>Object Hierarchy</title>
+ <xi:include href="xml/tree_index.sgml"/>
+ </chapter>
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/docs/reference/ige-mac-integration-overrides.txt b/docs/reference/ige-mac-integration-overrides.txt
new file mode 100644
index 0000000..e69de29
diff --git a/src/ige-mac-bundle.c b/src/ige-mac-bundle.c
new file mode 100644
index 0000000..eaad8cb
--- /dev/null
+++ b/src/ige-mac-bundle.c
@@ -0,0 +1,380 @@
+/* GTK+ Integration for app bundles.
+ *
+ * Copyright (C) 2007-2008 Imendio AB
+ *
+ * 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.1 of the License, 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.
+ */
+
+/* TODO: Add command line parsing and remove any
+ * -psn_... arguments?
+ */
+
+#ifndef __x86_64__
+#include <gtk/gtk.h>
+#include <Carbon/Carbon.h>
+
+#include "ige-mac-bundle.h"
+
+typedef struct IgeMacBundlePriv IgeMacBundlePriv;
+
+struct IgeMacBundlePriv {
+ CFBundleRef cf_bundle;
+ gchar *path;
+ gchar *id;
+ gchar *datadir;
+ gchar *localedir;
+ UInt32 type;
+ UInt32 creator;
+};
+
+static void mac_bundle_finalize (GObject *object);
+static gchar *cf_string_to_utf8 (CFStringRef str);
+static void mac_bundle_set_environment_value (IgeMacBundle *bundle,
+ const gchar *key,
+ const gchar *value);
+
+static IgeMacBundle *global_bundle;
+
+G_DEFINE_TYPE (IgeMacBundle, ige_mac_bundle, G_TYPE_OBJECT)
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), IGE_TYPE_MAC_BUNDLE, IgeMacBundlePriv))
+
+static void
+ige_mac_bundle_class_init (IgeMacBundleClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = mac_bundle_finalize;
+
+ g_type_class_add_private (object_class, sizeof (IgeMacBundlePriv));
+}
+
+static void
+ige_mac_bundle_init (IgeMacBundle *bundle)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+ CFURLRef cf_url;
+ CFStringRef cf_string;
+ CFDictionaryRef cf_dict;
+
+ priv->cf_bundle = CFBundleGetMainBundle ();
+ if (!priv->cf_bundle)
+ return;
+
+ CFRetain (priv->cf_bundle);
+
+ /* Bundle or binary location. */
+ cf_url = CFBundleCopyBundleURL (priv->cf_bundle);
+ cf_string = CFURLCopyFileSystemPath (cf_url, kCFURLPOSIXPathStyle);
+ priv->path = cf_string_to_utf8 (cf_string);
+ CFRelease (cf_string);
+ CFRelease (cf_url);
+
+ /* Package info. */
+ CFBundleGetPackageInfo (priv->cf_bundle, &priv->type, &priv->creator);
+
+ /* Identifier. */
+ cf_string = CFBundleGetIdentifier (priv->cf_bundle);
+ if (cf_string)
+ priv->id = cf_string_to_utf8 (cf_string);
+
+ /* Get non-localized keys. */
+ cf_dict = CFBundleGetInfoDictionary (priv->cf_bundle);
+ if (cf_dict)
+ {
+ CFDictionaryRef env_dict;
+ CFIndex n_keys, i;
+ const void **keys;
+ const void **values;
+
+ env_dict = (CFDictionaryRef) CFDictionaryGetValue (cf_dict, CFSTR ("LSEnvironment"));
+ if (env_dict)
+ {
+ n_keys = CFDictionaryGetCount (env_dict);
+
+ keys = (const void **) g_new (void *, n_keys);
+ values = (const void **) g_new (void *, n_keys);
+
+ CFDictionaryGetKeysAndValues (env_dict, keys, values);
+
+ for (i = 0; i < n_keys; i++)
+ {
+ gchar *key;
+ gchar *value;
+
+ key = cf_string_to_utf8 ((CFStringRef) keys[i]);
+ value = cf_string_to_utf8 ((CFStringRef) values[i]);
+
+ mac_bundle_set_environment_value (bundle, key, value);
+
+ g_free (key);
+ g_free (value);
+ }
+
+ g_free (keys);
+ g_free (values);
+ }
+ }
+}
+
+static void
+mac_bundle_finalize (GObject *object)
+{
+ IgeMacBundlePriv *priv;
+
+ priv = GET_PRIV (object);
+
+ g_free (priv->path);
+ g_free (priv->id);
+ g_free (priv->datadir);
+ g_free (priv->localedir);
+
+ CFRelease (priv->cf_bundle);
+
+ G_OBJECT_CLASS (ige_mac_bundle_parent_class)->finalize (object);
+}
+
+IgeMacBundle *
+ige_mac_bundle_new (void)
+{
+ return g_object_new (IGE_TYPE_MAC_BUNDLE, NULL);
+}
+
+IgeMacBundle *
+ige_mac_bundle_get_default (void)
+{
+ if (!global_bundle)
+ global_bundle = ige_mac_bundle_new ();
+
+ return global_bundle;
+}
+
+static gchar *
+cf_string_to_utf8 (CFStringRef str)
+{
+ CFIndex len;
+ gchar *ret;
+
+ len = CFStringGetMaximumSizeForEncoding (CFStringGetLength (str),
+ kCFStringEncodingUTF8) + 1;
+
+ ret = g_malloc (len);
+ ret[len] = '\0';
+
+ if (CFStringGetCString (str, ret, len, kCFStringEncodingUTF8))
+ return ret;
+
+ g_free (ret);
+ return NULL;
+}
+
+static void
+mac_bundle_set_environment_value (IgeMacBundle *bundle,
+ const gchar *key,
+ const gchar *value)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+ GRegex *regex;
+ gchar *new_value;
+
+ regex = g_regex_new ("@executable_path", 0, 0, NULL);
+
+ new_value = g_regex_replace_literal (regex,
+ value,
+ -1,
+ 0,
+ priv->path,
+ 0, NULL);
+
+ g_print ("%s => %s\n", value, new_value);
+
+ if (new_value)
+ value = new_value;
+
+
+ g_setenv (key, value, TRUE);
+
+ g_free (new_value);
+ g_regex_unref (regex);
+}
+
+const gchar *
+ige_mac_bundle_get_id (IgeMacBundle *bundle)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+
+ return priv->id;
+}
+
+const gchar *
+ige_mac_bundle_get_path (IgeMacBundle *bundle)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+
+ return priv->path;
+}
+
+gboolean
+ige_mac_bundle_get_is_app_bundle (IgeMacBundle *bundle)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+
+ return (priv->type == 'APPL' && priv->id);
+}
+
+const gchar *
+ige_mac_bundle_get_datadir (IgeMacBundle *bundle)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+
+ if (!ige_mac_bundle_get_is_app_bundle (bundle))
+ return NULL;
+
+ if (!priv->datadir)
+ {
+ priv->datadir = g_build_filename (priv->path,
+ "Contents",
+ "Resources",
+ "share",
+ NULL);
+ }
+
+ return priv->datadir;
+}
+
+const gchar *
+ige_mac_bundle_get_localedir (IgeMacBundle *bundle)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+
+ if (!ige_mac_bundle_get_is_app_bundle (bundle))
+ return NULL;
+
+ if (!priv->localedir)
+ {
+ priv->localedir = g_build_filename (priv->path,
+ "Contents",
+ "Resources",
+ "share",
+ "locale",
+ NULL);
+ }
+
+ return priv->localedir;
+}
+
+void
+ige_mac_bundle_setup_environment (IgeMacBundle *bundle)
+{
+ IgeMacBundlePriv *priv = GET_PRIV (bundle);
+ gchar *resources;
+ gchar *share, *lib, *etc;
+ gchar *etc_xdg, *etc_immodules, *etc_gtkrc;
+ gchar *etc_pixbuf, *etc_pangorc;
+ const gchar *rc_files;
+
+ if (!ige_mac_bundle_get_is_app_bundle (bundle))
+ return;
+
+ resources = g_build_filename (priv->path,
+ "Contents",
+ "Resources",
+ NULL);
+
+ share = g_build_filename (resources, "share", NULL);
+ lib = g_build_filename (resources, "lib", NULL);
+ etc = g_build_filename (resources, "etc", NULL);
+ etc_xdg = g_build_filename (etc, "xdg", NULL);
+ etc_immodules = g_build_filename (etc, "gtk-2.0", "gtk.immodules", NULL);
+ etc_gtkrc = g_build_filename (etc, "gtk-2.0", "gtkrc", NULL);
+ etc_pixbuf = g_build_filename (etc, "gtk-2.0", "gdk-pixbuf.loaders", NULL);
+ etc_pangorc = g_build_filename (etc, "pango", "pangorc", NULL);
+
+ g_setenv ("XDG_CONFIG_DIRS", etc_xdg, TRUE);
+ g_setenv ("XDG_DATA_DIRS", share, TRUE);
+ g_setenv ("GTK_DATA_PREFIX", share, TRUE);
+ g_setenv ("GTK_EXE_PREFIX", resources, TRUE);
+ g_setenv ("GTK_PATH_PREFIX", resources, TRUE);
+
+ /* Append the normal gtkrc path to allow customizing the theme from
+ * Info.plist.
+ */
+ rc_files = g_getenv ("GTK2_RC_FILES");
+ if (rc_files)
+ {
+ gchar *tmp;
+
+ tmp = g_strdup_printf ("%s:%s", rc_files, etc_gtkrc);
+ g_setenv ("GTK2_RC_FILES", tmp, TRUE);
+ g_free (tmp);
+ }
+ else
+ g_setenv ("GTK2_RC_FILES", etc_gtkrc, TRUE);
+
+ g_setenv ("GTK_IM_MODULE_FILE", etc_immodules, TRUE);
+ g_setenv ("GDK_PIXBUF_MODULE_FILE", etc_pixbuf, TRUE);
+ g_setenv ("PANGO_RC_FILE", etc_pangorc, TRUE);
+ g_setenv ("CHARSETALIASDIR", lib, TRUE);
+
+ // could add FONTCONFIG_FILE
+
+ /*export LANG="\`grep \"\\\`defaults read .GlobalPreferences AppleCollationOrder \
+ 2>&1\\\`_\" /usr/share/locale/locale.alias | tail -n1 | sed 's/\./ /' | \
+ awk '{print \$2}'\`.UTF-8"*/
+
+ g_free (share);
+ g_free (lib);
+ g_free (etc);
+ g_free (etc_xdg);
+ g_free (etc_immodules);
+ g_free (etc_gtkrc);
+ g_free (etc_pixbuf);
+ g_free (etc_pangorc);
+}
+
+gchar *
+ige_mac_bundle_get_resource_path (IgeMacBundle *bundle,
+ const gchar *name,
+ const gchar *type,
+ const gchar *subdir)
+{
+ IgeMacBundlePriv *priv;
+ CFURLRef cf_url;
+ CFStringRef cf_string;
+ gchar *path;
+
+ if (!bundle)
+ bundle = ige_mac_bundle_get_default ();
+
+ priv = GET_PRIV (bundle);
+
+ if (!priv->cf_bundle)
+ return NULL;
+
+ // FIXME: Look at using CFURLGetFileSystemRepresentation (urlcf_, true, (UInt8*)outPathName, 256)
+
+ // FIXME: crate real cfstring here...
+ cf_url = CFBundleCopyResourceURL (priv->cf_bundle,
+ CFSTR("name"), CFSTR("type"), CFSTR("subdir"));
+ cf_string = CFURLCopyFileSystemPath (cf_url, kCFURLPOSIXPathStyle);
+ path = cf_string_to_utf8 (cf_string);
+ CFRelease (cf_string);
+ CFRelease (cf_url);
+
+ return path;
+}
+
+#endif //__x86_64__
diff --git a/src/ige-mac-bundle.h b/src/ige-mac-bundle.h
new file mode 100644
index 0000000..88373d5
--- /dev/null
+++ b/src/ige-mac-bundle.h
@@ -0,0 +1,64 @@
+/* GTK+ Integration for app bundles.
+ *
+ * Copyright (C) 2007-2008 Imendio AB
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __IGE_MAC_BUNDLE_H__
+#define __IGE_MAC_BUNDLE_H__
+
+#ifndef __x86_64__
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IGE_TYPE_MAC_BUNDLE (ige_mac_bundle_get_type ())
+#define IGE_MAC_BUNDLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IGE_TYPE_MAC_BUNDLE, IgeMacBundle))
+#define IGE_MAC_BUNDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IGE_TYPE_MAC_BUNDLE, IgeMacBundleClass))
+#define IGE_IS_MAC_BUNDLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IGE_TYPE_MAC_BUNDLE))
+#define IGE_IS_MAC_BUNDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IGE_TYPE_MAC_BUNDLE))
+#define IGE_MAC_BUNDLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IGE_TYPE_MAC_BUNDLE, IgeMacBundleClass))
+
+typedef struct _IgeMacBundle IgeMacBundle;
+typedef struct _IgeMacBundleClass IgeMacBundleClass;
+
+struct _IgeMacBundle {
+ GObject parent_instance;
+};
+
+struct _IgeMacBundleClass {
+ GObjectClass parent_class;
+};
+
+GType ige_mac_bundle_get_type (void);
+IgeMacBundle *ige_mac_bundle_new (void);
+IgeMacBundle *ige_mac_bundle_get_default (void);
+void ige_mac_bundle_setup_environment (IgeMacBundle *bundle);
+const gchar * ige_mac_bundle_get_id (IgeMacBundle *bundle);
+const gchar * ige_mac_bundle_get_path (IgeMacBundle *bundle);
+gboolean ige_mac_bundle_get_is_app_bundle (IgeMacBundle *bundle);
+const gchar * ige_mac_bundle_get_localedir (IgeMacBundle *bundle);
+const gchar * ige_mac_bundle_get_datadir (IgeMacBundle *bundle);
+gchar * ige_mac_bundle_get_resource_path (IgeMacBundle *bundle,
+ const gchar *name,
+ const gchar *type,
+ const gchar *subdir);
+
+G_END_DECLS
+
+#endif /* __x86_64__*/
+#endif /* __IGE_MAC_BUNDLE_H__ */
diff --git a/src/ige-mac-dock.c b/src/ige-mac-dock.c
new file mode 100644
index 0000000..89affe8
--- /dev/null
+++ b/src/ige-mac-dock.c
@@ -0,0 +1,467 @@
+/* GTK+ Integration for the Mac OS X Dock.
+ *
+ * Copyright (C) 2007, 2008 Imendio AB
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __x86_64__
+/* FIXME: Add example like this to docs for the open documents stuff:
+
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>txt</string>
+ </array>
+ </dict>
+ </array>
+
+*/
+
+#include <config.h>
+#include <Carbon/Carbon.h>
+#include <sys/param.h>
+#include <gtk/gtk.h>
+
+#include "ige-mac-dock.h"
+#include "ige-mac-bundle.h"
+#include "ige-mac-image-utils.h"
+#include "ige-mac-private.h"
+
+enum {
+ CLICKED,
+ QUIT_ACTIVATE,
+ OPEN_DOCUMENTS,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct IgeMacDockPriv IgeMacDockPriv;
+
+struct IgeMacDockPriv {
+ glong id;
+};
+
+static void mac_dock_finalize (GObject *object);
+static OSErr mac_dock_handle_quit (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefcon);
+static OSErr mac_dock_handle_open_documents (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefcon);
+static OSErr mac_dock_handle_open_application (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefcon);
+static OSErr mac_dock_handle_reopen_application (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefcon);
+
+G_DEFINE_TYPE (IgeMacDock, ige_mac_dock, G_TYPE_OBJECT)
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), IGE_TYPE_MAC_DOCK, IgeMacDockPriv))
+
+static GList *handlers;
+static IgeMacDock *global_dock;
+
+static void
+ige_mac_dock_class_init (IgeMacDockClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = mac_dock_finalize;
+
+ signals[CLICKED] =
+ g_signal_new ("clicked",
+ IGE_TYPE_MAC_DOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /* FIXME: Need marshaller. */
+ signals[OPEN_DOCUMENTS] =
+ g_signal_new ("open-documents",
+ IGE_TYPE_MAC_DOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[QUIT_ACTIVATE] =
+ g_signal_new ("quit-activate",
+ IGE_TYPE_MAC_DOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (object_class, sizeof (IgeMacDockPriv));
+
+ /* FIXME: Just testing with triggering Carbon to take control over
+ * the dock menu events instead of Cocoa (which happens when the
+ * sharedApplication is created) to get custom dock menu working
+ * with carbon menu code. However, doing this makes the dock icon
+ * not get a "running triangle".
+ */
+#if 0
+ EventTypeSpec kFakeEventList[] = { { INT_MAX, INT_MAX } };
+ EventRef event;
+
+ ReceiveNextEvent (GetEventTypeCount (kFakeEventList),
+ kFakeEventList,
+ kEventDurationNoWait, false,
+ &event);
+#endif
+}
+
+static void
+ige_mac_dock_init (IgeMacDock *dock)
+{
+ IgeMacDockPriv *priv = GET_PRIV (dock);
+ static glong id;
+
+ priv->id = ++id;
+
+ handlers = g_list_prepend (handlers, dock);
+
+ AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
+ mac_dock_handle_quit,
+ priv->id, true);
+ AEInstallEventHandler (kCoreEventClass, kAEOpenApplication,
+ mac_dock_handle_open_application,
+ priv->id, true);
+ AEInstallEventHandler (kCoreEventClass, kAEReopenApplication,
+ mac_dock_handle_reopen_application,
+ priv->id, true);
+ AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments,
+ mac_dock_handle_open_documents,
+ priv->id, true);
+}
+
+static void
+mac_dock_finalize (GObject *object)
+{
+ IgeMacDockPriv *priv;
+
+ priv = GET_PRIV (object);
+
+ AERemoveEventHandler (kCoreEventClass, kAEQuitApplication,
+ mac_dock_handle_quit, false);
+ AERemoveEventHandler (kCoreEventClass, kAEReopenApplication,
+ mac_dock_handle_reopen_application, false);
+ AERemoveEventHandler (kCoreEventClass, kAEOpenApplication,
+ mac_dock_handle_open_application, false);
+ AERemoveEventHandler (kCoreEventClass, kAEOpenDocuments,
+ mac_dock_handle_open_documents, false);
+
+ handlers = g_list_remove (handlers, object);
+
+ G_OBJECT_CLASS (ige_mac_dock_parent_class)->finalize (object);
+}
+
+IgeMacDock *
+ige_mac_dock_new (void)
+{
+ return g_object_new (IGE_TYPE_MAC_DOCK, NULL);
+}
+
+IgeMacDock *
+ige_mac_dock_get_default (void)
+{
+ if (!global_dock)
+ global_dock = g_object_new (IGE_TYPE_MAC_DOCK, NULL);
+
+ return global_dock;
+}
+
+/* For internal use only. Returns TRUE if there is a handled setup for the
+ * Quit dock menu item (i.e. if there is a dock instance alive).
+ */
+gboolean
+_ige_mac_dock_is_quit_menu_item_handled (void)
+{
+ return handlers != NULL;
+}
+
+static IgeMacDock *
+mac_dock_get_from_id (gulong id)
+{
+ GList *l;
+ IgeMacDock *dock = NULL;
+
+ for (l = handlers; l; l = l->next)
+ {
+ dock = l->data;
+ if (GET_PRIV (dock)->id == id)
+ break;
+
+ dock = NULL;
+ }
+
+ return dock;
+}
+
+static OSErr
+mac_dock_handle_quit (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefcon)
+{
+ IgeMacDock *dock;
+
+ dock = mac_dock_get_from_id (inHandlerRefcon);
+
+ if (dock)
+ g_signal_emit (dock, signals[QUIT_ACTIVATE], 0);
+
+ return noErr;
+}
+
+static OSErr
+mac_dock_handle_open_application (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefCon)
+{
+ /*g_print ("FIXME: mac_dock_handle_open_application\n");*/
+
+ return noErr;
+}
+
+static OSErr
+mac_dock_handle_reopen_application (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefcon)
+{
+ IgeMacDock *dock;
+
+ dock = mac_dock_get_from_id (inHandlerRefcon);
+
+ if (dock)
+ g_signal_emit (dock, signals[CLICKED], 0);
+
+ return noErr;
+}
+
+static OSErr
+mac_dock_handle_open_documents (const AppleEvent *inAppleEvent,
+ AppleEvent *outAppleEvent,
+ long inHandlerRefCon)
+{
+ IgeMacDock *dock;
+ OSStatus status;
+ AEDescList documents;
+ gchar path[MAXPATHLEN];
+
+ /*g_print ("FIXME: mac_dock_handle_open_documents\n");*/
+
+ dock = mac_dock_get_from_id (inHandlerRefCon);
+
+ status = AEGetParamDesc (inAppleEvent,
+ keyDirectObject, typeAEList,
+ &documents);
+ if (status == noErr)
+ {
+ long count = 0;
+ int i;
+
+ AECountItems (&documents, &count);
+
+ for (i = 0; i < count; i++)
+ {
+ FSRef ref;
+
+ status = AEGetNthPtr (&documents, i + 1, typeFSRef,
+ 0, 0, &ref, sizeof (ref),
+ 0);
+ if (status != noErr)
+ continue;
+
+ FSRefMakePath (&ref, (char *) path, MAXPATHLEN);
+
+ /* FIXME: Add to a list, then emit the open-documents
+ * signal.
+ */
+ /*g_print (" %s\n", path);*/
+ }
+ }
+
+ return status;
+}
+
+void
+ige_mac_dock_set_icon_from_pixbuf (IgeMacDock *dock,
+ GdkPixbuf *pixbuf)
+{
+ if (!pixbuf)
+ RestoreApplicationDockTileImage ();
+ else
+ {
+ CGImageRef image;
+
+ image = ige_mac_image_from_pixbuf (pixbuf);
+ SetApplicationDockTileImage (image);
+ CGImageRelease (image);
+ }
+}
+
+void
+ige_mac_dock_set_icon_from_resource (IgeMacDock *dock,
+ IgeMacBundle *bundle,
+ const gchar *name,
+ const gchar *type,
+ const gchar *subdir)
+{
+ gchar *path;
+
+ g_return_if_fail (IGE_IS_MAC_DOCK (dock));
+ g_return_if_fail (name != NULL);
+
+ path = ige_mac_bundle_get_resource_path (bundle, name, type, subdir);
+ if (path)
+ {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ if (pixbuf)
+ {
+ ige_mac_dock_set_icon_from_pixbuf (dock, pixbuf);
+ g_object_unref (pixbuf);
+ }
+
+ g_free (path);
+ }
+}
+
+void
+ige_mac_dock_set_overlay_from_pixbuf (IgeMacDock *dock,
+ GdkPixbuf *pixbuf)
+{
+ CGImageRef image;
+
+ g_return_if_fail (IGE_IS_MAC_DOCK (dock));
+ g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
+
+ if (pixbuf)
+ {
+ image = ige_mac_image_from_pixbuf (pixbuf);
+ OverlayApplicationDockTileImage (image);
+ CGImageRelease (image);
+ }
+ else
+ RestoreApplicationDockTileImage ();
+}
+
+void
+ige_mac_dock_set_overlay_from_resource (IgeMacDock *dock,
+ IgeMacBundle *bundle,
+ const gchar *name,
+ const gchar *type,
+ const gchar *subdir)
+{
+ gchar *path;
+
+ g_return_if_fail (IGE_IS_MAC_DOCK (dock));
+ g_return_if_fail (name != NULL);
+
+ path = ige_mac_bundle_get_resource_path (bundle, name, type, subdir);
+ if (path)
+ {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ if (pixbuf)
+ {
+ ige_mac_dock_set_overlay_from_pixbuf (dock, pixbuf);
+ g_object_unref (pixbuf);
+ }
+
+ g_free (path);
+ }
+}
+
+struct _IgeMacAttentionRequest {
+ NMRec nm_request;
+ guint timeout_id;
+ gboolean is_cancelled;
+};
+
+static gboolean
+mac_dock_attention_cb (IgeMacAttentionRequest *request)
+{
+ request->timeout_id = 0;
+ request->is_cancelled = TRUE;
+
+ NMRemove (&request->nm_request);
+
+ return FALSE;
+}
+
+
+/* FIXME: Add listener for "application activated" and cancel any
+ * requests.
+ */
+IgeMacAttentionRequest *
+ige_mac_dock_attention_request (IgeMacDock *dock,
+ IgeMacAttentionType type)
+{
+ IgeMacAttentionRequest *request;
+
+ request = g_new0 (IgeMacAttentionRequest, 1);
+
+ request->nm_request.nmMark = 1;
+ request->nm_request.qType = nmType;
+
+ if (NMInstall (&request->nm_request) != noErr)
+ {
+ g_free (request);
+ return NULL;
+ }
+
+ if (type == IGE_MAC_ATTENTION_INFO)
+ request->timeout_id = gdk_threads_add_timeout (
+ 1000,
+ (GSourceFunc) mac_dock_attention_cb,
+ request);
+
+ return request;
+}
+
+void
+ige_mac_dock_attention_cancel (IgeMacDock *dock,
+ IgeMacAttentionRequest *request)
+{
+ if (request->timeout_id)
+ g_source_remove (request->timeout_id);
+
+ if (!request->is_cancelled)
+ NMRemove (&request->nm_request);
+
+ g_free (request);
+}
+
+GType
+ige_mac_attention_type_get_type (void)
+{
+ /* FIXME */
+ return 0;
+}
+
+#endif // __x86_64__
diff --git a/src/ige-mac-dock.h b/src/ige-mac-dock.h
new file mode 100644
index 0000000..5a630e8
--- /dev/null
+++ b/src/ige-mac-dock.h
@@ -0,0 +1,88 @@
+/* GTK+ Integration for the Mac OS X Dock.
+ *
+ * Copyright (C) 2007, 2008 Imendio AB
+ *
+ * For further information, see:
+ * http://sourceforge.net/apps/trac/gtk-osx/wiki/Integrate
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __IGE_MAC_DOCK_H__
+#define __IGE_MAC_DOCK_H__
+#ifndef __x86_64__
+
+#include <gtk/gtk.h>
+#include <ige-mac-bundle.h>
+
+G_BEGIN_DECLS
+
+#define IGE_TYPE_MAC_DOCK (ige_mac_dock_get_type ())
+#define IGE_MAC_DOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IGE_TYPE_MAC_DOCK, IgeMacDock))
+#define IGE_MAC_DOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IGE_TYPE_MAC_DOCK, IgeMacDockClass))
+#define IGE_IS_MAC_DOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IGE_TYPE_MAC_DOCK))
+#define IGE_IS_MAC_DOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IGE_TYPE_MAC_DOCK))
+#define IGE_MAC_DOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IGE_TYPE_MAC_DOCK, IgeMacDockClass))
+
+typedef struct _IgeMacDock IgeMacDock;
+typedef struct _IgeMacDockClass IgeMacDockClass;
+
+typedef struct _IgeMacAttentionRequest IgeMacAttentionRequest;
+
+struct _IgeMacDock
+{
+ GObject parent_instance;
+};
+
+struct _IgeMacDockClass
+{
+ GObjectClass parent_class;
+};
+
+typedef enum {
+ IGE_MAC_ATTENTION_CRITICAL,
+ IGE_MAC_ATTENTION_INFO
+} IgeMacAttentionType;
+
+GType ige_mac_dock_get_type (void);
+IgeMacDock * ige_mac_dock_new (void);
+IgeMacDock * ige_mac_dock_get_default (void);
+void ige_mac_dock_set_icon_from_pixbuf (IgeMacDock *dock,
+ GdkPixbuf *pixbuf);
+void ige_mac_dock_set_icon_from_resource (IgeMacDock *dock,
+ IgeMacBundle *bundle,
+ const gchar *name,
+ const gchar *type,
+ const gchar *subdir);
+void ige_mac_dock_set_overlay_from_pixbuf (IgeMacDock *dock,
+ GdkPixbuf *pixbuf);
+void ige_mac_dock_set_overlay_from_resource (IgeMacDock *dock,
+ IgeMacBundle *bundle,
+ const gchar *name,
+ const gchar *type,
+ const gchar *subdir);
+IgeMacAttentionRequest *ige_mac_dock_attention_request (IgeMacDock *dock,
+ IgeMacAttentionType type);
+void ige_mac_dock_attention_cancel (IgeMacDock *dock,
+ IgeMacAttentionRequest *request);
+
+#define IGE_TYPE_MAC_ATTENTION_TYPE (ige_mac_attention_type_get_type())
+GType ige_mac_attention_type_get_type (void);
+
+G_END_DECLS
+
+#endif /* __x86_64__ */
+#endif /* __IGE_MAC_DOCK_H__ */
diff --git a/src/ige-mac-image-utils.c b/src/ige-mac-image-utils.c
new file mode 100644
index 0000000..ad59359
--- /dev/null
+++ b/src/ige-mac-image-utils.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 Imendio AB
+ *
+ * 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.1 of the License, 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.
+ */
+
+/* This interface is checked to be 64-bit safe */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <Carbon/Carbon.h>
+
+#include "ige-mac-image-utils.h"
+
+CGImageRef
+ige_mac_image_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ CGColorSpaceRef colorspace;
+ CGDataProviderRef data_provider;
+ CGImageRef image;
+ void *data;
+ gint rowstride;
+ gint pixbuf_width, pixbuf_height;
+ gboolean has_alpha;
+
+ pixbuf_width = gdk_pixbuf_get_width (pixbuf);
+ pixbuf_height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+
+ data = gdk_pixbuf_get_pixels (pixbuf);
+
+ colorspace = CGColorSpaceCreateDeviceRGB ();
+ data_provider = CGDataProviderCreateWithData (NULL, data,
+ pixbuf_height * rowstride,
+ NULL);
+
+ image = CGImageCreate (pixbuf_width, pixbuf_height, 8,
+ has_alpha ? 32 : 24, rowstride,
+ colorspace,
+ has_alpha ? kCGImageAlphaLast : 0,
+ data_provider, NULL, FALSE,
+ kCGRenderingIntentDefault);
+
+ CGDataProviderRelease (data_provider);
+ CGColorSpaceRelease (colorspace);
+
+ return image;
+}
diff --git a/src/ige-mac-image-utils.h b/src/ige-mac-image-utils.h
new file mode 100644
index 0000000..5f9bca8
--- /dev/null
+++ b/src/ige-mac-image-utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 Imendio AB
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __IGE_MAC_IMAGE_UTILS_H__
+#define __IGE_MAC_IMAGE_UTILS_H__
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <Carbon/Carbon.h>
+
+G_BEGIN_DECLS
+
+CGImageRef ige_mac_image_from_pixbuf (GdkPixbuf *pixbuf);
+
+G_END_DECLS
+
+#endif /* __IGE_MAC_IMAGE_UTILS_H__ */
diff --git a/src/ige-mac-integration.h b/src/ige-mac-integration.h
new file mode 100644
index 0000000..44e10dc
--- /dev/null
+++ b/src/ige-mac-integration.h
@@ -0,0 +1,30 @@
+/* GTK+ Integration for Mac OS X.
+ *
+ * Copyright (C) 2007, 2008 Imendio AB
+ *
+ * For further information, see:
+ * http://sourceforge.net/apps/trac/gtk-osx/wiki/Integrate
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __IGE_MAC_INTEGRATION_H__
+#define __IGE_MAC_INTEGRATION_H__
+
+#include <ige-mac-menu.h>
+#include <ige-mac-dock.h>
+
+#endif /* __IGE_MAC_INTEGRATION_H__ */
diff --git a/src/ige-mac-menu.c b/src/ige-mac-menu.c
new file mode 100644
index 0000000..e0d003c
--- /dev/null
+++ b/src/ige-mac-menu.c
@@ -0,0 +1,1260 @@
+/* GTK+ Integration for the Mac OS X Menubar.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007, 2008 Imendio AB
+ * Copyright  2009, 2010 John Ralls
+ *
+ * For further information, see:
+ * http://sourceforge.net/apps/trac/gtk-osx/wiki/Integrate
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __x86_64__
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkquartz.h>
+#include <Carbon/Carbon.h>
+#import <Cocoa/Cocoa.h>
+
+#include "ige-mac-menu.h"
+#include "ige-mac-private.h"
+
+/* TODO
+ *
+ * - Adding a standard Window menu (Minimize etc)?
+ * - Sync reordering items? Does that work now?
+ * - Create on demand? (can this be done with gtk+? ie fill in menu
+ items when the menu is opened)
+ *
+ * - Deleting a menu item that is not the last one in a menu doesn't work
+ */
+
+#define IGE_QUARTZ_MENU_CREATOR 'IGEC'
+#define IGE_QUARTZ_ITEM_WIDGET 'IWID'
+
+#define IGE_MAC_KEY_HANDLER "ige-mac-key-handler"
+
+#define DEBUG FALSE
+#define DEBUG_SET FALSE
+#define DEBUG_SYNC FALSE
+#define DEBUG_SIGNAL FALSE
+#define DEBUG_CARBON FALSE
+
+static MenuID last_menu_id;
+static gboolean global_key_handler_enabled = TRUE;
+
+static void sync_menu_shell (GtkMenuShell *menu_shell, MenuRef carbon_menu,
+ gboolean toplevel, gboolean debug);
+
+/* A category that exposes the protected carbon event for an NSEvent. */
+ interface NSEvent (GdkQuartzNSEvent)
+- (void *)gdk_quartz_event_ref;
+ end
+
+ implementation NSEvent (GdkQuartzNSEvent)
+- (void *)gdk_quartz_event_ref {
+ return _eventRef;
+}
+ end
+
+static gboolean
+menu_flash_off_cb (gpointer data) {
+ /* Disable flash by flashing a non-existing menu id. */
+ FlashMenuBar (last_menu_id + 1);
+ return FALSE;
+}
+
+/*
+ * utility functions
+ */
+
+static GtkWidget *
+find_menu_label (GtkWidget *widget) {
+ GtkWidget *label = NULL;
+ if (GTK_IS_LABEL (widget))
+ return widget;
+ if (GTK_IS_CONTAINER (widget)) {
+ GList *children;
+ GList *l;
+ children = gtk_container_get_children (GTK_CONTAINER (widget));
+ for (l = children; l; l = l->next) {
+ label = find_menu_label (l->data);
+ if (label)
+ break;
+ }
+ g_list_free (children);
+ }
+ return label;
+}
+
+static const gchar *
+get_menu_label_text (GtkWidget *menu_item, GtkWidget **label) {
+ GtkWidget *my_label;
+ my_label = find_menu_label (menu_item);
+ if (label)
+ *label = my_label;
+ if (my_label)
+ return gtk_label_get_text (GTK_LABEL (my_label));
+ return NULL;
+}
+
+static gboolean
+accel_find_func (GtkAccelKey *key, GClosure *closure, gpointer data) {
+ return (GClosure *) data == closure;
+}
+
+static GClosure *
+_gtk_accel_label_get_closure (GtkAccelLabel *label) {
+ g_return_val_if_fail(GTK_IS_ACCEL_LABEL(label), NULL);
+
+ GClosure *closure = NULL;
+ g_object_get(G_OBJECT(label), "accel-closure", &closure, NULL);
+ return closure;
+}
+
+/*
+ * CarbonMenu functions
+ *
+ * A CarbonMenu contains a reference to the OSX menu; connect attaches
+ * it to the GtkMenu so that sync_menu will know which OSX menu to
+ * synchronise as it recurses through the GtkMenu tree. There is no
+ * back-pointer from the OSX Menu to the GtkMenu. Note that Gtk
+ * doesn't have an "App" menu (the one named after the application),
+ * so no MenuRef points to it.
+ */
+
+typedef struct {
+ MenuRef menu;
+ guint toplevel : 1;
+} CarbonMenu;
+
+static GQuark carbon_menu_quark = 0;
+
+static CarbonMenu *
+carbon_menu_new (void) {
+ return g_slice_new0 (CarbonMenu);
+}
+
+static void
+carbon_menu_free (CarbonMenu *menu) {
+ DisposeMenu(menu->menu);
+ g_slice_free (CarbonMenu, menu);
+}
+
+static CarbonMenu *
+carbon_menu_get (GtkWidget *widget) {
+ return g_object_get_qdata (G_OBJECT (widget), carbon_menu_quark);
+}
+
+static void
+carbon_menu_connect (GtkWidget *menu, MenuRef menuRef, gboolean toplevel) {
+ CarbonMenu *carbon_menu = carbon_menu_get (menu);
+ if (!carbon_menu) {
+ carbon_menu = carbon_menu_new ();
+ g_object_set_qdata_full (G_OBJECT (menu), carbon_menu_quark,
+ carbon_menu,
+ (GDestroyNotify) carbon_menu_free);
+ }
+ carbon_menu->menu = menuRef;
+ carbon_menu->toplevel = toplevel;
+}
+
+
+/*
+ * CarbonMenuItem functions
+ *
+ * Like CarbonMenu, the CarbonMenuItem contains a reference to the OSX
+ * Menu which contains it and the index the menu item in that menu
+ * (there aren't pointers directly to menu items in Carbon like there
+ * are in Cocoa). If the item has a submenu, there's a reference for
+ * that object as well, and there's a pointer to the accelerator for
+ * connecting signals from. This structure is inserted into the
+ * GtkMenuItem, and pointer to the GtkMenuItem is attached to the OSX
+ * Menu at the indicated index. Much effort goes into ensuring that
+ * the indices stay synchronized, as interesting behavior will result
+ * if they get out of sync.
+ */
+
+typedef struct {
+ MenuRef menu;
+ MenuItemIndex index;
+ MenuRef submenu;
+ GClosure *accel_closure;
+} CarbonMenuItem;
+
+static GQuark carbon_menu_item_quark = 0;
+
+static CarbonMenuItem *
+carbon_menu_item_new (void) {
+ return g_slice_new0 (CarbonMenuItem);
+}
+
+static void
+carbon_menu_item_free (CarbonMenuItem *menu_item) {
+ DeleteMenuItem(menu_item->menu, menu_item->index); //Clean up the Carbon Menu
+ if (menu_item->accel_closure)
+ g_closure_unref (menu_item->accel_closure);
+ g_slice_free (CarbonMenuItem, menu_item);
+}
+
+static const gchar *
+carbon_menu_error_string(OSStatus err) {
+ switch (err) {
+ case 0:
+ return "No Error";
+ case -50:
+ return "User Parameter List";
+ case -5603:
+ return "Menu Property Reserved Creator Type";
+ case -5604:
+ return "Menu Property Not Found";
+ case -5620:
+ return "Menu Not Found";
+ case -5621:
+ return "Menu uses system definition";
+ case -5622:
+ return "Menu Item Not Found";
+ case -5623:
+ return "Menu Reference Invalid";
+ case -9860:
+ return "Event Already Posted";
+ case -9861:
+ return "Event Target Busy";
+ case -9862:
+ return "Invalid Event Class";
+ case -9864:
+ return "Incorrect Event Class";
+ case -9866:
+ return "Event Handler Already Installed";
+ case -9868:
+ return "Internal Event Error";
+ case -9869:
+ return "Incorrect Event Kind";
+ case -9870:
+ return "Event Parameter Not Found";
+ case -9874:
+ return "Event Not Handled";
+ case -9875:
+ return "Event Loop Timeout";
+ case -9876:
+ return "Event Loop Quit";
+ case -9877:
+ return "Event Not In Queue";
+ case -9878:
+ return "Hot Key Exists";
+ case -9879:
+ return "Invalid Hot Key";
+ default:
+ return "Invalid Error Code";
+ }
+ return "System Error: Unreachable";
+}
+
+#define carbon_menu_warn(err, msg) \
+ if (err && DEBUG_CARBON) \
+ g_printerr("%s: %s %s\n", G_STRFUNC, msg, carbon_menu_error_string(err));
+
+#define carbon_menu_warn_label(err, label, msg) \
+ if (err && DEBUG_CARBON) \
+ g_printerr("%s: %s %s %s\n", G_STRFUNC, label, msg, carbon_menu_error_string(err));
+
+#define carbon_menu_err_return(err, msg) \
+ if (err) { \
+ if (DEBUG_CARBON) \
+ g_printerr("%s: %s %s\n", G_STRFUNC, msg, carbon_menu_error_string(err)); \
+ return;\
+ }
+
+#define carbon_menu_err_return_val(err, msg, val) \
+ if (err) { \
+ if (DEBUG_CARBON) \
+ g_printerr("%s: %s %s\n", G_STRFUNC, msg, carbon_menu_error_string(err)); \
+ return val;\
+ }
+
+#define carbon_menu_err_return_label(err, label, msg) \
+ if (err) { \
+ if (DEBUG_CARBON) \
+ g_printerr("%s: %s %s %s\n", G_STRFUNC, label, msg, carbon_menu_error_string(err)); \
+ return;\
+ }
+
+#define carbon_menu_err_return_label_val(err, label, msg, val) \
+ if (err) { \
+ if (DEBUG_CARBON) \
+ g_printerr("%s: %s %s %s\n", G_STRFUNC, label, msg, carbon_menu_error_string(err)); \
+ return val;\
+ }
+
+static CarbonMenuItem *
+carbon_menu_item_get (GtkWidget *widget) {
+ return g_object_get_qdata (G_OBJECT (widget), carbon_menu_item_quark);
+}
+
+static CarbonMenuItem *
+carbon_menu_item_get_checked (GtkWidget *widget) {
+ CarbonMenuItem * carbon_item = carbon_menu_item_get(widget);
+ GtkWidget *checkWidget = NULL;
+ OSStatus err;
+ const gchar *label = get_menu_label_text(GTK_WIDGET(widget), NULL);
+ const gchar *name = gtk_widget_get_name(widget);
+
+ if (!carbon_item)
+ return NULL;
+
+ /* Get any GtkWidget associated with the item. */
+ err = GetMenuItemProperty (carbon_item->menu, carbon_item->index,
+ IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET,
+ sizeof (checkWidget), 0, &checkWidget);
+ if (err) {
+ if (DEBUG_CARBON)
+ g_printerr("%s: Widget %s %s Cross-check error %s\n", G_STRFUNC,
+ name, label, carbon_menu_error_string(err));
+ return NULL;
+ }
+/* This could check the checkWidget, but that could turn into a
+ * recursion nightmare, so worry about it when it we run
+ * carbon_menu_item_get on it.
+ */
+ if (widget != checkWidget) {
+ const gchar *clabel = get_menu_label_text(GTK_WIDGET(checkWidget),
+ NULL);
+ const gchar *cname = gtk_widget_get_name(checkWidget);
+ if (DEBUG_CARBON)
+ g_printerr("%s: Widget mismatch, expected %s %s got %s %s\n",
+ G_STRFUNC, name, label, cname, clabel);
+ return NULL;
+ }
+ return carbon_item;
+}
+
+static void
+carbon_menu_item_update_state (CarbonMenuItem *carbon_item, GtkWidget *widget) {
+ gboolean sensitive;
+ gboolean visible;
+ UInt32 set_attrs = 0;
+ UInt32 clear_attrs = 0;
+ OSStatus err;
+
+ g_object_get (widget, "sensitive", &sensitive, "visible", &visible, NULL);
+ if (!sensitive)
+ set_attrs |= kMenuItemAttrDisabled;
+ else
+ clear_attrs |= kMenuItemAttrDisabled;
+ if (!visible)
+ set_attrs |= kMenuItemAttrHidden;
+ else
+ clear_attrs |= kMenuItemAttrHidden;
+ err = ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
+ set_attrs, clear_attrs);
+ if (!err && carbon_item->submenu)
+ err = ChangeMenuAttributes (carbon_item->submenu,
+ set_attrs, clear_attrs);
+ carbon_menu_warn(err, "Failed to update state");
+}
+
+static void
+carbon_menu_item_update_active (CarbonMenuItem *carbon_item,
+ GtkWidget *widget) {
+ gboolean active;
+ g_object_get (widget, "active", &active, NULL);
+ CheckMenuItem (carbon_item->menu, carbon_item->index, active);
+}
+
+static void
+carbon_menu_item_update_submenu (CarbonMenuItem *carbon_item,
+ GtkWidget *widget, bool debug) {
+ GtkWidget *submenu;
+ const gchar *label_text;
+ CFStringRef cfstr = NULL;
+ OSStatus err;
+
+ label_text = get_menu_label_text (widget, NULL);
+ submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ if (!submenu) {
+ err = SetMenuItemHierarchicalMenu (carbon_item->menu,
+ carbon_item->index, NULL);
+ carbon_menu_warn_label(err, label_text, "Failed to clear submenu");
+ carbon_item->submenu = NULL;
+ return;
+ }
+ err = CreateNewMenu (++last_menu_id, 0, &carbon_item->submenu);
+ carbon_menu_err_return_label(err, label_text, "Failed to create new menu");
+ if (label_text)
+ cfstr = CFStringCreateWithCString (NULL, label_text,
+ kCFStringEncodingUTF8);
+
+ err = SetMenuTitleWithCFString (carbon_item->submenu, cfstr);
+ if (cfstr)
+ CFRelease (cfstr);
+ carbon_menu_err_return_label(err, label_text, "Failed to set menu title");
+ err = SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
+ carbon_item->submenu);
+ carbon_menu_err_return_label(err, label_text, "Failed to set menu");
+ sync_menu_shell (GTK_MENU_SHELL (submenu), carbon_item->submenu,
+ FALSE, debug);
+}
+
+static void
+carbon_menu_item_update_label (CarbonMenuItem *carbon_item, GtkWidget *widget) {
+ const gchar *label_text;
+ CFStringRef cfstr = NULL;
+ OSStatus err;
+
+ label_text = get_menu_label_text (widget, NULL);
+ if (label_text)
+ cfstr = CFStringCreateWithCString (NULL, label_text,
+ kCFStringEncodingUTF8);
+ err = SetMenuItemTextWithCFString (carbon_item->menu, carbon_item->index,
+ cfstr);
+ carbon_menu_warn(err, "Failed to set menu text");
+ if (cfstr)
+ CFRelease (cfstr);
+}
+
+static void
+carbon_menu_item_update_accelerator (CarbonMenuItem *carbon_item,
+ GtkWidget *widget) {
+ GtkAccelKey *key;
+ GtkWidget *label;
+ GdkDisplay *display = NULL;
+ GdkKeymap *keymap = NULL;
+ GdkKeymapKey *keys = NULL;
+ gint n_keys = 0;
+ UInt8 modifiers = 0;
+ OSStatus err;
+
+ const gchar *label_txt = get_menu_label_text (widget, &label);
+ if (!(GTK_IS_ACCEL_LABEL (label)
+ && _gtk_accel_label_get_closure(GTK_ACCEL_LABEL (label)))) {
+// Clear the menu shortcut
+ err = SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
+ kMenuNoModifiers | kMenuNoCommandModifier);
+ carbon_menu_warn_label(err, label_txt, "Failed to set modifiers");
+ err = ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
+ 0, kMenuItemAttrUseVirtualKey);
+ carbon_menu_warn_label(err, label_txt, "Failed to change attributes");
+ err = SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
+ false, 0);
+ carbon_menu_warn_label(err, label_txt, "Failed to clear command key");
+ return;
+ }
+ GClosure *closure = _gtk_accel_label_get_closure(GTK_ACCEL_LABEL(label));
+ key = gtk_accel_group_find (gtk_accel_group_from_accel_closure(closure),
+ accel_find_func,
+ closure);
+ if (!(key && key->accel_key && key->accel_flags & GTK_ACCEL_VISIBLE))
+ return;
+ display = gtk_widget_get_display (widget);
+ keymap = gdk_keymap_get_for_display (display);
+
+ if (!gdk_keymap_get_entries_for_keyval (keymap, key->accel_key,
+ &keys, &n_keys))
+ return;
+
+ err = SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
+ true, keys[0].keycode);
+ carbon_menu_warn_label(err, label_txt, "Set Command Key Failed");
+ g_free (keys);
+ if (key->accel_mods) {
+ if (key->accel_mods & GDK_SHIFT_MASK)
+ modifiers |= kMenuShiftModifier;
+ if (key->accel_mods & GDK_MOD1_MASK)
+ modifiers |= kMenuOptionModifier;
+ }
+ if (!(key->accel_mods & GDK_CONTROL_MASK)) {
+ modifiers |= kMenuNoCommandModifier;
+ }
+ err = SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
+ modifiers);
+ carbon_menu_warn_label(err, label_txt, "Set Item Modifiers Failed");
+ return;
+}
+
+static void
+carbon_menu_item_accel_changed (GtkAccelGroup *accel_group, guint keyval,
+ GdkModifierType modifier,
+ GClosure *accel_closure, GtkWidget *widget) {
+ CarbonMenuItem *carbon_item = carbon_menu_item_get (widget);
+ GtkWidget *label;
+
+ const gchar *label_text = get_menu_label_text (widget, &label);
+ if (!carbon_item) {
+ if (DEBUG_CARBON)
+ g_printerr("%s: Bad carbon item for %s\n", G_STRFUNC, label_text);
+ return;
+ }
+ if (gtk_accel_group_from_accel_closure(accel_closure) != accel_group)
+ return;
+ if (GTK_IS_ACCEL_LABEL (label) &&
+ _gtk_accel_label_get_closure(GTK_ACCEL_LABEL(label)) == accel_closure)
+ carbon_menu_item_update_accelerator (carbon_item, widget);
+}
+
+static void
+carbon_menu_item_update_accel_closure (CarbonMenuItem *carbon_item,
+ GtkWidget *widget) {
+ GtkAccelGroup *group;
+ GtkWidget *label;
+ get_menu_label_text (widget, &label);
+ if (carbon_item->accel_closure) {
+ group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
+ g_signal_handlers_disconnect_by_func (group,
+ carbon_menu_item_accel_changed,
+ widget);
+ g_closure_unref (carbon_item->accel_closure);
+ carbon_item->accel_closure = NULL;
+ }
+ if (GTK_IS_ACCEL_LABEL (label))
+ carbon_item->accel_closure = _gtk_accel_label_get_closure(GTK_ACCEL_LABEL (label));
+ if (carbon_item->accel_closure) {
+ g_closure_ref (carbon_item->accel_closure);
+ group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure);
+ g_signal_connect_object (group, "accel-changed",
+ G_CALLBACK (carbon_menu_item_accel_changed),
+ widget, 0);
+ }
+ carbon_menu_item_update_accelerator (carbon_item, widget);
+}
+
+static void
+carbon_menu_item_notify (GObject *object, GParamSpec *pspec,
+ CarbonMenuItem *carbon_item) {
+ if (!strcmp (pspec->name, "sensitive") ||
+ !strcmp (pspec->name, "visible")) {
+ carbon_menu_item_update_state (carbon_item, GTK_WIDGET (object));
+ }
+ else if (!strcmp (pspec->name, "active")) {
+ carbon_menu_item_update_active (carbon_item, GTK_WIDGET (object));
+ }
+ else if (!strcmp (pspec->name, "submenu")) {
+ carbon_menu_item_update_submenu (carbon_item, GTK_WIDGET (object),
+DEBUG_SIGNAL);
+ }
+ else if (DEBUG)
+ g_printerr("%s: Invalid parameter specification %s\n", G_STRFUNC,
+ pspec->name);
+}
+
+static void
+carbon_menu_item_notify_label (GObject *object, GParamSpec *pspec,
+ gpointer data) {
+ CarbonMenuItem *carbon_item =
+ carbon_menu_item_get_checked (GTK_WIDGET (object));
+ const gchar *label_text = get_menu_label_text(GTK_WIDGET(object), NULL);
+
+ if (!carbon_item) {
+ if (DEBUG_CARBON)
+ g_printerr("%s: Bad carbon item for %s\n", G_STRFUNC, label_text);
+ return;
+ }
+ if (!strcmp (pspec->name, "label")) {
+ carbon_menu_item_update_label (carbon_item, GTK_WIDGET (object));
+ }
+ else if (!strcmp (pspec->name, "accel-closure")) {
+ carbon_menu_item_update_accel_closure (carbon_item,
+ GTK_WIDGET (object));
+ }
+}
+
+static CarbonMenuItem *
+carbon_menu_item_connect (GtkWidget *menu_item, GtkWidget *label,
+ MenuRef menu, MenuItemIndex index) {
+ CarbonMenuItem *carbon_item =
+ carbon_menu_item_get_checked (menu_item);
+
+ if (!carbon_item) {
+ carbon_item = carbon_menu_item_new ();
+ g_object_set_qdata_full (G_OBJECT (menu_item), carbon_menu_item_quark,
+ carbon_item,
+ (GDestroyNotify) carbon_menu_item_free);
+ g_signal_connect (menu_item, "notify",
+ G_CALLBACK (carbon_menu_item_notify), carbon_item);
+ if (label)
+ g_signal_connect_swapped(label, "notify::label",
+ G_CALLBACK (carbon_menu_item_notify_label),
+ menu_item);
+ }
+ carbon_item->menu = menu;
+ carbon_item->index = index;
+ return carbon_item;
+}
+
+static CarbonMenuItem *
+carbon_menu_item_create (GtkWidget *menu_item, MenuRef carbon_menu,
+ MenuItemIndex index, bool debug) {
+ GtkWidget *label = NULL;
+ const gchar *label_text;
+ CFStringRef cfstr = NULL;
+ MenuItemAttributes attributes = 0;
+ CarbonMenuItem *carbon_item;
+ OSStatus err;
+
+ label_text = get_menu_label_text (menu_item, &label);
+ if (debug)
+ g_printerr ("%s: -> creating new %s\n", G_STRFUNC, label_text);
+ if (label_text)
+ cfstr = CFStringCreateWithCString (NULL, label_text,
+ kCFStringEncodingUTF8);
+ if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
+ attributes |= kMenuItemAttrSeparator;
+ if (!gtk_widget_get_sensitive(menu_item))
+ attributes |= kMenuItemAttrDisabled;
+ if (!gtk_widget_get_visible (menu_item))
+ attributes |= kMenuItemAttrHidden;
+ err = InsertMenuItemTextWithCFString (carbon_menu, cfstr, index - 1,
+ attributes, 0);
+ carbon_menu_err_return_label_val(err, label_text,
+ "Failed to insert menu item", NULL);
+ err = SetMenuItemProperty (carbon_menu, index,
+ IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET,
+ sizeof (menu_item), &menu_item);
+
+ if (cfstr)
+ CFRelease (cfstr);
+ if (err) {
+ carbon_menu_warn_label(err, label_text,
+ "Failed to set menu property");
+ DeleteMenuItem(carbon_menu, index); //Clean up the extra menu item
+ return NULL;
+ }
+ carbon_item = carbon_menu_item_connect (menu_item, label,
+ carbon_menu,
+ index);
+ if (!carbon_item) { //Got a bad carbon_item, bail out
+ DeleteMenuItem(carbon_menu, index); //Clean up the extra menu item
+ return carbon_item;
+ }
+ return carbon_item;
+}
+
+
+typedef struct {
+ GtkWidget *widget;
+} ActivateIdleData;
+
+static void
+activate_destroy_cb (gpointer user_data) {
+ ActivateIdleData *data = user_data;
+
+ if (data->widget)
+ g_object_remove_weak_pointer (G_OBJECT (data->widget),
+ (gpointer) &data->widget);
+ g_free (data);
+}
+
+static gboolean
+activate_idle_cb (gpointer user_data) {
+ ActivateIdleData *data = user_data;
+
+ if (data->widget)
+ gtk_menu_item_activate (GTK_MENU_ITEM (data->widget));
+ return FALSE;
+}
+
+/*
+ * carbon event handler
+ */
+
+static OSStatus
+menu_event_handler_func (EventHandlerCallRef event_handler_call_ref,
+ EventRef event_ref, void *data) {
+ UInt32 event_class = GetEventClass (event_ref);
+ UInt32 event_kind = GetEventKind (event_ref);
+ HICommand command;
+ OSStatus err;
+ GtkWidget *widget = NULL;
+ ActivateIdleData *idleData;
+
+ switch (event_class) {
+ case kEventClassCommand:
+ /* This is called when activating (is that the right GTK+ term?)
+ * a menu item.
+ */
+ if (event_kind != kEventCommandProcess)
+ break;
+
+#if DEBUG
+ g_printerr ("Menu: kEventClassCommand/kEventCommandProcess\n");
+#endif
+ err = GetEventParameter (event_ref, kEventParamDirectObject,
+ typeHICommand, 0,
+ sizeof (command), 0, &command);
+ if (err != noErr) {
+ carbon_menu_warn(err, "Get Event Returned Error");
+ break;
+ }
+ /* Get any GtkWidget associated with the item. */
+ err = GetMenuItemProperty (command.menu.menuRef,
+ command.menu.menuItemIndex,
+ IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET,
+ sizeof (widget), 0, &widget);
+ if (err != noErr) {
+ carbon_menu_warn(err, "Failed to retrieve the widget associated with the menu item");
+ break;
+ }
+ if (! GTK_IS_WIDGET (widget)) {
+ g_printerr("The item associated with the menu item isn't a widget\n");
+ break;
+ }
+ /* Activate from an idle handler so that the event is
+ * emitted from the main loop instead of in the middle of
+ * handling quartz events.
+ */
+ idleData = g_new0 (ActivateIdleData, 1);
+ idleData->widget= widget;
+ g_object_add_weak_pointer (G_OBJECT (widget),
+ (gpointer) &idleData->widget);
+ g_idle_add_full (G_PRIORITY_HIGH, activate_idle_cb,
+ idleData, activate_destroy_cb);
+ return noErr;
+ break;
+ case kEventClassMenu:
+ if (event_kind == kEventMenuEndTracking)
+ g_idle_add (menu_flash_off_cb, NULL);
+ break;
+ default:
+ break;
+ }
+ return eventNotHandledErr;
+}
+
+static gboolean
+nsevent_handle_menu_key (NSEvent *nsevent) {
+ EventRef event_ref;
+ MenuRef menu_ref;
+ MenuItemIndex index;
+ MenuCommand menu_command;
+ HICommand hi_command;
+ OSStatus err;
+
+ if ([nsevent type] != NSKeyDown)
+ return FALSE;
+ event_ref = [nsevent gdk_quartz_event_ref];
+ if (!IsMenuKeyEvent (NULL, event_ref, kMenuEventQueryOnly,
+ &menu_ref, &index))
+ return FALSE;
+ err = GetMenuItemCommandID (menu_ref, index, &menu_command);
+ carbon_menu_err_return_val(err, "Failed to get command id", FALSE);
+ hi_command.commandID = menu_command;
+ hi_command.menu.menuRef = menu_ref;
+ hi_command.menu.menuItemIndex = index;
+ err = CreateEvent (NULL, kEventClassCommand, kEventCommandProcess,
+ 0, kEventAttributeUserEvent, &event_ref);
+ carbon_menu_err_return_val(err, "Failed to create event", FALSE);
+ err = SetEventParameter (event_ref, kEventParamDirectObject, typeHICommand,
+ sizeof (HICommand), &hi_command);
+ if (err != noErr)
+ ReleaseEvent(event_ref); //We're about to bail, don't want to leak
+ carbon_menu_err_return_val(err, "Failed to set event parm", FALSE);
+ FlashMenuBar (GetMenuID (menu_ref));
+ g_timeout_add (30, menu_flash_off_cb, NULL);
+ err = SendEventToEventTarget (event_ref, GetMenuEventTarget (menu_ref));
+ ReleaseEvent (event_ref);
+ carbon_menu_err_return_val(err, "Failed to send event", FALSE);
+ return TRUE;
+}
+
+gboolean
+ige_mac_menu_handle_menu_event (GdkEventKey *event) {
+ NSEvent *nsevent;
+
+ /* FIXME: If the event here is unallocated, we crash. */
+ nsevent = gdk_quartz_event_get_nsevent ((GdkEvent *) event);
+ if (nsevent)
+ return nsevent_handle_menu_key (nsevent);
+ return FALSE;
+}
+
+static GdkFilterReturn
+global_event_filter_func (gpointer windowing_event, GdkEvent *event,
+ gpointer user_data) {
+ NSEvent *nsevent = windowing_event;
+
+ /* Handle menu events with no window, since they won't go through the
+ * regular event processing.
+ */
+ if ([nsevent window] == nil) {
+ if (nsevent_handle_menu_key (nsevent))
+ return GDK_FILTER_REMOVE;
+ }
+ else if (global_key_handler_enabled && [nsevent type] == NSKeyDown) {
+ GList *toplevels, *l;
+ GtkWindow *focus = NULL;
+
+ toplevels = gtk_window_list_toplevels ();
+ for (l = toplevels; l; l = l->next) {
+ if (gtk_window_has_toplevel_focus (l->data)) {
+ focus = l->data;
+ break;
+ }
+ }
+ g_list_free (toplevels);
+
+ /* FIXME: We could do something to skip menu events if there is a
+ * modal dialog...
+ */
+ if (!focus
+ || !g_object_get_data (G_OBJECT (focus), IGE_MAC_KEY_HANDLER)) {
+ if (nsevent_handle_menu_key (nsevent))
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data) {
+ GtkWindow *window = GTK_WINDOW (widget);
+ GtkWidget *focus = gtk_window_get_focus (window);
+ gboolean handled = FALSE;
+
+ /* Text widgets get all key events first. */
+ if (GTK_IS_EDITABLE (focus) || GTK_IS_TEXT_VIEW (focus))
+ handled = gtk_window_propagate_key_event (window, event);
+
+ if (!handled)
+ handled = ige_mac_menu_handle_menu_event (event);
+
+ /* Invoke control/alt accelerators. */
+ if (!handled && event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))
+ handled = gtk_window_activate_key (window, event);
+
+ /* Invoke focus widget handlers. */
+ if (!handled)
+ handled = gtk_window_propagate_key_event (window, event);
+
+ /* Invoke non-(control/alt) accelerators. */
+ if (!handled && !(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
+ handled = gtk_window_activate_key (window, event);
+
+ return handled;
+}
+
+static void
+setup_menu_event_handler (void) {
+ static gboolean is_setup = FALSE;
+
+ EventHandlerUPP menu_event_handler_upp;
+ EventHandlerRef menu_event_handler_ref;
+ OSStatus err;
+ const EventTypeSpec menu_events[] = {
+ { kEventClassCommand, kEventCommandProcess },
+ { kEventClassMenu, kEventMenuEndTracking }
+ };
+
+ if (is_setup)
+ return;
+ gdk_window_add_filter (NULL, global_event_filter_func, NULL);
+ menu_event_handler_upp = NewEventHandlerUPP (menu_event_handler_func);
+ err = InstallEventHandler (GetApplicationEventTarget (),
+ menu_event_handler_upp,
+ GetEventTypeCount (menu_events), menu_events, 0,
+ &menu_event_handler_ref);
+ carbon_menu_err_return(err, "Failed to install event handler");
+#if 0
+ /* Note: If we want to supporting shutting down, remove the handler
+ * with:
+ */
+ err = RemoveEventHandler(menu_event_handler_ref);
+ carbon_menu_warn(err, "Failed to remove handler");
+ err = DisposeEventHandlerUPP(menu_event_handler_upp);
+ carbon_menu_warn(err, "Failed to elete menu handler UPP");
+#endif
+ is_setup = TRUE;
+}
+
+static void
+sync_menu_shell (GtkMenuShell *menu_shell, MenuRef carbon_menu,
+ gboolean toplevel, gboolean debug) {
+ GList *children;
+ GList *l;
+ MenuItemIndex carbon_index = 1;
+ OSStatus err;
+
+ if (debug)
+ g_printerr ("%s: syncing shell %s (%p)\n", G_STRFUNC,
+ get_menu_label_text(GTK_WIDGET(menu_shell), NULL),
+ menu_shell);
+ carbon_menu_connect (GTK_WIDGET (menu_shell), carbon_menu, toplevel);
+ children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
+ for (l = children; l; l = l->next) {
+ GtkWidget *menu_item = l->data;
+ CarbonMenuItem *carbon_item;
+ MenuAttributes attrs;
+ const gchar *label = get_menu_label_text (menu_item, NULL);
+
+ if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
+ continue;
+ if (toplevel && (g_object_get_data (G_OBJECT (menu_item),
+ "gtk-empty-menu-item")
+ || GTK_IS_SEPARATOR_MENU_ITEM (menu_item)))
+ continue;
+ carbon_item = carbon_menu_item_get (menu_item);
+ if (debug)
+ g_printerr ("%s: carbon_item %d for menu_item %d (%s, %s)\n",
+ G_STRFUNC, carbon_item ? carbon_item->index : -1,
+ carbon_index, label,
+ g_type_name (G_TYPE_FROM_INSTANCE (menu_item)));
+
+ if (carbon_item && carbon_item->index != carbon_index) {
+ if (carbon_item->index == carbon_index - 1) {
+ if (debug)
+ g_printerr("%s: %s incrementing index\n", G_STRFUNC, label);
+ ++carbon_item->index;
+ }
+ else if (carbon_item->index == carbon_index + 1) {
+ if (debug)
+ g_printerr("%s: %s decrementing index\n", G_STRFUNC, label);
+ --carbon_item->index;
+ }
+ else {
+ if (debug)
+ g_printerr ("%s: %s -> not matching, deleting\n",
+ G_STRFUNC, label);
+ DeleteMenuItem (carbon_item->menu, carbon_index);
+ carbon_item = NULL;
+ }
+ }
+ if (!carbon_item)
+ carbon_item = carbon_menu_item_create(menu_item, carbon_menu,
+ carbon_index, debug);
+ if (!carbon_item) //Bad carbon item, give up
+ continue;
+ if (GTK_IS_CHECK_MENU_ITEM (menu_item))
+ carbon_menu_item_update_active (carbon_item, menu_item);
+ carbon_menu_item_update_accel_closure (carbon_item, menu_item);
+ if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)))
+ carbon_menu_item_update_submenu (carbon_item, menu_item, debug);
+ else {
+ carbon_index++;
+ continue;
+ }
+
+/*The rest only applies to submenus, not to items which should have
+ * been fixed up in carbon_menu_item_create
+ */
+ err = GetMenuAttributes( carbon_item->submenu, &attrs);
+ carbon_menu_warn(err, "Failed to get menu attributes");
+ if (!gtk_widget_get_visible (menu_item)) {
+ if ((attrs & kMenuAttrHidden) == 0) {
+ if (debug)
+ g_printerr("Hiding menu %s\n", label);
+ err = ChangeMenuAttributes (carbon_item->submenu,
+ kMenuAttrHidden, 0);
+ carbon_menu_warn_label(err, label, "Failed to set visible");
+ }
+ }
+ else if ((attrs & kMenuAttrHidden) != 0) {
+ if (debug)
+ g_printerr("Revealing menu %s\n", label);
+ err = ChangeMenuAttributes (carbon_item->submenu, 0,
+ kMenuAttrHidden);
+ carbon_menu_warn_label(err, label, "Failed to set Hidden");
+ }
+ carbon_index++;
+ }
+ g_list_free (children);
+}
+
+static gulong emission_hook_id = 0;
+static gint emission_hook_count = 0;
+
+static gboolean
+parent_set_emission_hook (GSignalInvocationHint *ihint, guint n_param_values,
+ const GValue *param_values, gpointer data) {
+ GtkWidget *instance = g_value_get_object (param_values);
+ CarbonMenu *carbon_menu;
+ GtkWidget *previous_parent = NULL;
+ GtkWidget *menu_shell = NULL;
+
+ if (!GTK_IS_MENU_ITEM (instance))
+ return TRUE;
+ previous_parent = g_value_get_object (param_values + 1);
+ if (GTK_IS_MENU_SHELL (previous_parent)) {
+ menu_shell = previous_parent;
+ }
+ else if (GTK_IS_MENU_SHELL (gtk_widget_get_parent(instance))) {
+ menu_shell = gtk_widget_get_parent(instance);
+ }
+ if (!menu_shell)
+ return TRUE;
+ carbon_menu = carbon_menu_get (menu_shell);
+
+ if (!carbon_menu)
+ return TRUE;
+#if DEBUG
+ g_printerr ("%s: item %s (%s) %s %s (%p)\n", G_STRFUNC,
+ get_menu_label_text (instance, NULL),
+ g_type_name (G_TYPE_FROM_INSTANCE (instance)),
+ previous_parent ? "removed from" : "added to",
+ get_menu_label_text(menu_shell, NULL),
+ menu_shell);
+#endif
+ sync_menu_shell (GTK_MENU_SHELL (menu_shell), carbon_menu->menu,
+ carbon_menu->toplevel, DEBUG_SIGNAL);
+ return TRUE;
+}
+
+static void
+parent_set_emission_hook_remove (GtkWidget *widget, gpointer data) {
+ CarbonMenu *carbon_menu = carbon_menu_get(widget);
+ if (carbon_menu) {
+ MenuID id = GetMenuID(carbon_menu->menu);
+ ClearMenuBar();
+ DeleteMenu(id);
+ }
+ emission_hook_count--;
+ if (emission_hook_count > 0)
+ return;
+ g_signal_remove_emission_hook (
+ g_signal_lookup("parent-set", GTK_TYPE_WIDGET), emission_hook_id);
+ emission_hook_id = 0;
+}
+
+static gboolean
+window_focus(GtkWindow *window, GdkEventFocus *event, CarbonMenu *menu) {
+ OSStatus err = SetRootMenu(menu->menu);
+ if (err) {
+ carbon_menu_warn(err, "Failed to transfer menu");
+ }
+ else if (DEBUG){
+ g_printerr("%s: Switched Menu\n", G_STRFUNC);
+ }
+ return FALSE;
+}
+
+
+/*
+ * public functions
+ */
+
+void
+ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell) {
+ CarbonMenu *current_menu;
+ MenuRef carbon_menubar;
+ OSStatus err;
+ GtkWidget *parent = gtk_widget_get_toplevel(GTK_WIDGET(menu_shell));
+
+ g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+ if (carbon_menu_quark == 0)
+ carbon_menu_quark = g_quark_from_static_string ("CarbonMenu");
+ if (carbon_menu_item_quark == 0)
+ carbon_menu_item_quark = g_quark_from_static_string ("CarbonMenuItem");
+ current_menu = carbon_menu_get (GTK_WIDGET (menu_shell));
+ if (current_menu) {
+ err = SetRootMenu (current_menu->menu);
+ carbon_menu_warn(err, "Failed to set root menu");
+ return;
+ }
+ err = CreateNewMenu (++last_menu_id /*id*/, 0 /*options*/, &carbon_menubar);
+ carbon_menu_err_return(err, "Failed to create menu");
+ err = SetRootMenu (carbon_menubar);
+ carbon_menu_err_return(err, "Failed to set root menu");
+ setup_menu_event_handler ();
+ if (emission_hook_id == 0) {
+ emission_hook_id =
+ g_signal_add_emission_hook(
+ g_signal_lookup("parent-set", GTK_TYPE_WIDGET), 0,
+ parent_set_emission_hook, NULL, NULL);
+ }
+ emission_hook_count++;
+ g_signal_connect (menu_shell, "destroy",
+ G_CALLBACK (parent_set_emission_hook_remove), NULL);
+
+#if DEBUG_SET
+ g_printerr ("%s: syncing menubar\n", G_STRFUNC);
+#endif
+ sync_menu_shell (menu_shell, carbon_menubar, TRUE, DEBUG_SET);
+ if (parent)
+ g_signal_connect (parent, "focus-in-event",
+ G_CALLBACK(window_focus),
+ carbon_menu_get(GTK_WIDGET(menu_shell)));
+
+}
+
+void
+ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item) {
+ MenuRef appmenu;
+ MenuItemIndex index;
+ OSStatus err;
+
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+ setup_menu_event_handler ();
+ err = GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
+ &appmenu, &index);
+ carbon_menu_err_return(err, "Failed to obtain Quit Menu");
+ err = SetMenuItemCommandID (appmenu, index, 0);
+ carbon_menu_err_return(err,
+ "Failed to set Quit menu command id");
+ err = SetMenuItemProperty (appmenu, index, IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET, sizeof (menu_item),
+ &menu_item);
+ carbon_menu_err_return(err,
+ "Failed to associate Quit menu item");
+ gtk_widget_hide (GTK_WIDGET (menu_item));
+ return;
+}
+
+void
+ige_mac_menu_connect_window_key_handler (GtkWindow *window) {
+ if (g_object_get_data (G_OBJECT (window), IGE_MAC_KEY_HANDLER)) {
+ g_warning ("Window %p is already connected", window);
+ return;
+ }
+
+ g_signal_connect (window, "key-press-event",
+ G_CALLBACK (key_press_event), NULL);
+ g_object_set_data (G_OBJECT (window), IGE_MAC_KEY_HANDLER,
+ GINT_TO_POINTER (1));
+}
+
+/* Most applications will want to have this enabled (which is the
+ * defalt). For apps that need to deal with the events themselves, the
+ * global handling can be disabled.
+ */
+void
+ige_mac_menu_set_global_key_handler_enabled (gboolean enabled) {
+ global_key_handler_enabled = enabled;
+}
+
+/* For testing use only. Returns TRUE if there is a GtkMenuItem assigned to
+ * the Quit menu item.
+ */
+gboolean
+_ige_mac_menu_is_quit_menu_item_handled (void) {
+ MenuRef appmenu;
+ MenuItemIndex index;
+ OSStatus err = GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
+ &appmenu, &index);
+ carbon_menu_warn(err, "failed with");
+ return (err == noErr);
+}
+
+/* Application Menu Functions
+ *
+ * The "application" menu (the one named the same as the application),
+ * is special because there isn't a corresponding Gtk menu, but OSX
+ * practice puts the About, Preferences, and Quit menu items in it, so
+ * we need to provide a way for app developers to move those items
+ * (and others, if they want) from their Gtk locations to the app
+ * menu.
+ */
+struct _IgeMacMenuGroup {
+ GList *items;
+};
+/* app_menu_groups is a list of IgeMacMenuGroups, itself a list of
+ * menu_items. They're provided to insert separators into the app
+ * menu, grouping the items.
+*/
+static GList *app_menu_groups = NULL;
+
+IgeMacMenuGroup *
+ige_mac_menu_add_app_menu_group (void) {
+ IgeMacMenuGroup *group = g_slice_new0 (IgeMacMenuGroup);
+
+ app_menu_groups = g_list_append (app_menu_groups, group);
+ return group;
+}
+
+/** Move a menu item to the App menu (the one named with the
+ * application's name). This has issues with multiple window menubars,
+ * because the menu items are tied to a particular window's menu and
+ * because there's only one App menu in Carbon regardless of how many
+ * menubars there are. Don't use this for Quit, it has its own
+ * function.
+ */
+void
+ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group, GtkMenuItem *menu_item,
+ const gchar *label) {
+ MenuRef appmenu;
+ GList *list;
+ gint index = 0;
+ CFStringRef cfstr;
+ OSStatus err;
+
+ g_return_if_fail (group != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+ setup_menu_event_handler ();
+ err = GetIndMenuItemWithCommandID (NULL, kHICommandHide, 1,
+ &appmenu, NULL);
+ carbon_menu_err_return(err, "retrieving app menu failed");
+ for (list = app_menu_groups; list; list = g_list_next (list)) {
+ IgeMacMenuGroup *list_group = list->data;
+
+ index += g_list_length (list_group->items);
+ /* adjust index for the separator between groups, but not
+ * before the first group
+ */
+ if (list_group->items && list->prev)
+ index++;
+ if (group != list_group)
+ continue;
+
+ /* add a separator before adding the first item, but not
+ * for the first group
+ */
+ if (!group->items && list->prev) {
+ err = InsertMenuItemTextWithCFString (appmenu, NULL, index,
+ kMenuItemAttrSeparator, 0);
+ carbon_menu_err_return(err, "Failed to add separator");
+ index++;
+ }
+ if (!label)
+ label = get_menu_label_text (GTK_WIDGET (menu_item), NULL);
+ cfstr = CFStringCreateWithCString (NULL, label,
+ kCFStringEncodingUTF8);
+//Add a new menu item and associate it with the GtkMenuItem.
+ err = InsertMenuItemTextWithCFString (appmenu, cfstr, index, 0, 0);
+ carbon_menu_err_return(err, "Failed to add menu item");
+ err = SetMenuItemProperty (appmenu, index + 1,
+ IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET,
+ sizeof (menu_item), &menu_item);
+ CFRelease (cfstr);
+ carbon_menu_err_return(err, "Failed to associate Gtk Widget");
+ gtk_widget_hide (GTK_WIDGET (menu_item));
+//Finally add the item to the group; this is really just for tracking the count.
+ group->items = g_list_append (group->items, menu_item);
+//Bail out: The rest of the menu doesn't matter.
+ return;
+
+ }
+ if (!list)
+ g_warning ("%s: app menu group %p does not exist", G_STRFUNC, group);
+}
+/** Syncronize changes in the GtkMenuBar to an already-created Mac
+ * MenuBar. You must have already run ige_mac_menu_set_menu_bar on the
+ * GtkMenuBar to be synced.
+ */
+void
+ige_mac_menu_sync(GtkMenuShell *menu_shell) {
+ CarbonMenu *carbon_menu = carbon_menu_get (GTK_WIDGET(menu_shell));
+ g_return_if_fail(carbon_menu != NULL);
+#if DEBUG_SYNC
+ g_printerr ("%s: syncing menubar\n", G_STRFUNC);
+#endif
+ sync_menu_shell (menu_shell, carbon_menu->menu,
+ carbon_menu->toplevel, DEBUG_SYNC);
+}
+
+#endif __x86_64__
diff --git a/src/ige-mac-menu.h b/src/ige-mac-menu.h
new file mode 100644
index 0000000..713bcf6
--- /dev/null
+++ b/src/ige-mac-menu.h
@@ -0,0 +1,50 @@
+/* GTK+ Integration for the Mac OS X Menubar.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007, 2008 Imendio AB
+ * Copyright  2009, 2010 John Ralls
+ *
+ * For further information, see:
+ * http://sourceforge.net/apps/trac/gtk-osx/wiki/Integrate
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __IGE_MAC_MENU_H__
+#define __IGE_MAC_MENU_H__
+#ifndef __x86_64__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IgeMacMenuGroup IgeMacMenuGroup;
+
+void ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell);
+void ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item);
+IgeMacMenuGroup *ige_mac_menu_add_app_menu_group (void);
+void ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group,
+ GtkMenuItem *menu_item,
+ const gchar *label);
+void ige_mac_menu_sync (GtkMenuShell *menu_shell);
+gboolean ige_mac_menu_handle_menu_event (GdkEventKey *event);
+void ige_mac_menu_set_global_key_handler_enabled (gboolean enabled);
+void ige_mac_menu_connect_window_key_handler (GtkWindow *window);
+
+G_END_DECLS
+
+#endif /* __x86_64__ */
+#endif /* __IGE_MAC_MENU_H__ */
diff --git a/src/ige-mac-private.h b/src/ige-mac-private.h
new file mode 100644
index 0000000..bbaf514
--- /dev/null
+++ b/src/ige-mac-private.h
@@ -0,0 +1,34 @@
+/* GTK+ Integration for Mac OS X.
+ *
+ * Copyright (C) 2007, 2008 Imendio AB
+ *
+ * For further information, see:
+ * http://sourceforge.net/apps/trac/gtk-osx/wiki/Integrate
+ *
+ * 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.1 of the License, 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.
+ */
+
+#ifndef __IGE_MAC_PRIVATE_H__
+#define __IGE_MAC_PRIVATE_H__
+
+G_BEGIN_DECLS
+
+gboolean _ige_mac_menu_is_quit_menu_item_handled (void);
+gboolean _ige_mac_dock_is_quit_menu_item_handled (void);
+
+G_END_DECLS
+
+#endif /* __IGE_MAC_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]