← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~cjcurran/indicator-sound/custom_player_controls into lp:indicator-sound

 

Conor Curran has proposed merging lp:~cjcurran/indicator-sound/custom_player_controls into lp:indicator-sound.

Requested reviews:
  Indicator Applet Developers (indicator-applet-developers)


UI elements for basic player control underway
-- 
https://code.launchpad.net/~cjcurran/indicator-sound/custom_player_controls/+merge/27612
Your team ayatana-commits is subscribed to branch lp:indicator-sound.
=== modified file 'src/Makefile.am'
--- src/Makefile.am	2010-05-20 17:50:44 +0000
+++ src/Makefile.am	2010-06-15 10:35:38 +0000
@@ -9,6 +9,10 @@
 libsoundmenu_la_SOURCES = \
   common-defs.h \
 	indicator-sound.h \
+	transport-widget.c \
+	transport-widget.h \
+	metadata-widget.c \
+	metadata-widget.h \
 	indicator-sound.c \
 	dbus-shared-names.h \
   sound-service-client.h 
@@ -47,7 +51,10 @@
 # libsoundmenu vala
 #####################
 music_bridge_VALASOURCES = \
-	music-player-bridge.vala 
+	music-player-bridge.vala \
+	transport-menu-item.vala \
+	metadata-menu-item.vala \
+	player-controller.vala
 
 music_bridge_VALAFLAGS = \
   --ccode \
@@ -87,7 +94,7 @@
   slider-menu-item.c \
   $(music_bridge_VALASOURCES:.vala=.c)
 
-indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) $(SOUNDSERVICE_CFLAGS) $(GCONF_CFLAGS) -DLIBEXECDIR=\"$(libexecdir)\" -Wall -Werror
+indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) $(SOUNDSERVICE_CFLAGS) $(GCONF_CFLAGS) -DLIBEXECDIR=\"$(libexecdir)\" -Wall
 indicator_sound_service_LDADD = $(PULSEAUDIO_LIBS) $(SOUNDSERVICE_LIBS) $(GCONF_LIBS)
 
 #########################

=== modified file 'src/common-defs.h'
--- src/common-defs.h	2010-03-08 19:12:18 +0000
+++ src/common-defs.h	2010-06-15 10:35:38 +0000
@@ -24,6 +24,13 @@
 #define SIGNAL_SINK_MUTE_UPDATE "SinkMuteUpdate"
 #define SIGNAL_SINK_AVAILABLE_UPDATE "SinkAvailableUpdate"
 
-// DBUS items
-#define DBUSMENU_SLIDER_MENUITEM_TYPE          "x-canonical-ido-slider-item"
-#define DBUSMENU_SLIDER_MENUITEM_PROP_VOLUME   "volume"
+/* DBUS Custom Items */
+#define DBUSMENU_SLIDER_MENUITEM_TYPE          	"x-canonical-ido-slider-item"
+#define DBUSMENU_TRANSPORT_MENUITEM_TYPE       	"x-canonical-transport-bar"
+#define DBUSMENU_TRANSPORT_MENUITEM_STATE  		 	"x-canonical-transport-state"
+
+#define DBUSMENU_METADATA_MENUITEM_TYPE  				"x-canonical-metadata-menu-item"
+#define DBUSMENU_METADATA_MENUITEM_TEXT_ARTIST  "x-canonical-metadata-text-artist"
+#define DBUSMENU_METADATA_MENUITEM_TEXT_PIECE  	"x-canonical-metadata-text-piece"
+#define DBUSMENU_METADATA_MENUITEM_TEXT_CONTAINER  "x-canonical-metadata-text-container"
+#define DBUSMENU_METADATA_MENUITEM_IMAGE_PATH  			"x-canonical-metadata-image"

=== modified file 'src/indicator-sound.c'
--- src/indicator-sound.c	2010-04-27 10:33:38 +0000
+++ src/indicator-sound.c	2010-06-15 10:35:38 +0000
@@ -38,6 +38,8 @@
 #include <libindicator/indicator-image-helper.h>
 
 #include "indicator-sound.h"
+#include "transport-widget.h"
+#include "metadata-widget.h"
 #include "dbus-shared-names.h"
 #include "sound-service-client.h"
 #include "common-defs.h"
@@ -61,7 +63,7 @@
 //GObject instance struct
 struct _IndicatorSound {
 	IndicatorObject parent;
-        GtkWidget *slider;
+  GtkWidget *slider;
 	IndicatorServiceManager *service;
 };
 // GObject Boiler plate
@@ -91,6 +93,10 @@
 static void slider_released(GtkWidget *widget, gpointer user_data);
 static void style_changed_cb(GtkWidget *widget, gpointer user_data);
 
+//player widgets related
+static gboolean new_transport_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
+static gboolean new_metadata_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
+
 // DBUS communication
 static DBusGProxy *sound_dbus_proxy = NULL;
 static void connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata);
@@ -150,9 +156,9 @@
 	io_class->get_label = get_label;
 	io_class->get_image = get_icon;
 	io_class->get_menu  = get_menu;
-    io_class->scroll    = scroll;
+  io_class->scroll    = scroll;
 
-    design_team_size = gtk_icon_size_register("design-team-size", 22, 22);
+  design_team_size = gtk_icon_size_register("design-team-size", 22, 22);
 
 	return;
 }
@@ -162,17 +168,17 @@
 {
 	self->service = NULL;
 	self->service = indicator_service_manager_new_version(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_VERSION);
-    prepare_state_machine();
-    prepare_blocked_animation();
-    animation_id = 0;
-    blocked_id = 0;
-    initial_mute = FALSE;
-    device_available = TRUE;
-    slider_in_direct_use = FALSE;
-    exterior_vol_update = OUT_OF_RANGE;
+  prepare_state_machine();
+  prepare_blocked_animation();
+  animation_id = 0;
+  blocked_id = 0;
+  initial_mute = FALSE;
+  device_available = TRUE;
+  slider_in_direct_use = FALSE;
+  exterior_vol_update = OUT_OF_RANGE;
 
 	g_signal_connect(G_OBJECT(self->service), INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connection_changed), self);
-    return;
+  return;
 }
 
 static void
@@ -184,9 +190,9 @@
 		g_object_unref(G_OBJECT(self->service));
 		self->service = NULL;
 	}
