changeset 3492:1f250eb90dfe trunk

Merge.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 03 Sep 2007 10:41:19 +0300
parents 899a7ed37a70 (current diff) 18cffff8eaa0 (diff)
children 62b78f2aa450
files src/audacious/input.c
diffstat 11 files changed, 895 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Sat Sep 01 16:13:17 2007 +0300
+++ b/configure.ac	Mon Sep 03 10:41:19 2007 +0300
@@ -186,7 +186,7 @@
 case "$target" in
 	*-apple-*)
 		AC_MSG_RESULT([yes, sorry you poor bastard])
-		LDFLAGS="$LDFLAGS -Wl,-framework -Wl,CoreFoundation -Wl,-framework -Wl,CoreServices"
+		LDFLAGS="$LDFLAGS -framework CoreFoundation -framework CoreServices -framework Carbon"
 		;;
 	*)
 		AC_MSG_RESULT([no])
--- a/src/audacious/Makefile	Sat Sep 01 16:13:17 2007 +0300
+++ b/src/audacious/Makefile	Mon Sep 03 10:41:19 2007 +0300
@@ -144,6 +144,7 @@
 	vfs_buffered_file.c \
 	vfs_common.c \
 	visualization.c \
+	sync-menu.c \
 	xconvert.c
 
 LIBDEP = ../libguess/libguess.a
--- a/src/audacious/input.c	Sat Sep 01 16:13:17 2007 +0300
+++ b/src/audacious/input.c	Mon Sep 03 10:41:19 2007 +0300
@@ -586,66 +586,6 @@
         playback->plugin->set_eq(on, preamp, bands);
 }
 
-void
-input_get_song_info(const gchar * filename, gchar ** title, gint * length)
-{
-    InputPlugin *ip = NULL;
-    Tuple *tuple;
-    gchar *tmp = NULL, *ext;
-    gchar *filename_proxy;
-    ProbeResult *pr;
-
-    g_return_if_fail(filename != NULL);
-    g_return_if_fail(title != NULL);
-    g_return_if_fail(length != NULL);
-
-    filename_proxy = g_strdup(filename);
-
-    pr = input_check_file(filename_proxy, FALSE);
-
-    if (!pr)
-        return;
-
-    ip = pr->ip;
-
-    g_free(pr);
-
-    if (ip && ip->get_song_info) {
-        ip->get_song_info(filename_proxy, &tmp, length);
-        *title = str_to_utf8(tmp);
-        g_free(tmp);
-    }
-    else {
-        tuple = tuple_new();
-
-        tmp = g_strdup(filename);
-        if ((ext = strrchr(tmp, '.')))
-            *ext = '\0';
-
-        tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, g_path_get_basename(tmp));
-
-        if (ext)
-            tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, ext + 1);
-
-        tuple_associate_string(tuple, FIELD_FILE_PATH, NULL, g_path_get_dirname(tmp));
-
-        tmp = tuple_formatter_process_string(tuple, get_gentitle_format());
-        if (tmp != NULL && *tmp != '\0') {
-            (*title) = str_to_utf8(tmp);
-            g_free(tmp);
-        }
-        else {
-            (*title) = filename_to_utf8(tuple_get_string(tuple, FIELD_FILE_NAME, NULL));
-        }
-
-        (*length) = -1;
-
-        mowgli_object_unref(tuple);
-        tuple = NULL;
-    }
-
-    g_free(filename_proxy);
-}
 
 Tuple *
 input_get_song_tuple(const gchar * filename)
--- a/src/audacious/input.h	Sat Sep 01 16:13:17 2007 +0300
+++ b/src/audacious/input.h	Mon Sep 03 10:41:19 2007 +0300
@@ -61,8 +61,6 @@
 gint input_get_time(void);
 void input_set_eq(gint on, gfloat preamp, gfloat * bands);
 void input_seek(gint time);
-void input_get_song_info(const gchar * filename, gchar ** title,
-                         gint * length);
 
 guchar *input_get_vis(gint time);
 void input_update_vis_plugin(gint time);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/sync-menu.c	Mon Sep 03 10:41:19 2007 +0300
