[gtk-mac-integration] Restore ige-mac-foo files



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]