-    g_hash_table_destroy(volume_states);
+  g_hash_table_destroy(volume_states);
 
-    free_the_animation_list();
+  free_the_animation_list();
 
 	G_OBJECT_CLASS (indicator_sound_parent_class)->dispose (object);
 	return;
@@ -195,11 +201,11 @@
 static void 
 free_the_animation_list()
 {
-    if(blocked_animation_list != NULL){
-        g_list_foreach (blocked_animation_list, (GFunc)g_object_unref, NULL);
-        g_list_free(blocked_animation_list);
-        blocked_animation_list = NULL;
-    }
+  if(blocked_animation_list != NULL){
+    g_list_foreach (blocked_animation_list, (GFunc)g_object_unref, NULL);
+    g_list_free(blocked_animation_list);
+    blocked_animation_list = NULL;
+  }
 }
 
 static void
@@ -217,9 +223,9 @@
 
 static GtkImage *
 get_icon (IndicatorObject * io)
-{
-    gchar* current_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));
-    g_debug("At start-up attempting to set the image to %s", current_name);
+{	
+  gchar* current_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));
+  g_debug("At start-up attempting to set the image to %s", current_name);
 	speaker_image = indicator_image_helper(current_name);
 	gtk_widget_show(GTK_WIDGET(speaker_image));
 	return speaker_image;
@@ -231,16 +237,16 @@
 static GtkMenu *
 get_menu (IndicatorObject * io)
 {
-    DbusmenuGtkMenu *menu = dbusmenu_gtkmenu_new(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_OBJECT);
-    DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(menu);
-
-    g_object_set_data (G_OBJECT (client),
-                       "indicator", io);
-    dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_SLIDER_MENUITEM_TYPE, new_slider_item);
-
-    // register Key-press listening on the menu widget as the slider does not allow this.
-    g_signal_connect(menu, "key-press-event", G_CALLBACK(key_press_cb), NULL);
-    return GTK_MENU(menu);
+  DbusmenuGtkMenu *menu = dbusmenu_gtkmenu_new(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_OBJECT);
+  DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(menu);
+  g_object_set_data (G_OBJECT (client), "indicator", io);
+  dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_SLIDER_MENUITEM_TYPE, new_slider_item);
+	dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_TRANSPORT_MENUITEM_TYPE, new_transport_widget);
+	dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_METADATA_MENUITEM_TYPE, new_metadata_widget);
+
+  // register Key-press listening on the menu widget as the slider does not allow this.
+  g_signal_connect(menu, "key-press-event", G_CALLBACK(key_press_cb), NULL);
+  return GTK_MENU(menu);
 }
 
 static void
@@ -307,6 +313,51 @@
     return TRUE;
 }
 
+static gboolean
+new_transport_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+	g_debug("indicator-sound: new_transport_bar() called ");
+
+	GtkWidget* bar = NULL;
+	
+	g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
+  g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
+
+	bar = transport_widget_new(newitem);
+  GtkMenuItem *menu_transport_bar = GTK_MENU_ITEM(bar);
+
+  dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_transport_bar, parent);
+	
+	gtk_widget_show_all(bar);
+	
+	return TRUE;
+}
+
+static gboolean
+new_metadata_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+	g_debug("indicator-sound: new_metadata_widget");
+
+	GtkWidget* metadata = NULL;
+
+	g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
+  g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
+
+	metadata = metadata_widget_new (newitem);
+	GtkMenuItem *menu_metadata_widget = GTK_MENU_ITEM(metadata);
+	
+  dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_metadata_widget, parent);
+
+	gtk_widget_show_all(metadata);
+
+	return TRUE;
+}
+
+//const gchar* path = dbusmenu_menuitem_property_get(new_item, DBUSMENU_METADATA_MENUITEM_IMAGE_PATH);
+
+//g_debug("New transport bar path = %s", path);
+
+
 static void
 connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata)
 {
@@ -337,11 +388,11 @@
 			dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_AVAILABLE_UPDATE, G_TYPE_BOOLEAN, G_TYPE_INVALID);
 			dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_AVAILABLE_UPDATE, G_CALLBACK(catch_signal_sink_availability_update), NULL, NULL);
 
-            // Ensure we are in a coherent state with the service at start up.
-            // Preserve ordering!
-            fetch_volume_percent_from_dbus();
-            fetch_mute_value_from_dbus();
-            fetch_sink_availability_from_dbus();
+			// Ensure we are in a coherent state with the service at start up.
+			// Preserve ordering!
+			fetch_volume_percent_from_dbus();
+			fetch_mute_value_from_dbus();
+			fetch_sink_availability_from_dbus();
 		}
 
 	} else {
@@ -376,34 +427,34 @@
 static void
 prepare_blocked_animation()
 {
-    gchar* blocked_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT));
-    gchar* muted_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED));
-
-    GtkImage* temp_image = indicator_image_helper(muted_name);
-    GdkPixbuf* mute_buf = gtk_image_get_pixbuf(temp_image);
-
-    temp_image = indicator_image_helper(blocked_name);
-    GdkPixbuf* blocked_buf = gtk_image_get_pixbuf(temp_image);
-
-    if(mute_buf == NULL || blocked_buf == NULL){
-        g_debug("Don bother with the animation, the theme aint got the goods !");
-        return;
-    }
-
-    int i;
-
-    // sample 51 snapshots - range : 0-256
-    for(i = 0; i < 51; i++)
-    {
-        gdk_pixbuf_composite(mute_buf, blocked_buf, 0, 0,
-                             gdk_pixbuf_get_width(mute_buf),
-                             gdk_pixbuf_get_height(mute_buf),
-                             0, 0, 1, 1, GDK_INTERP_BILINEAR, MIN(255, i * 5));
-        blocked_animation_list = g_list_append(blocked_animation_list, gdk_pixbuf_copy(blocked_buf));
-    }
-    g_object_ref_sink(mute_buf);
+  gchar* blocked_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT));
+  gchar* muted_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED));
+
+  GtkImage* temp_image = indicator_image_helper(muted_name);
+  GdkPixbuf* mute_buf = gtk_image_get_pixbuf(temp_image);
+
+  temp_image = indicator_image_helper(blocked_name);
+  GdkPixbuf* blocked_buf = gtk_image_get_pixbuf(temp_image);
+
+  if(mute_buf == NULL || blocked_buf == NULL){
+    g_debug("Don bother with the animation, the theme aint got the goods !");
+    return;
+  }
+
+  int i;
+
+  // sample 51 snapshots - range : 0-256
+  for(i = 0; i < 51; i++)
+  {
+    gdk_pixbuf_composite(mute_buf, blocked_buf, 0, 0,
+                         gdk_pixbuf_get_width(mute_buf),
+                         gdk_pixbuf_get_height(mute_buf),
+                         0, 0, 1, 1, GDK_INTERP_BILINEAR, MIN(255, i * 5));
+    blocked_animation_list = g_list_append(blocked_animation_list, gdk_pixbuf_copy(blocked_buf));
+  }
+  g_object_ref_sink(mute_buf);
 	g_object_unref(mute_buf);
