summaryrefslogtreecommitdiff
path: root/libs/gtkmm2ext
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2008-02-16 22:55:47 +0000
committerDavid Robillard <d@drobilla.net>2008-02-16 22:55:47 +0000
commit859e9106e72a7908fa093d946111d148223225a0 (patch)
treeec47825b5f5746bcbc5d321d40da7fc798f5c380 /libs/gtkmm2ext
parent8aa9508c82f32efcf9c7c00e2c9e76268d4dddce (diff)
Merge with 2.0-ongoing R3071.
git-svn-id: svn://localhost/ardour2/branches/3.0@3074 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/gtkmm2ext')
-rw-r--r--libs/gtkmm2ext/SConscript14
-rw-r--r--libs/gtkmm2ext/gtk_ui.cc21
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/pixfader.h21
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/slider_controller.h2
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/sync-menu.h44
-rw-r--r--libs/gtkmm2ext/pixfader.cc151
-rw-r--r--libs/gtkmm2ext/slider_controller.cc8
-rw-r--r--libs/gtkmm2ext/sync-menu.c977
8 files changed, 1173 insertions, 65 deletions
diff --git a/libs/gtkmm2ext/SConscript b/libs/gtkmm2ext/SConscript
index 829182c71e..fad538a40e 100644
--- a/libs/gtkmm2ext/SConscript
+++ b/libs/gtkmm2ext/SConscript
@@ -32,6 +32,8 @@ gtkmm2ext.Append(CXXFLAGS=["-DLIBSIGC_DISABLE_DEPRECATED", "-DGLIBMM_DEFAULT_SIG
gtkmm2ext.Append(PACKAGE=domain)
gtkmm2ext.Append(POTFILE=domain + '.pot')
+extra_sources = []
+
gtkmm2ext_files = Split("""
auto_spin.cc
barcontroller.cc
@@ -61,12 +63,21 @@ version.cc
window_title.cc
""")
+gtkosx_files=Split("""
+sync-menu.c
+""")
+
+if gtkmm2ext['GTKOSX']:
+ extra_sources += gtkosx_files
+ gtkmm2ext.Append (CCFLAGS="-DTOP_MENUBAR -DGTKOSX")
+ gtkmm2ext.Append (LINKFLAGS="-framework Carbon")
+
gtkmm2ext.VersionBuild(['version.cc','gtkmm2ext/version.h'], [])
gtkmm2ext.Append(CCFLAGS="-D_REENTRANT")
gtkmm2ext.Append(CCFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
-libgtkmm2ext = gtkmm2ext.SharedLibrary('gtkmm2ext', gtkmm2ext_files)
+libgtkmm2ext = gtkmm2ext.SharedLibrary('gtkmm2ext', gtkmm2ext_files + extra_sources)
Default(libgtkmm2ext)
@@ -78,5 +89,6 @@ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ar
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'i18n.h', 'gettext.h'] +
gtkmm2ext_files +
+ gtkosx_files +
glob.glob('po/*.po') +
glob.glob('gtkmm2ext/*.h')))
diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc
index 660ea32ad1..f00c6cd1e1 100644
--- a/libs/gtkmm2ext/gtk_ui.cc
+++ b/libs/gtkmm2ext/gtk_ui.cc
@@ -560,10 +560,31 @@ UI::popup_error (const char *text)
pup->touch ();
}
+#ifdef GTKOSX
+extern "C" {
+ int gdk_quartz_in_carbon_menu_event_handler ();
+}
+#endif
void
UI::flush_pending ()
{
+#ifdef GTKOSX
+ /* as of february 11th 2008, gtk/osx has a problem in that mac menu events
+ are handled using Carbon with an "internal" event handling system that
+ doesn't pass things back to the glib/gtk main loop. this makes
+ gtk_main_iteration() block if we call it while in a menu event handler
+ because glib gets confused and thinks there are two threads running
+ g_main_poll_func().
+
+ this hack (relies on code in gtk2_ardour/sync-menu.c) works
+ around that.
+ */
+
+ if (gdk_quartz_in_carbon_menu_event_handler()) {
+ return;
+ }
+#endif
if (!caller_is_ui_thread()) {
error << "non-UI threads cannot call UI::flush_pending()"
<< endmsg;
diff --git a/libs/gtkmm2ext/gtkmm2ext/pixfader.h b/libs/gtkmm2ext/gtkmm2ext/pixfader.h
index d974f5d5bc..519e1c9e1e 100644
--- a/libs/gtkmm2ext/gtkmm2ext/pixfader.h
+++ b/libs/gtkmm2ext/gtkmm2ext/pixfader.h
@@ -30,7 +30,7 @@ namespace Gtkmm2ext {
class PixFader : public Gtk::DrawingArea {
public:
- PixFader (Glib::RefPtr<Gdk::Pixbuf> belt_image, Gtk::Adjustment& adjustment);
+ PixFader (Glib::RefPtr<Gdk::Pixbuf> belt_image, Gtk::Adjustment& adjustment, int orientation);
virtual ~PixFader ();
protected:
@@ -44,23 +44,32 @@ class PixFader : public Gtk::DrawingArea {
bool on_motion_notify_event (GdkEventMotion*);
bool on_scroll_event (GdkEventScroll* ev);
- private:
+ enum Orientation {
+ VERT=1,
+ HORIZ=2,
+ };
+
+ private:
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
- gint pixheight;
+ int span, girth;
+ int _orien;
GdkRectangle view;
GdkWindow* grab_window;
- double grab_y;
+ double grab_loc;
double grab_start;
int last_drawn;
bool dragging;
float default_value;
- int unity_y;
+ int unity_loc;
void adjustment_changed ();
- int display_height ();
+ int display_span ();
+
+ static int fine_scale_modifier;
+ static int extra_fine_scale_modifier;
};
diff --git a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h
index 74ff36816b..60c8eef660 100644
--- a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h
+++ b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h
@@ -38,7 +38,7 @@ class SliderController : public Gtkmm2ext::PixFader
{
public:
SliderController (Glib::RefPtr<Gdk::Pixbuf> image,
- Gtk::Adjustment* adj,
+ Gtk::Adjustment* adj, int orientation,
PBD::Controllable&,
bool with_numeric = true);
diff --git a/libs/gtkmm2ext/gtkmm2ext/sync-menu.h b/libs/gtkmm2ext/gtkmm2ext/sync-menu.h
new file mode 100644
index 0000000000..2be5e71d02
--- /dev/null
+++ b/libs/gtkmm2ext/gtkmm2ext/sync-menu.h
@@ -0,0 +1,44 @@
+/* GTK+ Integration for the Mac OS X Menubar.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007 Imendio AB
+ *
+ * For further information, see:
+ * http://developer.imendio.com/projects/gtk-macosx/menubar
+ *
+ * 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.
+ */
+
+#ifndef __IGE_MAC_MENU_H__
+#define __IGE_MAC_MENU_H__
+
+#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);
+
+G_END_DECLS
+
+#endif /* __IGE_MAC_MENU_H__ */
diff --git a/libs/gtkmm2ext/pixfader.cc b/libs/gtkmm2ext/pixfader.cc
index f3a40ffc69..fe464d0fac 100644
--- a/libs/gtkmm2ext/pixfader.cc
+++ b/libs/gtkmm2ext/pixfader.cc
@@ -27,21 +27,36 @@ using namespace Gtk;
using namespace Gdk;
using namespace std;
-PixFader::PixFader (Glib::RefPtr<Pixbuf> belt, Gtk::Adjustment& adj)
+#ifdef GTKOSX
+int PixFader::fine_scale_modifier = GDK_META_MASK;
+#else
+int PixFader::fine_scale_modifier = GDK_CONTROL_MASK;
+#endif
+
+int PixFader::extra_fine_scale_modifier = GDK_MOD1_MASK;
+
+PixFader::PixFader (Glib::RefPtr<Pixbuf> belt, Gtk::Adjustment& adj, int orientation)
+
: adjustment (adj),
- pixbuf (belt)
+ pixbuf (belt),
+ _orien(orientation)
{
dragging = false;
default_value = adjustment.get_value();
last_drawn = -1;
- pixheight = pixbuf->get_height();
view.x = 0;
view.y = 0;
- view.width = pixbuf->get_width();
- view.height = pixheight / 2;
- unity_y = (int) rint (view.height - (default_value * view.height)) - 1;
+ if (orientation == VERT) {
+ view.width = girth = pixbuf->get_width();
+ view.height = span = pixbuf->get_height() / 2;
+ unity_loc = (int) rint (view.height - (default_value * view.height)) - 1;
+ } else {
+ view.width = span = pixbuf->get_width () / 2;
+ view.height = girth = pixbuf->get_height();
+ unity_loc = (int) rint (default_value * view.width) - 1;
+ }
add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK);
@@ -57,13 +72,20 @@ bool
PixFader::on_expose_event (GdkEventExpose* ev)
{
GdkRectangle intersection;
- int dh = display_height ();
- int offset_into_pixbuf = (int) floor (view.height / ((float) view.height / dh));
+ int srcx, srcy, ds = display_span ();
+ int offset_into_pixbuf = (int) floor (span / ((float) span / ds));
Glib::RefPtr<Gdk::GC> fg_gc (get_style()->get_fg_gc(get_state()));
if (gdk_rectangle_intersect (&view, &ev->area, &intersection)) {
+ if (_orien == VERT) {
+ srcx = intersection.x;
+ srcy = offset_into_pixbuf + intersection.y;
+ } else {
+ srcx = offset_into_pixbuf + intersection.x;
+ srcy = intersection.y;
+ }
get_window()->draw_pixbuf (fg_gc, pixbuf,
- intersection.x, offset_into_pixbuf + intersection.y,
+ srcx, srcy,
intersection.x, intersection.y,
intersection.width, intersection.height,
Gdk::RGB_DITHER_NONE, 0, 0);
@@ -75,10 +97,12 @@ PixFader::on_expose_event (GdkEventExpose* ev)
}
/* always draw the line */
-
- get_window()->draw_line (fg_gc, 1, unity_y, view.width - 2, unity_y);
-
- last_drawn = dh;
+ if (_orien == VERT) {
+ get_window()->draw_line (fg_gc, 1, unity_loc, girth - 2, unity_loc);
+ } else {
+ get_window()->draw_line (fg_gc, unity_loc, 1, unity_loc, girth - 2);
+ }
+ last_drawn = ds;
return true;
}
@@ -96,8 +120,8 @@ PixFader::on_button_press_event (GdkEventButton* ev)
case 1:
case 2:
add_modal_grab();
- grab_y = ev->y;
- grab_start = ev->y;
+ grab_loc = (_orien == VERT) ? ev->y : ev->x;
+ grab_start = (_orien == VERT) ? ev->y : ev->x;
grab_window = ev->window;
dragging = true;
break;
@@ -112,7 +136,9 @@ PixFader::on_button_press_event (GdkEventButton* ev)
bool
PixFader::on_button_release_event (GdkEventButton* ev)
{
- double fract;
+ double fract, ev_pos;
+
+ ev_pos = (_orien == VERT) ? ev->y : 0; // Don't step if we are horizontal
switch (ev->button) {
case 1:
@@ -120,15 +146,15 @@ PixFader::on_button_release_event (GdkEventButton* ev)
remove_modal_grab();
dragging = false;
- if (ev->y == grab_start) {
+ if (ev_pos == grab_start) {
/* no motion - just a click */
if (ev->state & Gdk::SHIFT_MASK) {
adjustment.set_value (default_value);
- } else if (ev->state & GDK_CONTROL_MASK) {
+ } else if (ev->state & fine_scale_modifier) {
adjustment.set_value (adjustment.get_lower());
- } else if (ev->y < view.height - display_height()) {
+ } else if (ev_pos < span - display_span()) {
/* above the current display height, remember X Window coords */
adjustment.set_value (adjustment.get_value() + adjustment.get_step_increment());
} else {
@@ -144,7 +170,7 @@ PixFader::on_button_release_event (GdkEventButton* ev)
remove_modal_grab();
dragging = false;
- fract = 1.0 - (ev->y / view.height); // inverted X Window coordinates, grrr
+ fract = 1.0 - (ev_pos / span); // inverted X Window coordinates, grrr
fract = min (1.0, fract);
fract = max (0.0, fract);
@@ -164,29 +190,45 @@ bool
PixFader::on_scroll_event (GdkEventScroll* ev)
{
double scale;
-
- if (ev->state & GDK_CONTROL_MASK) {
- if (ev->state & GDK_MOD1_MASK) {
- scale = 0.05;
+
+ if (ev->state & fine_scale_modifier) {
+ if (ev->state & extra_fine_scale_modifier) {
+ scale = 0.01;
} else {
- scale = 0.1;
+ scale = 0.05;
}
} else {
- scale = 0.5;
+ scale = 0.25;
}
- switch (ev->direction) {
-
- case GDK_SCROLL_UP:
- /* wheel up */
- adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale));
- break;
- case GDK_SCROLL_DOWN:
- /* wheel down */
- adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale));
- break;
- default:
- break;
+ if (_orien == VERT) {
+ switch (ev->direction) {
+
+ case GDK_SCROLL_UP:
+ /* wheel up */
+ adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale));
+ break;
+ case GDK_SCROLL_DOWN:
+ /* wheel down */
+ adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale));
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (ev->direction) {
+
+ case GDK_SCROLL_RIGHT:
+ /* wheel right */
+ adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale));
+ break;
+ case GDK_SCROLL_LEFT:
+ /* wheel left */
+ adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale));
+ break;
+ default:
+ break;
+ }
}
return false;
}
@@ -195,18 +237,17 @@ bool
PixFader::on_motion_notify_event (GdkEventMotion* ev)
{
if (dragging) {
- double fract;
- double delta;
- double scale;
-
+ double fract, delta, scale, ev_pos;
+ ev_pos = (_orien == VERT) ? ev->y : ev->x;
+ //cerr << "PixFader::on_motion_notify_event() called x:y = " << ev->x << ":" << ev->y;
if (ev->window != grab_window) {
- grab_y = ev->y;
+ grab_loc = ev_pos;
grab_window = ev->window;
return true;
}
- if (ev->state & GDK_CONTROL_MASK) {
- if (ev->state & GDK_MOD1_MASK) {
+ if (ev->state & fine_scale_modifier) {
+ if (ev->state & extra_fine_scale_modifier) {
scale = 0.05;
} else {
scale = 0.1;
@@ -214,20 +255,23 @@ PixFader::on_motion_notify_event (GdkEventMotion* ev)
} else {
scale = 1.0;
}
+ //cerr << " ev_pos=" << ev_pos << " grab_loc=" << grab_loc;
+ delta = ev_pos - grab_loc;
+ grab_loc = ev_pos;
- delta = ev->y - grab_y;
- grab_y = ev->y;
-
- fract = (delta / view.height);
+ fract = (delta / span);
fract = min (1.0, fract);
fract = max (-1.0, fract);
// X Window is top->bottom for 0..Y
- fract = -fract;
+ if (_orien == VERT) {
+ fract = -fract;
+ }
adjustment.set_value (adjustment.get_value() + scale * fract * (adjustment.get_upper() - adjustment.get_lower()));
+ //cerr << " adj=" << adjustment.get_value() << " fract=" << fract << " delta=" << delta << " scale=" << scale << endl;
}
return true;
@@ -236,14 +280,15 @@ PixFader::on_motion_notify_event (GdkEventMotion* ev)
void
PixFader::adjustment_changed ()
{
- if (display_height() != last_drawn) {
+ if (display_span() != last_drawn) {
queue_draw ();
}
}
int
-PixFader::display_height ()
+PixFader::display_span ()
{
float fract = (adjustment.get_upper() - adjustment.get_value ()) / ((adjustment.get_upper() - adjustment.get_lower()));
- return (int) floor (view.height * (1.0 - fract));
+ return (_orien == VERT) ? (int)floor (span * (1.0 - fract)) : (int)floor (span * fract);
}
+
diff --git a/libs/gtkmm2ext/slider_controller.cc b/libs/gtkmm2ext/slider_controller.cc
index 3e2b42f409..93dfb27ae2 100644
--- a/libs/gtkmm2ext/slider_controller.cc
+++ b/libs/gtkmm2ext/slider_controller.cc
@@ -29,11 +29,11 @@ using namespace Gtkmm2ext;
using namespace PBD;
SliderController::SliderController (Glib::RefPtr<Gdk::Pixbuf> image,
- Gtk::Adjustment *adj,
+ Gtk::Adjustment *adj, int orientation,
Controllable& c,
bool with_numeric)
- : PixFader (image, *adj),
+ : PixFader (image, *adj, orientation),
binding_proxy (c),
spin (*adj, 0, 2)
{
@@ -63,7 +63,7 @@ VSliderController::VSliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Controllable& control,
bool with_numeric)
- : SliderController (image, adj, control, with_numeric)
+ : SliderController (image, adj, VERT, control, with_numeric)
{
if (with_numeric) {
spin_frame.add (spin);
@@ -79,7 +79,7 @@ HSliderController::HSliderController (Glib::RefPtr<Gdk::Pixbuf> image,
Controllable& control,
bool with_numeric)
- : SliderController (image, adj, control, with_numeric)
+ : SliderController (image, adj, HORIZ, control, with_numeric)
{
if (with_numeric) {
spin_frame.add (spin);
diff --git a/libs/gtkmm2ext/sync-menu.c b/libs/gtkmm2ext/sync-menu.c
new file mode 100644
index 0000000000..894446c424
--- /dev/null
+++ b/libs/gtkmm2ext/sync-menu.c
@@ -0,0 +1,977 @@
+/* GTK+ Integration for the Mac OS X Menubar.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ * Copyright (C) 2007 Imendio AB
+ *
+ * For further information, see:
+ * http://developer.imendio.com/projects/gtk-macosx/menubar
+ *
+ * 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.
+ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <Carbon/Carbon.h>
+
+#include <gtkmm2ext/sync-menu.h>
+
+
+/* TODO
+ *
+ * - Sync adding/removing/reordering items
+ * - Create on demand? (can this be done with gtk+? ie fill in menu
+ items when the menu is opened)
+ * - Figure out what to do per app/window...
+ *
+ */
+
+#define IGE_QUARTZ_MENU_CREATOR 'IGEC'
+#define IGE_QUARTZ_ITEM_WIDGET 'IWID'
+
+
+static void sync_menu_shell (GtkMenuShell *menu_shell,
+ MenuRef carbon_menu,
+ gboolean toplevel,
+ gboolean debug);
+
+
+/*
+ * 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;
+}
+
+
+/*
+ * CarbonMenu functions
+ */
+
+typedef struct
+{
+ MenuRef menu;
+} 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)
+{
+ 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)
+{
+ 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;
+}
+
+
+/*
+ * CarbonMenuItem functions
+ */
+
+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)
+{
+ if (menu_item->accel_closure)
+ g_closure_unref (menu_item->accel_closure);
+
+ g_slice_free (CarbonMenuItem, menu_item);
+}
+
+static CarbonMenuItem *
+carbon_menu_item_get (GtkWidget *widget)
+{
+ return g_object_get_qdata (G_OBJECT (widget), carbon_menu_item_quark);
+}
+
+static void
+carbon_menu_item_update_state (CarbonMenuItem *carbon_item,
+ GtkWidget *widget)
+{
+ gboolean sensitive;
+ gboolean visible;
+ UInt32 set_attrs = 0;
+ UInt32 clear_attrs = 0;
+
+ 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;
+
+ ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
+ set_attrs, clear_attrs);
+}
+
+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)
+{
+ GtkWidget *submenu;
+
+ submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+
+ if (submenu)
+ {
+ const gchar *label_text;
+ CFStringRef cfstr = NULL;
+
+ label_text = get_menu_label_text (widget, NULL);
+ if (label_text)
+ cfstr = CFStringCreateWithCString (NULL, label_text,
+ kCFStringEncodingUTF8);
+
+ CreateNewMenu (0, 0, &carbon_item->submenu);
+ SetMenuTitleWithCFString (carbon_item->submenu, cfstr);
+ SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
+ carbon_item->submenu);
+
+ sync_menu_shell (GTK_MENU_SHELL (submenu), carbon_item->submenu, FALSE, FALSE);
+
+ if (cfstr)
+ CFRelease (cfstr);
+ }
+ else
+ {
+ SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index,
+ NULL);
+ carbon_item->submenu = NULL;
+ }
+}
+
+static void
+carbon_menu_item_update_label (CarbonMenuItem *carbon_item,
+ GtkWidget *widget)
+{
+ const gchar *label_text;
+ CFStringRef cfstr = NULL;
+
+ label_text = get_menu_label_text (widget, NULL);
+ if (label_text)
+ cfstr = CFStringCreateWithCString (NULL, label_text,
+ kCFStringEncodingUTF8);
+
+ SetMenuItemTextWithCFString (carbon_item->menu, carbon_item->index,
+ cfstr);
+
+ if (cfstr)
+ CFRelease (cfstr);
+}
+
+static void
+carbon_menu_item_update_accelerator (CarbonMenuItem *carbon_item,
+ GtkWidget *widget)
+{
+ GtkWidget *label;
+
+ get_menu_label_text (widget, &label);
+
+ if (GTK_IS_ACCEL_LABEL (label) &&
+ GTK_ACCEL_LABEL (label)->accel_closure)
+ {
+ GtkAccelKey *key;
+
+ key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group,
+ accel_find_func,
+ GTK_ACCEL_LABEL (label)->accel_closure);
+
+ if (key &&
+ key->accel_key &&
+ key->accel_flags & GTK_ACCEL_VISIBLE)
+ {
+ GdkDisplay *display = gtk_widget_get_display (widget);
+ GdkKeymap *keymap = gdk_keymap_get_for_display (display);
+ GdkKeymapKey *keys;
+ gint n_keys;
+ gint use_command;
+ gboolean add_modifiers = FALSE;
+
+ if (gdk_keymap_get_entries_for_keyval (keymap, key->accel_key,
+ &keys, &n_keys) == 0)
+ {
+ gint realkey = -1;
+
+ switch (key->accel_key) {
+ case GDK_rightarrow:
+ case GDK_Right:
+ realkey = kRightArrowCharCode;
+ break;
+ case GDK_leftarrow:
+ case GDK_Left:
+ realkey = kLeftArrowCharCode;
+ break;
+ case GDK_uparrow:
+ case GDK_Up:
+ realkey = kUpArrowCharCode;
+ break;
+ case GDK_downarrow:
+ case GDK_Down:
+ realkey = kDownArrowCharCode;
+ break;
+ default:
+ break;
+ }
+
+ if (realkey != -1) {
+ SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
+ false, realkey);
+ add_modifiers = TRUE;
+ }
+
+ } else {
+ SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
+ true, keys[0].keycode);
+ g_free (keys);
+ add_modifiers = TRUE;
+ }
+
+ if (add_modifiers)
+ {
+ UInt8 modifiers = 0; /* implies Command key */
+
+ use_command = 0;
+
+ if (key->accel_mods)
+ {
+ if (key->accel_mods & GDK_SHIFT_MASK) {
+ modifiers |= kMenuShiftModifier;
+ }
+
+ /* gdk/quartz maps Alt/Option to Mod1 */
+
+ if (key->accel_mods & (GDK_MOD1_MASK)) {
+ modifiers |= kMenuOptionModifier;
+ }
+
+ if (key->accel_mods & GDK_CONTROL_MASK) {
+ modifiers |= kMenuControlModifier;
+ }
+
+ /* gdk/quartz maps Command to Meta */
+
+ if (key->accel_mods & GDK_META_MASK) {
+ use_command = 1;
+ }
+ }
+
+ if (!use_command)
+ modifiers |= kMenuNoCommandModifier;
+
+ SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
+ modifiers);
+
+ return;
+ }
+ }
+ }
+
+ /* otherwise, clear the menu shortcut */
+ SetMenuItemModifiers (carbon_item->menu, carbon_item->index,
+ kMenuNoModifiers | kMenuNoCommandModifier);
+ ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index,
+ 0, kMenuItemAttrUseVirtualKey);
+ SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
+ false, 0);
+}
+
+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;
+
+ get_menu_label_text (widget, &label);
+
+ if (GTK_IS_ACCEL_LABEL (label) &&
+ GTK_ACCEL_LABEL (label)->accel_closure == 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 (label)->accel_closure;
+
+ 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));
+ }
+}
+
+static void
+carbon_menu_item_notify_label (GObject *object,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ CarbonMenuItem *carbon_item = carbon_menu_item_get (GTK_WIDGET (object));
+
+ 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 (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;
+}
+
+
+/*
+ * carbon event handler
+ */
+
+static int _in_carbon_menu_event_handler = 0;
+
+int
+gdk_quartz_in_carbon_menu_event_handler ()
+{
+ return _in_carbon_menu_event_handler;
+}
+
+static gboolean
+dummy_gtk_menu_item_activate (gpointer *arg)
+{
+ gtk_menu_item_activate (GTK_MENU_ITEM(arg));
+ return FALSE;
+}
+
+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);
+ MenuRef menu_ref;
+ OSStatus ret;
+
+ _in_carbon_menu_event_handler = 1;
+
+ switch (event_class)
+ {
+ case kEventClassCommand:
+ /* This is called when activating (is that the right GTK+ term?)
+ * a menu item.
+ */
+ if (event_kind == kEventCommandProcess)
+ {
+ HICommand command;
+ OSStatus err;
+
+ /*g_printerr ("Menu: kEventClassCommand/kEventCommandProcess\n");*/
+
+ err = GetEventParameter (event_ref, kEventParamDirectObject,
+ typeHICommand, 0,
+ sizeof (command), 0, &command);
+
+ if (err == noErr)
+ {
+ GtkWidget *widget = NULL;
+
+ /* 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 && GTK_IS_WIDGET (widget))
+ {
+ g_idle_add (dummy_gtk_menu_item_activate, widget);
+ // gtk_menu_item_activate (GTK_MENU_ITEM (widget));
+ _in_carbon_menu_event_handler = 0;
+ return noErr;
+ }
+ }
+ }
+ break;
+
+ case kEventClassMenu:
+ GetEventParameter (event_ref,
+ kEventParamDirectObject,
+ typeMenuRef,
+ NULL,
+ sizeof (menu_ref),
+ NULL,
+ &menu_ref);
+
+ switch (event_kind)
+ {
+ case kEventMenuTargetItem:
+ /* This is called when an item is selected (what is the
+ * GTK+ term? prelight?)
+ */
+ /*g_printerr ("kEventClassMenu/kEventMenuTargetItem\n");*/
+ break;
+
+ case kEventMenuOpening:
+ /* Is it possible to dynamically build the menu here? We
+ * can at least set visibility/sensitivity.
+ */
+ /*g_printerr ("kEventClassMenu/kEventMenuOpening\n");*/
+ break;
+
+ case kEventMenuClosed:
+ /*g_printerr ("kEventClassMenu/kEventMenuClosed\n");*/
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ ret = CallNextEventHandler (event_handler_call_ref, event_ref);
+ _in_carbon_menu_event_handler = 0;
+ return ret;
+}
+
+static void
+setup_menu_event_handler (void)
+{
+ EventHandlerUPP menu_event_handler_upp;
+ EventHandlerRef menu_event_handler_ref;
+ const EventTypeSpec menu_events[] = {
+ { kEventClassCommand, kEventCommandProcess },
+ { kEventClassMenu, kEventMenuTargetItem },
+ { kEventClassMenu, kEventMenuOpening },
+ { kEventClassMenu, kEventMenuClosed }
+ };
+
+ /* FIXME: We might have to install one per window? */
+
+ menu_event_handler_upp = NewEventHandlerUPP (menu_event_handler_func);
+ InstallEventHandler (GetApplicationEventTarget (), menu_event_handler_upp,
+ GetEventTypeCount (menu_events), menu_events, 0,
+ &menu_event_handler_ref);
+
+#if 0
+ /* FIXME: Remove the handler with: */
+ RemoveEventHandler(menu_event_handler_ref);
+ DisposeEventHandlerUPP(menu_event_handler_upp);
+#endif
+}
+
+static void
+sync_menu_shell (GtkMenuShell *menu_shell,
+ MenuRef carbon_menu,
+ gboolean toplevel,
+ gboolean debug)
+{
+ GList *children;
+ GList *l;
+ MenuItemIndex carbon_index = 1;
+
+ if (debug)
+ g_printerr ("%s: syncing shell %p\n", G_STRFUNC, menu_shell);
+
+ carbon_menu_connect (GTK_WIDGET (menu_shell), carbon_menu);
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu_shell));
+
+ for (l = children; l; l = l->next)
+ {
+ GtkWidget *menu_item = l->data;
+ CarbonMenuItem *carbon_item;
+
+ if (GTK_IS_TEAROFF_MENU_ITEM (menu_item))
+ continue;
+
+ if (toplevel && g_object_get_data (G_OBJECT (menu_item),
+ "gtk-empty-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, get_menu_label_text (menu_item, NULL),
+ g_type_name (G_TYPE_FROM_INSTANCE (menu_item)));
+
+ if (carbon_item && carbon_item->index != carbon_index)
+ {
+ if (debug)
+ g_printerr ("%s: -> not matching, deleting\n", G_STRFUNC);
+
+ DeleteMenuItem (carbon_item->menu, carbon_index);
+ carbon_item = NULL;
+ }
+
+ if (!carbon_item)
+ {
+ GtkWidget *label = NULL;
+ const gchar *label_text;
+ CFStringRef cfstr = NULL;
+ MenuItemAttributes attributes = 0;
+
+ if (debug)
+ g_printerr ("%s: -> creating new\n", G_STRFUNC);
+
+ label_text = get_menu_label_text (menu_item, &label);
+ if (label_text)
+ cfstr = CFStringCreateWithCString (NULL, label_text,
+ kCFStringEncodingUTF8);
+
+ if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
+ attributes |= kMenuItemAttrSeparator;
+
+ if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
+ attributes |= kMenuItemAttrDisabled;
+
+ if (!GTK_WIDGET_VISIBLE (menu_item))
+ attributes |= kMenuItemAttrHidden;
+
+ InsertMenuItemTextWithCFString (carbon_menu, cfstr,
+ carbon_index - 1,
+ attributes, 0);
+ SetMenuItemProperty (carbon_menu, carbon_index,
+ IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET,
+ sizeof (menu_item), &menu_item);
+
+ if (cfstr)
+ CFRelease (cfstr);
+
+ carbon_item = carbon_menu_item_connect (menu_item, label,
+ carbon_menu,
+ carbon_index);
+
+ 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);
+ }
+
+ carbon_index++;
+ }
+
+ g_list_free (children);
+}
+
+
+static gulong emission_hook_id = 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);
+
+ if (GTK_IS_MENU_ITEM (instance))
+ {
+ GtkWidget *previous_parent = g_value_get_object (param_values + 1);
+ GtkWidget *menu_shell = NULL;
+
+ if (GTK_IS_MENU_SHELL (previous_parent))
+ {
+ menu_shell = previous_parent;
+ }
+ else if (GTK_IS_MENU_SHELL (instance->parent))
+ {
+ menu_shell = instance->parent;
+ }
+
+ if (menu_shell)
+ {
+ CarbonMenu *carbon_menu = carbon_menu_get (menu_shell);
+
+ if (carbon_menu)
+ {
+#if 0
+ g_printerr ("%s: item %s %p (%s, %s)\n", G_STRFUNC,
+ previous_parent ? "removed from" : "added to",
+ menu_shell,
+ get_menu_label_text (instance, NULL),
+ g_type_name (G_TYPE_FROM_INSTANCE (instance)));
+#endif
+
+ sync_menu_shell (GTK_MENU_SHELL (menu_shell),
+ carbon_menu->menu,
+ carbon_menu->menu == (MenuRef) data,
+ FALSE);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+parent_set_emission_hook_remove (GtkWidget *widget,
+ gpointer data)
+{
+ g_signal_remove_emission_hook (g_signal_lookup ("parent-set",
+ GTK_TYPE_WIDGET),
+ emission_hook_id);
+}
+
+
+/*
+ * public functions
+ */
+
+void
+ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell)
+{
+ MenuRef carbon_menubar;
+
+ 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");
+
+ CreateNewMenu (0 /*id*/, 0 /*options*/, &carbon_menubar);
+ SetRootMenu (carbon_menubar);
+
+ setup_menu_event_handler ();
+
+ emission_hook_id =
+ g_signal_add_emission_hook (g_signal_lookup ("parent-set",
+ GTK_TYPE_WIDGET),
+ 0,
+ parent_set_emission_hook,
+ carbon_menubar, NULL);
+
+ g_signal_connect (menu_shell, "destroy",
+ G_CALLBACK (parent_set_emission_hook_remove),
+ NULL);
+
+ sync_menu_shell (menu_shell, carbon_menubar, TRUE, FALSE);
+}
+
+void
+ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item)
+{
+ MenuRef appmenu;
+ MenuItemIndex index;
+
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ if (GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
+ &appmenu, &index) == noErr)
+ {
+ SetMenuItemCommandID (appmenu, index, 0);
+ SetMenuItemProperty (appmenu, index,
+ IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET,
+ sizeof (menu_item), &menu_item);
+
+ gtk_widget_hide (GTK_WIDGET (menu_item));
+ }
+}
+
+
+struct _IgeMacMenuGroup
+{
+ GList *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;
+}
+
+void
+ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group,
+ GtkMenuItem *menu_item,
+ const gchar *label)
+{
+ MenuRef appmenu;
+ GList *list;
+ gint index = 0;
+
+ g_return_if_fail (group != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ if (GetIndMenuItemWithCommandID (NULL, kHICommandHide, 1,
+ &appmenu, NULL) != noErr)
+ {
+ g_warning ("%s: retrieving app menu failed",
+ G_STRFUNC);
+ return;
+ }
+
+ 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)
+ {
+ CFStringRef cfstr;
+
+ /* add a separator before adding the first item, but not
+ * for the first group
+ */
+ if (!group->items && list->prev)
+ {
+ InsertMenuItemTextWithCFString (appmenu, NULL, index,
+ kMenuItemAttrSeparator, 0);
+ index++;
+ }
+
+ if (!label)
+ label = get_menu_label_text (GTK_WIDGET (menu_item), NULL);
+
+ cfstr = CFStringCreateWithCString (NULL, label,
+ kCFStringEncodingUTF8);
+
+ InsertMenuItemTextWithCFString (appmenu, cfstr, index, 0, 0);
+ SetMenuItemProperty (appmenu, index + 1,
+ IGE_QUARTZ_MENU_CREATOR,
+ IGE_QUARTZ_ITEM_WIDGET,
+ sizeof (menu_item), &menu_item);
+
+ CFRelease (cfstr);
+
+ gtk_widget_hide (GTK_WIDGET (menu_item));
+
+ group->items = g_list_append (group->items, menu_item);
+
+ return;
+ }
+ }
+
+ if (!list)
+ g_warning ("%s: app menu group %p does not exist",
+ G_STRFUNC, group);
+}