@@ -0,0 +1,715 @@
+/* GTK+ Integration for the Mac OS X Menubar.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the 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.
+ */
+
+#include <gtk/gtk.h>
+
+#ifdef GDK_WINDOWING_QUARTZ
+
+#include <gdk/gdkkeysyms.h>
+
+#include <Carbon/Carbon.h>
+
+#include "sync-menu.h"
+
+
+/* TODO
+ *
+ * - Setup shortcuts, possibly transforming ctrl->cmd
+ * - Sync menus
+ * - 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...
+ * - Toggle/radio items
+ *
+ */
+
+#define GTK_QUARTZ_MENU_CREATOR 'GTKC'
+#define GTK_QUARTZ_ITEM_WIDGET  'GWID'
+
+
+static void   sync_menu_shell (GtkMenuShell *menu_shell,
+			       MenuRef       carbon_menu,
+			       gboolean      toplevel);
+
+
+/*
+ * 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)
+{
+  *label = find_menu_label (menu_item);
+  if (!*label)
+    return NULL;
+
+  return gtk_label_get_text (GTK_LABEL (*label));
+}
+
+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)
+    {
+      GtkWidget   *label = NULL;
+      const gchar *label_text;
+      CFStringRef  cfstr = NULL;
+
+      label_text = get_menu_label_text (widget, &label);
+      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);
+
+      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)
+{
+  GtkWidget   *label;
+  const gchar *label_text;
+  CFStringRef  cfstr = NULL;
+
+  label_text = get_menu_label_text (widget, &label);
+  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;
+
+	  if (gdk_keymap_get_entries_for_keyval (keymap, key->accel_key,
+						 &keys, &n_keys))
+	    {
+	      UInt8 modifiers = 0;
+
+	      SetMenuItemCommandKey (carbon_item->menu, carbon_item->index,
+				     true, keys[0].keycode);
+
+	      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;
+		}
+
+	      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 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;
+
+  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_print ("Menu: kEventClassCommand/kEventCommandProcess\n");
+
+	  err = GetEventParameter (event_ref, kEventParamDirectObject, 
+				   typeHICommand, 0, 
+				   sizeof (command), 0, &command);
+
+	  if (err == noErr)
+	    {
+	      GtkWidget *widget = NULL;
+              
+	      if (command.commandID == kHICommandQuit)
+		{
+		  gtk_main_quit (); /* Just testing... */
+		  return noErr;
+		}
+	      
+	      /* Get any GtkWidget associated with the item. */
+	      err = GetMenuItemProperty (command.menu.menuRef, 
+					 command.menu.menuItemIndex, 
+					 GTK_QUARTZ_MENU_CREATOR, 
+					 GTK_QUARTZ_ITEM_WIDGET,
+					 sizeof (widget), 0, &widget);
+	      if (err == noErr && widget)
+		{
+		  gtk_menu_item_activate (GTK_MENU_ITEM (widget));
+		  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_print ("kEventClassMenu/kEventMenuTargetItem\n");
+	  break;
+
+	case kEventMenuOpening:
+	  /* Is it possible to dynamically build the menu here? We
+	   * can at least set visibility/sensitivity. 
+	   */
+	  //g_print ("kEventClassMenu/kEventMenuOpening\n");
+	  break;
+	    
+	case kEventMenuClosed:
+	  //g_print ("kEventClassMenu/kEventMenuClosed\n");
+	  break;
+
+	default:
+	  break;
+	}
+
+      break;
+	
+    default:
+      break;
+    }
+
+  return CallNextEventHandler (event_handler_call_ref, event_ref);
+}
+
+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)
+{
+  GList         *children;
+  GList         *l;
+  MenuItemIndex  carbon_index = 1;
+
+  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 (carbon_item && carbon_item->index != carbon_index)
+	{
+	  DeleteMenuItem (carbon_item->menu,
+			  carbon_item->index);
+	  carbon_item = NULL;
+	}
+
+      if (!carbon_item)
+	{
+	  GtkWidget          *label      = NULL;
+	  const gchar        *label_text;
+	  CFStringRef         cfstr      = NULL;
+	  MenuItemAttributes  attributes = 0;
+
+	  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,
+					  attributes, 0);
+	  SetMenuItemProperty (carbon_menu, carbon_index,
+			       GTK_QUARTZ_MENU_CREATOR,
+			       GTK_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);
+}
+
+void
+sync_menu_takeover_menu (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 ();
+  
+  sync_menu_shell (menu_shell, carbon_menubar, TRUE);
+}
+
+#else
+
+void
+sync_menu_takeover_menu (GtkMenuShell *menu_shell)
+{
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/sync-menu.h	Mon Sep 03 10:41:19 2007 +0300
@@ -0,0 +1,27 @@
+/* GTK+ Integration for the Mac OS X Menubar.
+ *
+ * Copyright (C) 2007 Pioneer Research Center USA, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the 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.
+ */
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void sync_menu_takeover_menu (GtkMenuShell *menu_shell);
+
+G_END_DECLS
--- a/src/audacious/ui/Makefile	Sat Sep 01 16:13:17 2007 +0300
+++ b/src/audacious/ui/Makefile	Mon Sep 03 10:41:19 2007 +0300
@@ -4,6 +4,7 @@
 gladexmldir = $(pkgdatadir)/ui
 
 OBJECTIVE_DATA = \
+	carbon-menubar.ui:$(gladexmldir) \
 	mainwin.ui:$(gladexmldir) \
 	playlist.ui:$(gladexmldir) \
 	equalizer.ui:$(gladexmldir)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/ui/carbon-menubar.ui	Mon Sep 03 10:41:19 2007 +0300
@@ -0,0 +1,128 @@
+<?xml version="1.0" standalone="yes" encoding="utf-8" ?>
+
+<ui>
+	<menubar name="carbon-menubar">
+		<menu action="dummy" name="main-menu">
+			<menu action="file">
+				<menuitem action="play file" />
+				<menuitem action="play location" />
+        			<separator />
+				<menuitem action="preferences" />
+				<menuitem action="quit" />
+			</menu>
+			<menu action="playback">
+				<menuitem action="playback repeat" />
+				<menuitem action="playback shuffle" />
+				<menuitem action="playback no playlist advance" />
+				<menuitem action="stop after current song" />
+				<separator />
+				<menuitem action="playback play" />
+				<menuitem action="playback pause" />
+				<menuitem action="playback stop" />
+				<menuitem action="playback previous" />
+				<menuitem action="playback next" />
+				<separator />
+				<menuitem action="jump to playlist start" />
+				<separator />
+				<menuitem action="ab set" />
+				<menuitem action="ab clear" />
+				<separator />
+				<menuitem action="jump to file" />
+				<menuitem action="jump to time" />
+				<separator />
+				<menuitem action="current track info" />
+			</menu>
+			<menu action="playlist">
+				<menuitem action="playlist new" />
+				<menuitem action="playlist delete" />
+				<separator />
+				<menuitem action="playlist select next" />
+				<menuitem action="playlist select previous" />
+				<separator />
+				<menuitem action="playlist load" />
+				<menuitem action="playlist save" />
+				<menuitem action="playlist save default" />
+				<separator />
+				<menuitem action="playlist manager" />
+				<separator />
+				<menuitem action="playlist refresh" />
+				<separator />
+<!--				<menuitem action="playlist delete" /> -->
+			</menu>
+			<menu action="view">
+				<menuitem action="show player" />
+				<menuitem action="show playlist editor" />
+				<menuitem action="show equalizer" />
+				<separator />
+				<menuitem action="view time elapsed" />
+				<menuitem action="view time remaining" />
+				<separator />
+				<menuitem action="view always on top" />
+				<menuitem action="view put on all workspaces" />
+				<menuitem action="autoscroll songname" />
+				<separator />
+				<menuitem action="roll up player" />
+				<menuitem action="roll up playlist editor" />
+				<menuitem action="roll up equalizer" />
+				<separator />
+				<menuitem action="view doublesize" />
+				<menuitem action="view easy move" />
+			</menu>
+			<menu action="visualization">
+				<menu action="vismode">
+					<menuitem action="vismode analyzer" />
+					<menuitem action="vismode scope" />
+					<menuitem action="vismode voiceprint" />
+					<menuitem action="vismode off" />
+				</menu>
+				<menu action="anamode">
+					<menuitem action="anamode normal" />
+					<menuitem action="anamode fire" />
+					<menuitem action="anamode vertical lines" />
+					<separator />
+					<menuitem action="anatype lines" />
+					<menuitem action="anatype bars" />
+					<separator />
+					<menuitem action="anamode peaks" />
+				</menu>
+				<menu action="scomode">
+					<menuitem action="scomode dot" />
+					<menuitem action="scomode line" />
+					<menuitem action="scomode solid" />
+				</menu>
+				<menu action="vprmode">
+					<menuitem action="vprmode normal" />
+					<menuitem action="vprmode fire" />
+					<menuitem action="vprmode ice" />
+				</menu>
+				<menu action="wshmode">
+					<menuitem action="wshmode normal" />
+					<menuitem action="wshmode smooth" />
+				</menu>
+				<menu action="refrate">
+					<menuitem action="refrate full" />
+					<menuitem action="refrate half" />
+					<menuitem action="refrate quarter" />
+					<menuitem action="refrate eighth" />
+				</menu>
+				<menu action="anafoff">
+					<menuitem action="anafoff slowest" />
+					<menuitem action="anafoff slow" />
+					<menuitem action="anafoff medium" />
+					<menuitem action="anafoff fast" />
+					<menuitem action="anafoff fastest" />
+				</menu>
+				<menu action="peafoff">
+					<menuitem action="peafoff slowest" />
+					<menuitem action="peafoff slow" />
+					<menuitem action="peafoff medium" />
+					<menuitem action="peafoff fast" />
+					<menuitem action="peafoff fastest" />
+				</menu>
+			</menu>
+			<menu action="help">
+				<menuitem action="about audacious" />
+			</menu>
+		</menu>
+	</menubar>
+</ui>
--- a/src/audacious/ui_fileopener.c	Sat Sep 01 16:13:17 2007 +0300
+++ b/src/audacious/ui_fileopener.c	Mon Sep 03 10:41:19 2007 +0300
@@ -47,12 +47,6 @@
             playlist_add(playlist, filename ? filename : (const gchar *) cur->data);
         }       
 