-    g_object_ref_sink(blocked_buf);
+  g_object_ref_sink(blocked_buf);
 	g_object_unref(blocked_buf);
 }
 
@@ -411,26 +462,26 @@
 gint
 get_state()
 {
-    return current_state;
+  return current_state;
 }
 
 gchar*
 get_state_image_name(gint state)
 {
-    return g_hash_table_lookup(volume_states, GINT_TO_POINTER(state));
+  return g_hash_table_lookup(volume_states, GINT_TO_POINTER(state));
 }
 
 void
 prepare_for_tests(IndicatorObject *io)
 {
-    prepare_state_machine();
-    get_icon(io);
+  prepare_state_machine();
+  get_icon(io);
 }
 
 void
 tidy_up_hash()
 {
-    g_hash_table_destroy(volume_states);
+  g_hash_table_destroy(volume_states);
 }
 
 static void
@@ -438,13 +489,13 @@
 {
 /*    g_debug("update state beginning - previous_state = %i", previous_state);*/
 
-    previous_state = current_state;
+  previous_state = current_state;
 
 /*    g_debug("update state 3rd line - previous_state = %i", previous_state);*/
 
-    current_state = state;
-    gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));
-    indicator_image_helper_update(speaker_image, image_name);
+  current_state = state;
+  gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));
+  indicator_image_helper_update(speaker_image, image_name);
 }
 
 
@@ -645,7 +696,6 @@
 /*******************************************************************/
 //UI callbacks
 /******************************************************************/
-
 /**
 value_changed_event_cb:
 This callback will get triggered irregardless of whether its a user change or a programmatic change.
@@ -692,94 +742,93 @@
 static gboolean
 key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
 {
-    gboolean digested = FALSE;
-
-    GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider);
-    GtkRange* range = (GtkRange*)slider;
-    gdouble current_value = gtk_range_get_value(range);
-    gdouble new_value = current_value;
-    const gdouble five_percent = 5;
-    GtkWidget *menuitem;
-
-    menuitem = GTK_MENU_SHELL (widget)->active_menu_item;
-    if(IDO_IS_SCALE_MENU_ITEM(menuitem) == TRUE)
-    {
-        switch(event->keyval)
-            {
-            case GDK_Right:
-                digested = TRUE;
-                if(event->state & GDK_CONTROL_MASK)
-                {
-                    new_value = 100;
-                }
-                else
-                {
-                    new_value = current_value + five_percent;
-                }
-                break;
-            case GDK_Left:
-                digested = TRUE;
-                if(event->state & GDK_CONTROL_MASK)
-                {
-                    new_value = 0;
-                }
-                else
-                {
-                    new_value = current_value - five_percent;
-                }
-                break;
-            case GDK_plus:
-                digested = TRUE;
-                new_value = current_value + five_percent;
-                break;
-            case GDK_minus:
-                digested = TRUE;
-                new_value = current_value - five_percent;
-                break;
-            default:
-                break;
-            }
-
-            new_value = CLAMP(new_value, 0, 100);
-            if(new_value != current_value && current_state != STATE_MUTED)
-            {
-                g_debug("Attempting to set the range from the key listener to %f", new_value);
-                // In order to ensure that the exterior filtering does not catch this, reset the exterior_vol_update
-                // to ensure these updates. 
-                exterior_vol_update = OUT_OF_RANGE;
-                gtk_range_set_value(range, new_value);
-            }
-    }
-    return digested;
+  gboolean digested = FALSE;
+
+  GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider);
+  GtkRange* range = (GtkRange*)slider;
+  gdouble current_value = gtk_range_get_value(range);
+  gdouble new_value = current_value;
+  const gdouble five_percent = 5;
+  GtkWidget *menuitem;
+
+  menuitem = GTK_MENU_SHELL (widget)->active_menu_item;
+  if(IDO_IS_SCALE_MENU_ITEM(menuitem) == TRUE)
+  {
+		switch(event->keyval)
+    {
+    case GDK_Right:
+	    digested = TRUE;
+	    if(event->state & GDK_CONTROL_MASK)
+	    {
+		    new_value = 100;
+    	}
+    	else
+    	{
+        new_value = current_value + five_percent;
+    	}
+    	break;
+    case GDK_Left:
+    	digested = TRUE;
+      if(event->state & GDK_CONTROL_MASK)
+      {
+          new_value = 0;
+      }
+      else
+      {
+          new_value = current_value - five_percent;
+      }
+      break;
+    case GDK_plus:
+      digested = TRUE;
+      new_value = current_value + five_percent;
+      break;
+    case GDK_minus:
+      digested = TRUE;
+      new_value = current_value - five_percent;
+      break;
+    default:
+      break;
+    }
+
+    new_value = CLAMP(new_value, 0, 100);
+    if(new_value != current_value && current_state != STATE_MUTED)
+    {
+      g_debug("Attempting to set the range from the key listener to %f", new_value);
+      // In order to ensure that the exterior filtering does not catch this, reset the exterior_vol_update
+      // to ensure these updates. 
+      exterior_vol_update = OUT_OF_RANGE;
+      gtk_range_set_value(range, new_value);
+    }
+  }
+  return digested;
 }
 
 static void
 style_changed_cb(GtkWidget *widget, gpointer user_data)
 {
-    g_debug("Just caught a style change event");
-    update_state(current_state);
-    reset_mute_blocking_animation();
-    update_state(current_state);
-    free_the_animation_list();
-    prepare_blocked_animation();
+  g_debug("Just caught a style change event");
+  update_state(current_state);
+  reset_mute_blocking_animation();
+  update_state(current_state);
+  free_the_animation_list();
+  prepare_blocked_animation();
 }
 
 static void
 scroll (IndicatorObject *io, gint delta, IndicatorScrollDirection direction)
 {
-    if (device_available == FALSE || current_state == STATE_MUTED)
-        return;
+	if (device_available == FALSE || current_state == STATE_MUTED)
+  	return;
 
-    IndicatorSound *sound = INDICATOR_SOUND (io);
-    GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (sound->slider));
-    gdouble value = gtk_range_get_value (GTK_RANGE (sound->slider));
+  IndicatorSound *sound = INDICATOR_SOUND (io);
+  GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (sound->slider));
+  gdouble value = gtk_range_get_value (GTK_RANGE (sound->slider));
     
-    if (direction == INDICATOR_OBJECT_SCROLL_UP){
-        value += adj->step_increment;
-    }
-    else{
-        value -= adj->step_increment;
-    }
-    gtk_range_set_value (GTK_RANGE (sound->slider),
-                   value);
+  if (direction == INDICATOR_OBJECT_SCROLL_UP){
+  	value += adj->step_increment;
+  }
+  else{
+		value -= adj->step_increment;
+  }
+  gtk_range_set_value (GTK_RANGE (sound->slider), value);
 }

=== added file 'src/metadata-menu-item.vala'
--- src/metadata-menu-item.vala	1970-01-01 00:00:00 +0000
+++ src/metadata-menu-item.vala	2010-06-15 10:35:38 +0000
@@ -0,0 +1,29 @@
+using Dbusmenu;
+using Gee;
+
+public class MetadataMenuitem : Dbusmenu.Menuitem
+{
+	/* Not ideal duplicate definition of const - see common-defs/h */
+ 	const string DBUSMENU_METADATA_MENUITEM_TYPE = "x-canonical-metadata-menu-item";
+ 	const string DBUSMENU_METADATA_MENUITEM_TEXT_ARTIST = "x-canonical-metadata-text-artist";
+ 	const string DBUSMENU_METADATA_MENUITEM_TEXT_PIECE = "x-canonical-metadata-text-piece";
+ 	const string DBUSMENU_METADATA_MENUITEM_TEXT_CONTAINER = "x-canonical-metadata-text-container";
+	const string DBUSMENU_METADATA_MENUITEM_IMAGE_PATH = "x-canonical-metadata-image";
+
+	public MetadataMenuitem()
+  {
+		this.property_set(MENUITEM_PROP_TYPE, DBUSMENU_METADATA_MENUITEM_TYPE);
+		this.property_set(DBUSMENU_METADATA_MENUITEM_TEXT_ARTIST, "Sonnamble");
+		this.property_set(DBUSMENU_METADATA_MENUITEM_TEXT_PIECE, "Nocturne");
+		this.property_set(DBUSMENU_METADATA_MENUITEM_TEXT_CONTAINER, "Seven Months in E minor");
+		this.property_set(DBUSMENU_METADATA_MENUITEM_IMAGE_PATH, "/home/ronoc/Desktop/Sonnamble/Sonnamble_CD.jpg");
+
+		debug("image_path property set %s:", this.property_get(DBUSMENU_METADATA_MENUITEM_IMAGE_PATH));
+
+	}
+
+	public override void handle_event(string name, GLib.Value input_value, uint timestamp)
+	{
+		debug("MetadataItem -> handle event caught!");
+	}	
+}
\ No newline at end of file

=== added file 'src/metadata-widget.c'
--- src/metadata-widget.c	1970-01-01 00:00:00 +0000
+++ src/metadata-widget.c	2010-06-15 10:35:38 +0000
@@ -0,0 +1,177 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+    Conor Curran <conor.curran@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of the GNU General Public License version 3, as published 
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along 
+with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gi18n.h>
+#include "metadata-widget.h"
+#include "common-defs.h"
+#include <gtk/gtk.h>
+
+static DbusmenuMenuitem* twin_item;
+
+typedef struct _MetadataWidgetPrivate MetadataWidgetPrivate;
+
+struct _MetadataWidgetPrivate
+{
+	GtkWidget* hbox;
+	GtkWidget* album_art;
+  gchar* 		 our_path;
+	GtkWidget* artist_label;
+	GtkWidget* piece_label;
+	GtkWidget* container_label;	
+};
+
+#define METADATA_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), METADATA_WIDGET_TYPE, MetadataWidgetPrivate))
+
+/* Prototypes */
+static void metadata_widget_class_init (MetadataWidgetClass *klass);
+static void metadata_widget_init       (MetadataWidget *self);
+static void metadata_widget_dispose    (GObject *object);
+static void metadata_widget_finalize   (GObject *object);
+// keyevent consumers
+static gboolean metadata_widget_button_press_event (GtkWidget *menuitem, 
+                                  									GdkEventButton *event);
+static gboolean metadata_widget_button_release_event (GtkWidget *menuitem, 
+                                  										GdkEventButton *event);
+// Dbusmenuitem properties update callback
+static void metadata_widget_update_state(gchar * property, 
+                                       GValue * value,
+                                       gpointer userdata);
+//static void update_content( 
+
+
+G_DEFINE_TYPE (MetadataWidget, metadata_widget, GTK_TYPE_MENU_ITEM);
+
+
+
+static void
+metadata_widget_class_init (MetadataWidgetClass *klass)
+{
+	GObjectClass 			*gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
+
+	widget_class->button_press_event = metadata_widget_button_press_event;
+  widget_class->button_release_event = metadata_widget_button_release_event;
+	
+	g_type_class_add_private (klass, sizeof (MetadataWidgetPrivate));
+
+	gobject_class->dispose = metadata_widget_dispose;
+	gobject_class->finalize = metadata_widget_finalize;
+
+}
+
+static void
+metadata_widget_init (MetadataWidget *self)
+{
+	g_debug("MetadataWidget::metadata_widget_init");
+
+	MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(self);
+
+	GtkWidget *hbox;
+
+	hbox = gtk_hbox_new(TRUE, 0);
+	priv->hbox = hbox;
+	
+	// image
+	const gchar* path = dbusmenu_menuitem_property_get(twin_item, DBUSMENU_METADATA_MENUITEM_IMAGE_PATH);
+	g_debug("MetadataWidget:init - path = %s", path);
+	priv->our_path = g_strdup(path);
+	GdkPixbuf* pixbuf;
+	pixbuf=gdk_pixbuf_new_from_file(path, NULL);
+  pixbuf=gdk_pixbuf_scale_simple(pixbuf,60,60,GDK_INTERP_BILINEAR);
+	priv->album_art = gtk_image_new_from_pixbuf(pixbuf);
+	g_object_unref(pixbuf);
+	gtk_box_pack_start (GTK_BOX (priv->hbox), priv->album_art, FALSE, FALSE, 0);	
+	GtkWidget* vbox = gtk_vbox_new(TRUE, 0);
+
+	// artist
+	GtkWidget* artist;
+	artist = gtk_label_new(dbusmenu_menuitem_property_get(twin_item, DBUSMENU_METADATA_MENUITEM_TEXT_ARTIST));
+	priv->artist_label = artist;
+	gtk_box_pack_start (GTK_BOX (vbox), priv->artist_label, FALSE, FALSE, 0);	
+	
+	// piece
+	GtkWidget* piece;
+	piece = gtk_label_new(dbusmenu_menuitem_property_get(twin_item, DBUSMENU_METADATA_MENUITEM_TEXT_PIECE));
+	priv->piece_label =  piece;
+	gtk_box_pack_start (GTK_BOX (vbox), priv->piece_label, FALSE, FALSE, 0);	
+	
+	// container
+	GtkWidget* container;
+	container = gtk_label_new(dbusmenu_menuitem_property_get(twin_item, DBUSMENU_METADATA_MENUITEM_TEXT_CONTAINER));
+	priv->container_label = container;
+	gtk_box_pack_start (GTK_BOX (vbox), priv->container_label, FALSE, FALSE, 0);	
+	
+	gtk_box_pack_start (GTK_BOX (priv->hbox), vbox, FALSE, FALSE, 0);	
+
+	g_signal_connect(G_OBJECT(twin_item), "property-changed", 
+	                 G_CALLBACK(metadata_widget_update_state), self);
+	gtk_widget_show_all (priv->hbox);
+  gtk_container_add (GTK_CONTAINER (self), hbox);
+	
+}
+
+static void
+metadata_widget_dispose (GObject *object)
+{
+	G_OBJECT_CLASS (metadata_widget_parent_class)->dispose (object);
+}
+
+static void
+metadata_widget_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (metadata_widget_parent_class)->finalize (object);
+}
+
+/* Suppress/consume keyevents */
+static gboolean
+metadata_widget_button_press_event (GtkWidget *menuitem, 
+                                  GdkEventButton *event)
+{
+	g_debug("MetadataWidget::menu_press_event");
+	return TRUE;
+}
+
+static gboolean
+metadata_widget_button_release_event (GtkWidget *menuitem, 
+                                  GdkEventButton *event)
+{
+	g_debug("MetadataWidget::menu_release_event");
+	return TRUE;
+}
+
+static void metadata_widget_update_state(gchar *property, GValue *value, gpointer userdata)
+{
+	g_debug("metadata_widget_update_state - with property  %s", property);  
+}
+
+ /**
+ * transport_new:
+ * @returns: a new #MetadataWidget.
+ **/
+GtkWidget* 
+metadata_widget_new(DbusmenuMenuitem *item)
+{
+	twin_item = item;
+	return g_object_new(METADATA_WIDGET_TYPE, NULL);
+}
+