-        if (++ctr == 20) {
-            playlistwin_update_list(playlist);
-            ctr = 0;
-            while (gtk_events_pending() ) gtk_main_iteration();
-        }
-
         g_free(filename);
     } 
 
--- a/src/audacious/ui_manager.c	Sat Sep 01 16:13:17 2007 +0300
+++ b/src/audacious/ui_manager.c	Mon Sep 03 10:41:19 2007 +0300
@@ -35,6 +35,7 @@
 
 #include "icons-stock.h"
 
+#include "sync-menu.h"
 
 static GtkUIManager *ui_manager = NULL;
 static gboolean menu_created = FALSE;
@@ -375,6 +376,10 @@
 
 	{ "dummy", NULL, "dummy" },
 
+        /* XXX Carbon support */
+        { "file", NULL, N_("File") },
+        { "help", NULL, N_("Help") },
+
 	{ "plugins-menu", NULL, N_("Plugin Services") },
 
 	{ "current track info", AUD_STOCK_INFO , N_("View Track Details"), "I",
@@ -625,6 +630,9 @@
   return;
 }
 
+#ifdef GDK_WINDOWING_QUARTZ
+static GtkWidget *carbon_menubar;
+#endif
 
 static void
 ui_manager_create_menus_init_pmenu( gchar * path )