=== added file 'src/metadata-widget.h'
--- src/metadata-widget.h	1970-01-01 00:00:00 +0000
+++ src/metadata-widget.h	2010-06-15 10:35:38 +0000
@@ -0,0 +1,51 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+    Conor Curran <conor.curran@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of the GNU General Public License version 3, as published 
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along 
+with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __METADATA_WIDGET_H__
+#define __METADATA_WIDGET_H__
+
+#include <gtk/gtkmenuitem.h>
+#include <libdbusmenu-gtk/menu.h>
+
+G_BEGIN_DECLS
+
+#define METADATA_WIDGET_TYPE            (metadata_widget_get_type ())
+#define METADATA_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), METADATA_WIDGET_TYPE, TransportBar))
+#define METADATA_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), METADATA_WIDGET_TYPE, MetadataWidgetClass))
+#define IS_METADATA_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), METADATA_WIDGET_TYPE))
+#define IS_METADATA_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), METADATA_WIDGET_TYPE))
+#define METADATA_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), METADATA_WIDGET_TYPE, MetadataWidgetClass))
+
+typedef struct _MetadataWidget      MetadataWidget;
+typedef struct _MetadataWidgetClass MetadataWidgetClass;
+
+struct _MetadataWidgetClass {
+	  GtkMenuItemClass parent_class;
+};
+
+struct _MetadataWidget {
+	  GtkMenuItem parent;
+};
+
+GType metadata_widget_get_type (void);
+GtkWidget* metadata_widget_new(DbusmenuMenuitem *twin_item);
+
+G_END_DECLS
+
+#endif
+

=== modified file 'src/music-player-bridge.vala'
--- src/music-player-bridge.vala	2010-05-20 17:50:44 +0000
+++ src/music-player-bridge.vala	2010-06-15 10:35:38 +0000
@@ -7,11 +7,11 @@
 
   private Listener listener;
   private Dbusmenu.Menuitem root_menu;
-	private HashMap<string, Dbusmenu.Menuitem> registered_clients;  
+	private HashMap<string, PlayerController> registered_clients;  
 	
   public MusicPlayerBridge()
   {
-		registered_clients = new HashMap<string, Dbusmenu.Menuitem> ();
+		registered_clients = new HashMap<string, PlayerController> ();
     listener = Listener.ref_default();
     listener.indicator_added.connect(on_indicator_added);
     listener.indicator_removed.connect(on_indicator_removed);
@@ -48,10 +48,8 @@
 		if(server_is_not_of_interest(type)) return;
 		string client_name = type.split(".")[1];
 		if (root_menu != null && client_name != null){
-			Dbusmenu.Menuitem client_item = new Dbusmenu.Menuitem();
-			client_item.property_set(MENUITEM_PROP_LABEL, client_name.concat(" is registered"));
-			registered_clients.set(client_name, client_item); 
-			root_menu.child_append(client_item);
+			PlayerController ctrl = new PlayerController(root_menu, client_name, true);
+			registered_clients.set(client_name, ctrl); 
 			debug("client of name %s has successfully registered with us", client_name);
 		}
   }
@@ -62,7 +60,7 @@
 		if(server_is_not_of_interest(type)) return;
 		string client_name = type.split(".")[1];
 		if (root_menu != null && client_name != null){
-			root_menu.child_delete(registered_clients[client_name]);
+			registered_clients[client_name].vanish();
 			registered_clients.remove(client_name);
 			debug("Successively removed menu_item for client %s from registered_clients", client_name);
 		}

=== added file 'src/player-controller.vala'
--- src/player-controller.vala	1970-01-01 00:00:00 +0000
+++ src/player-controller.vala	2010-06-15 10:35:38 +0000
@@ -0,0 +1,67 @@
+using Dbusmenu;
+using Gee;
+
+public class PlayerController : GLib.Object
+{
+	private Dbusmenu.Menuitem root_menu;
+	private string name;
+	private bool is_active;
+	private ArrayList<Dbusmenu.Menuitem> custom_items;	
+
+	// TODO: pass in the appropriate position for the menu (to handle multiple players)
+	public PlayerController(Dbusmenu.Menuitem root, string client_name, bool active)
+	{
+		this.root_menu = root;
+		this.name = format_client_name(client_name.strip());
+		this.is_active = active;
+		this.custom_items = new ArrayList<Dbusmenu.Menuitem>();
+		self_construct();
+	}
+
+	public void vanish()
+	{
+		foreach(Dbusmenu.Menuitem item in this.custom_items){
+			root_menu.child_delete(item);			
+		}
+	}
+	
+	private bool self_construct()
+	{
+		// Separator item
+		Dbusmenu.Menuitem separator_item = new Dbusmenu.Menuitem();
+		separator_item.property_set(MENUITEM_PROP_TYPE, CLIENT_TYPES_SEPARATOR);			
+		this.custom_items.add(separator_item);
+
+		// Title item
+		Dbusmenu.Menuitem title_item = new Dbusmenu.Menuitem();
+		title_item.property_set(MENUITEM_PROP_LABEL, this.name);					
+		title_item.property_set(MENUITEM_PROP_ICON_NAME, "applications-multimedia");			
+		this.custom_items.add(title_item);
+
+		// Metadata item
+		MetadataMenuitem metadata_item = new MetadataMenuitem();
+		this.custom_items.add(metadata_item);
+		
+		// Transport item
+		TransportMenuitem transport_item = new TransportMenuitem();
+		this.custom_items.add(transport_item);
+
+		int offset = 2;
+		foreach(Dbusmenu.Menuitem item in this.custom_items){
+			root_menu.child_add_position(item, offset + this.custom_items.index_of(item));			
+		}
+		return true;
+	}	
+	
+
+	private static string format_client_name(string client_name)
+	{
+		string formatted = client_name;
+		if(formatted.len() > 1){
+			formatted = client_name.up(1).concat(client_name.slice(1, client_name.len()));
+			debug("PlayerController->format_client_name - : %s", formatted);
+		}		
+		return formatted;
+	}
+	
+}
\ No newline at end of file

=== modified file 'src/slider-menu-item.c'
--- src/slider-menu-item.c	2010-03-26 13:56:46 +0000
+++ src/slider-menu-item.c	2010-06-15 10:35:38 +0000
@@ -53,8 +53,8 @@
 	object_class->dispose = slider_menu_item_dispose;
 	object_class->finalize = slider_menu_item_finalize;
 
-    DbusmenuMenuitemClass * mclass = DBUSMENU_MENUITEM_CLASS(klass);
-    mclass->handle_event = handle_event;
+  DbusmenuMenuitemClass * mclass = DBUSMENU_MENUITEM_CLASS(klass);
+  mclass->handle_event = handle_event;
 	return;
 }
 
@@ -81,10 +81,10 @@
 handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp)
 {
 	g_debug("in the handle event method of slider_menu_item");
-    gdouble volume_input = 0;
-    volume_input = g_value_get_double(value);
-    if(value != NULL)
-        set_sink_volume(volume_input);
+  gdouble volume_input = 0;
+  volume_input = g_value_get_double(value);
+  if(value != NULL)
+  	set_sink_volume(volume_input);
 }
 
 
@@ -92,9 +92,9 @@
 SliderMenuItem* slider_menu_item_new(gboolean sinks_available, gdouble start_volume)
 {
 	SliderMenuItem *self = g_object_new(SLIDER_MENU_ITEM_TYPE, NULL);
-    dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_SLIDER_MENUITEM_TYPE);
-    dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_ENABLED, sinks_available);
-    dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_VISIBLE, sinks_available);
+  dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_SLIDER_MENUITEM_TYPE);
+  dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_ENABLED, sinks_available);
+  dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_VISIBLE, sinks_available);
 	return self;
 }
 

=== modified file 'src/sound-service.c'
--- src/sound-service.c	2010-05-21 18:14:16 +0000
+++ src/sound-service.c	2010-06-15 10:35:38 +0000
@@ -44,9 +44,9 @@
 
 	if (mainloop != NULL) {
 		g_debug("Service shutdown !");
-        // TODO: uncomment for release !!
-       	close_pulse_activites();
-        g_main_loop_quit(mainloop);
+    // TODO: uncomment for release !!
+   	//close_pulse_activites();
+    //g_main_loop_quit(mainloop);
 	}
 	return;
 }

=== added file 'src/transport-menu-item.vala'
--- src/transport-menu-item.vala	1970-01-01 00:00:00 +0000
+++ src/transport-menu-item.vala	2010-06-15 10:35:38 +0000
@@ -0,0 +1,19 @@
+using Dbusmenu;
+using Gee;
+
+public class TransportMenuitem : Dbusmenu.Menuitem
+{
+	/* Not ideal duplicate definition of const - see common-defs/h */
+ 	const string DBUSMENU_TRANSPORT_MENUITEM_TYPE = "x-canonical-transport-bar";
+ 	const string DBUSMENU_TRANSPORT_MENUITEM_STATE = "x-canonical-transport-state";
+
+	public TransportMenuitem()
+  {
+		this.property_set(MENUITEM_PROP_TYPE, DBUSMENU_TRANSPORT_MENUITEM_TYPE);
+		this.property_set(DBUSMENU_TRANSPORT_MENUITEM_STATE, "play");
+	}
+
+	public override void handle_event(string name, GLib.Value input_value, uint timestamp)
+	{
+	}	
+}
\ No newline at end of file