@@ -667,6 +675,20 @@
   /* initialize plugins-menu for mainwin-menus */
   ui_manager_create_menus_init_pmenu( "/mainwin-menus/main-menu/plugins-menu" );
 
+#ifdef GDK_WINDOWING_QUARTZ
+  gtk_ui_manager_add_ui_from_file( ui_manager , DATA_DIR "/ui/carbon-menubar.ui" , &gerr );
+
+  if ( gerr != NULL )
+  {
+    g_critical( "Error creating UI<ui/carbon-menubar.ui>: %s" , gerr->message );
+    g_error_free( gerr );
+    return;
+  }
+
+  carbon_menubar = ui_manager_get_popup_menu( ui_manager , "/carbon-menubar/main-menu" );
+  sync_menu_takeover_menu(GTK_MENU_SHELL(carbon_menubar));
+#endif
+
   gtk_ui_manager_add_ui_from_file( ui_manager , DATA_DIR "/ui/playlist.ui" , &gerr );
 
   if ( gerr != NULL )
--- a/src/audacious/ui_skinselector.c	Sat Sep 01 16:13:17 2007 +0300
+++ b/src/audacious/ui_skinselector.c	Mon Sep 03 10:41:19 2007 +0300
@@ -316,9 +316,6 @@
             iter_current_skin = iter;
             have_current_skin = TRUE;
         }
-
-        while (gtk_events_pending())
-            gtk_main_iteration();
     }
 
     if (have_current_skin) {