=== added file 'src/transport-widget.c'
--- src/transport-widget.c	1970-01-01 00:00:00 +0000
+++ src/transport-widget.c	2010-06-15 10:35:38 +0000
@@ -0,0 +1,284 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+    Conor Curran <conor.curran@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of the GNU General Public License version 3, as published 
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along 
+with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gi18n.h>
+#include "transport-widget.h"
+#include "common-defs.h"
+#include <gtk/gtk.h>
+
+// TODO: think about leakage: ted !
+
+static DbusmenuMenuitem* twin_item;
+
+typedef struct _TransportWidgetPrivate TransportWidgetPrivate;
+
+struct _TransportWidgetPrivate
+{
+	GtkWidget* hbox;
+	//GtkWidget* previous_button;
+	GtkWidget* play_button;
+	//GtkWidget* next_button;	
+};
+
+enum {
+  PLAY,
+	PAUSE,
+  NEXT,
+	PREVIOUS,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define TRANSPORT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRANSPORT_WIDGET_TYPE, TransportWidgetPrivate))
+
+/* Gobject boiler plate */
+static void transport_widget_class_init (TransportWidgetClass *klass);
+static void transport_widget_init       (TransportWidget *self);
+static void transport_widget_dispose    (GObject *object);
+static void transport_widget_finalize   (GObject *object);
+
+/* UI and dbusmenu callbacks */
+static gboolean transport_widget_button_press_event 	(GtkWidget             *menuitem,
+                                                  		GdkEventButton        *event);
+static gboolean transport_widget_button_release_event (GtkWidget             *menuitem,
+                                                    	GdkEventButton        *event);
+//static gboolean transport_widget_play_button_press_event (GtkWidget 		 	*widget, 
+//																													GdkEventButton 	*event,
+//	                                     										gpointer        user_data);
+//static gboolean transport_widget_previous_button_press_event (GtkWidget 		 	*widget, 
+//																															GdkEventButton 	*event,
+//                                      												gpointer        user_data);
+//static gboolean transport_widget_next_button_press_event (GtkWidget 		 	*widget, 
+//																													GdkEventButton 	*event,
+//                                      										gpointer        user_data);
+
+static void transport_widget_update_state(DbusmenuMenuitem* item,
+                                       gchar * property, 
+                                       GValue * value,
+                                       gpointer userdata);
+// utility methods
+static gchar* transport_widget_determine_play_label(const gchar* state);
+
+G_DEFINE_TYPE (TransportWidget, transport_widget, GTK_TYPE_MENU_ITEM);
+
+
+
+static void
+transport_widget_class_init (TransportWidgetClass *klass)
+{
+	GObjectClass 			*gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
+
+  widget_class->button_press_event = transport_widget_button_press_event;
+  widget_class->button_release_event = transport_widget_button_release_event;
+	
+	g_type_class_add_private (klass, sizeof (TransportWidgetPrivate));
+
+	gobject_class->dispose = transport_widget_dispose;
+	gobject_class->finalize = transport_widget_finalize;
+
+  signals[PLAY] = g_signal_new ("play",
+                               G_OBJECT_CLASS_TYPE (gobject_class),
+                               G_SIGNAL_RUN_FIRST,
+                               0,
+                               NULL, NULL,
+                               g_cclosure_marshal_VOID__VOID,
+                               G_TYPE_NONE, 0);
+
+  signals[PAUSE] = g_signal_new ("pause",
+                               G_OBJECT_CLASS_TYPE (gobject_class),
+                               G_SIGNAL_RUN_FIRST,
+                               0,
+                               NULL, NULL,
+                               g_cclosure_marshal_VOID__VOID,
+                               G_TYPE_NONE, 0);
+
+
+	signals[NEXT] = g_signal_new ("next",
+                               G_OBJECT_CLASS_TYPE (gobject_class),
+                               G_SIGNAL_RUN_FIRST,
+                               0,
+                               NULL, NULL,
+                               g_cclosure_marshal_VOID__VOID,
+                               G_TYPE_NONE, 0);
+
+  signals[PREVIOUS] = g_signal_new ("previous",
+                               G_OBJECT_CLASS_TYPE (gobject_class),
+                               G_SIGNAL_RUN_FIRST,
+                               0,
+                               NULL, NULL,
+                               g_cclosure_marshal_VOID__VOID,
+                               G_TYPE_NONE, 0);	
+}
+
+static void
+transport_widget_init (TransportWidget *self)
+{
+	g_debug("TransportWidget::transport_widget_init");
+
+	TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE(self);
+	GtkWidget *hbox;
+
+	hbox = gtk_hbox_new(TRUE, 2);
+	//priv->previous_button = gtk_button_new_with_label("<<");
+  //priv->next_button = gtk_button_new_with_label(">>");
+	priv->play_button =	gtk_button_new_with_label(">");
+
+	//gtk_box_pack_start (GTK_BOX (hbox), priv->previous_button, FALSE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), priv->play_button, FALSE, TRUE, 0);
+	//gtk_box_pack_start (GTK_BOX (hbox), priv->next_button, FALSE, TRUE, 0);
+
+	//g_signal_connect(GTK_OBJECT(priv->play_button), "button-press-event", G_CALLBACK(transport_widget_play_button_press_event), priv->play_button);
+	//g_signal_connect(GTK_OBJECT(priv->previous_button), "button-press-event", G_CALLBACK(transport_widget_previous_button_press_event), NULL);
+	//g_signal_connect(GTK_OBJECT(priv->next_button), "button-press-event", G_CALLBACK(transport_widget_next_button_press_event), NULL);
+
+	priv->hbox = hbox;
+	
+	g_signal_connect(G_OBJECT(twin_item), "property-changed", G_CALLBACK(transport_widget_update_state), self);
+
+	gtk_container_add (GTK_CONTAINER (self), priv->hbox);
+
+	//GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(priv->hbox));
+	//if (GTK_IS_MENU_ITEM(parent)){
+	//	g_debug("as expected the transport item is the parent");
+	//}
+	//gtk_widget_set_parent(GTK_WIDGET(priv->hbox), GTK_WIDGET(self));
+  gtk_widget_show_all (priv->hbox);
+}
+
+static void
+transport_widget_dispose (GObject *object)
+{
+	G_OBJECT_CLASS (transport_widget_parent_class)->dispose (object);
+}
+
+static void
+transport_widget_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (transport_widget_parent_class)->finalize (object);
+}
+
+/* keyevents */
+static gboolean
+transport_widget_button_press_event (GtkWidget *menuitem, 
+                                  	GdkEventButton *event)
+{
+	g_debug("TransportWidget::menu_press_event");
+	TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE(TRANSPORT_WIDGET(menuitem));
+	gtk_button_set_label(GTK_BUTTON(priv->play_button), g_strdup(transport_widget_determine_play_label(gtk_button_get_label(GTK_BUTTON(priv->play_button)))));
+
+	//gtk_button_pressed(GTK_BUTTON(priv->play_button));
+	//gtk_button_pressed(GTK_BUTTON(priv->previous_button));
+	//gtk_button_pressed(GTK_BUTTON(priv->next_button));
+
+	//if (event->type == GDK_BUTTON_PRESS) {
+		//GdkEventButton *bevent = (GdkEventButton *) event; 
+		//gtk_button_pressed(bevent->button);
+//gtk_menu_popup (GTK_MENU (menuitem), NULL, NULL, NULL, NULL,
+//    	               bevent->button, bevent->time);
+   	/* Tell calling code that we have handled this event; the buck
+   	* stops here. */
+   	//return TRUE;
+  //}
+	
+	return TRUE;
+}
+
+static gboolean
+transport_widget_button_release_event (GtkWidget *menuitem, 
+                                  GdkEventButton *event)
+{
+	g_debug("TransportWidget::menu_release_event");
+	return TRUE;
+}
+
+/* Individual keyevents on the buttons */
+//static gboolean
+//transport_widget_play_button_press_event(	GtkWidget* 			widget,
+//                                  				GdkEventButton 	*event,
+//                                  				gpointer        user_data)
+//{
+//	g_debug("!!!!! TransportWidget::PLAY button_press_event");	
+//	return TRUE;
+//}
+
+//static gboolean
+//transport_widget_previous_button_press_event(	GtkWidget* 			widget,
+//                                  				GdkEventButton 	*event,
+//                                  				gpointer        user_data)
+//{
+//	g_debug("!!!!! TransportWidget::PREVIOUS button_press_event");	
+//	return TRUE;
+//}
+
+//static gboolean
+//transport_widget_next_button_press_event(	GtkWidget* 			widget,
+//                                  				GdkEventButton 	*event,
+//                                 				gpointer        user_data)
+//{
+//	g_debug("!!!!! TransportWidget::NEXT button_press_event");	
+//	return TRUE;
+//}
+
+/**
+* transport_widget_update_state()
+* Callback for updates from the other side of dbus
+**/
+static void transport_widget_update_state(DbusmenuMenuitem* item, gchar* property, 
+                                       GValue* value, gpointer userdata)
+{
+	g_debug("transport_widget_update_state - with property  %s", property);  
+	gchar* input = g_strdup(g_value_get_string(value));
+	g_debug("transport_widget_update_state - with value  %s", input);  
+
+	TransportWidget* bar = (TransportWidget*)userdata;
+	TransportWidgetPrivate *priv = TRANSPORT_WIDGET_GET_PRIVATE(bar);
+	
+	gtk_button_set_label(GTK_BUTTON(priv->play_button), g_strdup(transport_widget_determine_play_label(property)));
+}
+
+// will be needed for image swapping
+static gchar* transport_widget_determine_play_label(const gchar* state)
+{
+	gchar* label = ">";
+	//if(g_strcmp0(state, "||") == 0){	
+	//	label = "||";
+	//}
+	if(g_strcmp0(state, ">") == 0){
+		label = "||";
+	}
+	return label;
+}
+
+ /**
+ * transport_new:
+ * @returns: a new #TransportWidget.
+ **/
+GtkWidget* 
+transport_widget_new(DbusmenuMenuitem *item)
+{
+	twin_item = item;
+	return g_object_new(TRANSPORT_WIDGET_TYPE, NULL);
+}
+

=== added file 'src/transport-widget.h'
--- src/transport-widget.h	1970-01-01 00:00:00 +0000
+++ src/transport-widget.h	2010-06-15 10:35:38 +0000
@@ -0,0 +1,51 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+    Conor Curran <conor.curran@xxxxxxxxxxxxx>
+
+This program is free software: you can redistribute it and/or modify it 
+under the terms of the GNU General Public License version 3, as published 
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but 
+WITHOUT ANY WARRANTY; without even the implied warranties of 
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along 
+with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __TRANSPORT_WIDGET_H__
+#define __TRANSPORT_WIDGET_H__
+
+#include <gtk/gtkmenuitem.h>
+#include <libdbusmenu-gtk/menu.h>
+
+G_BEGIN_DECLS
+
+#define TRANSPORT_WIDGET_TYPE            (transport_widget_get_type ())
+#define TRANSPORT_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRANSPORT_WIDGET_TYPE, TransportWidget))
+#define TRANSPORT_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TRANSPORT_WIDGET_TYPE, TransportWidgetClass))
+#define IS_TRANSPORT_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRANSPORT_WIDGET_TYPE))
+#define IS_TRANSPORT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRANSPORT_WIDGET_TYPE))
+#define TRANSPORT_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TRANSPORT_WIDGET_TYPE, TransportWidgetClass))
+
+typedef struct _TransportWidget      TransportWidget;
+typedef struct _TransportWidgetClass TransportWidgetClass;
+
+struct _TransportWidgetClass {
+	  GtkMenuItemClass parent_class;
+};
+
+struct _TransportWidget {
+	  GtkMenuItem parent;
+};
+
+GType transport_widget_get_type (void);
+GtkWidget* transport_widget_new(DbusmenuMenuitem *twin_item);
+
+G_END_DECLS
+
+#endif
+


Follow ups