← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~cjcurran/indicator-sound/app-launching-finish into lp:indicator-sound

 

Conor Curran has proposed merging lp:~cjcurran/indicator-sound/app-launching-finish into lp:indicator-sound.

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


Adds a new custom widget to help handle the Title behaviour, And the full libindicate familiar app launching now plugged in. 

-- 
https://code.launchpad.net/~cjcurran/indicator-sound/app-launching-finish/+merge/29395
Your team ayatana-commits is subscribed to branch lp:indicator-sound.
=== modified file 'data/Makefile.am'
--- data/Makefile.am	2010-01-27 13:21:02 +0000
+++ data/Makefile.am	2010-07-07 14:11:42 +0000
@@ -1,4 +1,3 @@
-
 dbus_servicesdir = $(DBUSSERVICEDIR)
 service_in_files =  indicator-sound.service.in 
 dbus_services_DATA = $(service_in_files:.service.in=.service)

=== added file 'data/album_artwork.png'
Binary files data/album_artwork.png	1970-01-01 00:00:00 +0000 and data/album_artwork.png	2010-07-07 14:11:42 +0000 differ
=== added file 'data/sound_icon.png'
Binary files data/sound_icon.png	1970-01-01 00:00:00 +0000 and data/sound_icon.png	2010-07-07 14:11:42 +0000 differ
=== modified file 'src/Makefile.am'
--- src/Makefile.am	2010-06-24 17:16:23 +0000
+++ src/Makefile.am	2010-07-07 14:11:42 +0000
@@ -14,6 +14,8 @@
 	metadata-widget.c \
 	metadata-widget.h \
 	indicator-sound.c \
+	title-widget.c \
+	title-widget.h \
 	dbus-shared-names.h \
   sound-service-client.h 
 
@@ -54,6 +56,7 @@
 	music-player-bridge.vala \
 	transport-menu-item.vala \
 	metadata-menu-item.vala \
+	title-menu-item.vala \
 	player-controller.vala \
 	mpris-controller-v2.vala \
 	mpris-controller.vala \

=== modified file 'src/common-defs.h'
--- src/common-defs.h	2010-06-22 11:43:24 +0000
+++ src/common-defs.h	2010-07-07 14:11:42 +0000
@@ -34,3 +34,6 @@
 #define DBUSMENU_METADATA_MENUITEM_TEXT_TITLE  	"x-canonical-metadata-text-title"
 #define DBUSMENU_METADATA_MENUITEM_TEXT_ALBUM   "x-canonical-metadata-text-album"
 #define DBUSMENU_METADATA_MENUITEM_ARTURL  			"x-canonical-metadata-arturl"
+
+#define DBUSMENU_TITLE_MENUITEM_TYPE          	"x-canonical-sound-menu-player-title-menu-item"
+#define DBUSMENU_TITLE_MENUITEM_TEXT_NAME       "x-canonical-sound-menu-player-title-name"

=== modified file 'src/familiar-players-db.vala'
--- src/familiar-players-db.vala	2010-06-24 16:23:39 +0000
+++ src/familiar-players-db.vala	2010-07-07 14:11:42 +0000
@@ -143,7 +143,8 @@
 	
 	public bool already_familiar(string desktop)
 	{
-		return this.players_DB.get(desktop);
+		debug("playerDB->already_familiar - result %s", this.players_DB.keys.contains(desktop).to_string()); 
+		return this.players_DB.keys.contains(desktop);
 	}
 
 	public Gee.Set<string> records()

=== modified file 'src/indicator-sound.c'
--- src/indicator-sound.c	2010-06-18 09:02:20 +0000
+++ src/indicator-sound.c	2010-07-07 14:11:42 +0000
@@ -40,6 +40,7 @@
 #include "indicator-sound.h"
 #include "transport-widget.h"
 #include "metadata-widget.h"
+#include "title-widget.h"
 #include "dbus-shared-names.h"
 #include "sound-service-client.h"
 #include "common-defs.h"
@@ -96,6 +97,7 @@
 //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);
+static gboolean new_title_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
 
 // DBUS communication
 static DBusGProxy *sound_dbus_proxy = NULL;
@@ -243,7 +245,7 @@
   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);
-
+  dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_TITLE_MENUITEM_TYPE, new_title_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);
@@ -353,9 +355,25 @@
   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 gboolean
+new_title_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+  g_debug("indicator-sound: new_title_widget");
+
+  GtkWidget* title = NULL;
+
+  g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
+  g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
+
+  title = title_widget_new (newitem);
+  GtkMenuItem *menu_title_widget = GTK_MENU_ITEM(title);
+
+  dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_title_widget, parent);
+
+  gtk_widget_show_all(title);
+
+  return TRUE;
+}
 
 
 static void

=== modified file 'src/metadata-menu-item.vala'
--- src/metadata-menu-item.vala	2010-06-24 16:23:39 +0000
+++ src/metadata-menu-item.vala	2010-07-07 14:11:42 +0000
@@ -25,7 +25,7 @@
 {
 	public MetadataMenuitem()
   {
-		this.property_set(MENUITEM_PROP_TYPE, MENUITEM_TYPE);
+		Object(item_type: MENUITEM_TYPE); 	
 	}
 
 	public static HashSet<string> attributes_format()
@@ -37,11 +37,6 @@
     attrs.add(MENUITEM_ARTURL);
 		return attrs;
 	}
-
-	public override void check_layout(){
-		this.property_set_bool(MENUITEM_PROP_VISIBLE, this.populated()); 
-		debug("check layout for the metadata = %s", this.populated().to_string()); 
-	}
 	
 	public bool populated()
 	{

=== modified file 'src/mpris-controller.vala'
--- src/mpris-controller.vala	2010-06-25 11:59:15 +0000
+++ src/mpris-controller.vala	2010-07-07 14:11:42 +0000
@@ -76,6 +76,11 @@
 			this.mpris_player.Pause();						
 		}		
 	}
+
+	public bool connected()
+	{
+		return (this.mpris_player != null);
+	}
 	
 	private void onStatusChange(dynamic DBus.Object mpris_client, status st)
   {

=== modified file 'src/music-player-bridge.vala'
--- src/music-player-bridge.vala	2010-06-24 16:23:39 +0000
+++ src/music-player-bridge.vala	2010-07-07 14:11:42 +0000
@@ -42,25 +42,29 @@
     listener.server_removed.connect(on_server_removed);
     listener.server_count_changed.connect(on_server_count_changed);
   }
-	// Alpha 2 not in use ... yet.
+
 	private void try_to_add_inactive_familiar_clients(){
-		// for now just use one of the entries.
+		// TODO handle multple players - just working with one right now
 		int count = 0;
 		foreach(string app in this.playersDB.records()){
 			if(count == 0){
-				debug("we have found %s", app);
-				string[] bits = app.split("/");
-
+				if(app == null){
+					warning("App string in keyfile is null therefore moving on to next player");
+					continue;
+				}
 				try{
-					string app_name = bits[bits.length -1].split(".")[0];
-					debug("we have found %s", app_name);
+					DesktopAppInfo info = new DesktopAppInfo.from_filename(app); 
+					if(info == null){
+						warning("Could not create a desktopappinfo instance from app: %s", app);
+						continue;					
+					}
+					GLib.AppInfo app_info = info as GLib.AppInfo;
 					PlayerController ctrl = new PlayerController(this.root_menu, 
-					                                             app_name,
-					                                             false);
-					this.registered_clients.set(app_name, ctrl);
-					DesktopAppInfo info = new DesktopAppInfo.from_filename(app_name); 					
-					string desc =	info.get_display_name();
-					debug("description from app %s", desc);
+					                                             app_info.get_name(), 
+					                                             PlayerController.OFFLINE);
+					ctrl.set("app_info", app_info);
+					this.registered_clients.set(app_info.get_name().down().strip(), ctrl);					
+					debug("Created a player controller for %s which was found in the cache file", app_info.get_name().down().strip());
 					count += 1;					
 				}
 				catch(Error er){
@@ -75,13 +79,25 @@
   {
     debug("MusicPlayerBridge -> on_server_added with value %s", type);
 		if(server_is_not_of_interest(type)) return;
-		string client_name = type.split(".")[1];		
+		string client_name = type.split(".")[1];
 		if (root_menu != null && client_name != null){
-			listener_get_server_property_cb cb = (listener_get_server_property_cb)desktop_info_callback;
-			this.listener.server_get_desktop(object, cb, this);			
-			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);
+			// If we have an instance already for this player, ensure it is switched to active
+			if(this.registered_clients.keys.contains(client_name)){
+				debug("It figured out that it already has an instance for this player already");
+				this.registered_clients[client_name].update_state(PlayerController.READY);
+				this.registered_clients[client_name].activate();
+			}
+			//else init a new one
+			else{			
+				PlayerController ctrl = new PlayerController(root_menu, client_name, PlayerController.READY);
+				registered_clients.set(client_name, ctrl); 
+				debug("New Client of name %s has successfully registered with us", client_name);
+			}
+			// irregardless check that it has a desktop file if not kick off a request for it
+			if(this.registered_clients[client_name].app_info == null){
+				listener_get_server_property_cb cb = (listener_get_server_property_cb)desktop_info_callback;
+				this.listener.server_get_desktop(object, cb, this);					
+			}			
 		}
   }
 
@@ -93,7 +109,8 @@
 		if (root_menu != null && client_name != null){
 			registered_clients[client_name].vanish();
 			registered_clients.remove(client_name);
-			debug("Successively removed menu_item for client %s from registered_clients", client_name);
+			debug("Successively removed menu_item for client %s from registered_clients",
+			      client_name);
 		}
 	}
 	
@@ -109,15 +126,26 @@
 	private void desktop_info_callback(Indicate.ListenerServer server,
 	                                 	owned string path, void* data)	                                  
 	{
-		debug("we got a desktop file path hopefully: %s", path);	
 		MusicPlayerBridge bridge = data as MusicPlayerBridge;
-		bridge.playersDB.insert(path);
+		if(path.contains("/") && bridge.playersDB.already_familiar(path) == false){
+			debug("About to store desktop file path: %s", path);
+			bridge.playersDB.insert(path);
+			AppInfo? app_info = create_app_info(path);
+			if(app_info != null){
+				PlayerController ctrl = bridge.registered_clients[app_info.get_name().down().strip()];				
+				ctrl.set("app_info", app_info);
+				debug("successfully created appinfo from path and set it on the respective instance");				
+			}	
+		}
+		else{
+			debug("Ignoring desktop file path because its either invalid of the db cache file has it already: %s", path);
+		}
 	}
 
   public void set_root_menu_item(Dbusmenu.Menuitem menu)
   {
 		this.root_menu = menu;
-		//try_to_add_inactive_familiar_clients();
+		try_to_add_inactive_familiar_clients();
   }
 
 	public void on_server_count_changed(Indicate.ListenerServer object, uint i)
@@ -139,6 +167,17 @@
     debug("MusicPlayerBridge -> indicator_modified with vale %s", s );
   }
 
+	public static AppInfo? create_app_info(string path)
+	{
+		DesktopAppInfo info = new DesktopAppInfo.from_filename(path);
+		if(path == null){
+			warning("Could not create a desktopappinfo instance from app: %s", path);
+			return null;
+		}
+		GLib.AppInfo app_info = info as GLib.AppInfo;		
+		return app_info;
+	}
+
 }
 
 

=== modified file 'src/player-controller.vala'
--- src/player-controller.vala	2010-06-24 16:23:39 +0000
+++ src/player-controller.vala	2010-07-07 14:11:42 +0000
@@ -25,66 +25,119 @@
 {
 	public const int METADATA = 2;	
 	private const int TRANSPORT = 3;
+
+	public static const int OFFLINE 				= 0;
+	public static const int INSTANTIATING  	= 1;
+	public static const int READY						= 2;
+	public static const int CONNECTED				= 3;
+	public static const int DISCONNECTED 		= 4;
+	
+	public int current_state = OFFLINE;
+	
 	
 	private Dbusmenu.Menuitem root_menu;
-	private string name;
-	private bool is_active;
+	public string name { get; set;}	
 	public ArrayList<PlayerItem> custom_items;	
-	private MprisController mpris_adaptor;
-	private string desktop_path;
-	
-	public PlayerController(Dbusmenu.Menuitem root, string client_name, bool active)
+	public MprisController mpris_adaptor;
+	public AppInfo? app_info { get; set;}
+		
+	public PlayerController(Dbusmenu.Menuitem root, string client_name, int state = OFFLINE)
 	{
 		this.root_menu = root;
 		this.name = format_client_name(client_name.strip());
-		this.is_active = active;
 		this.custom_items = new ArrayList<PlayerItem>();
-		self_construct();
-		
-		// Temporary scenario to handle both v1 and v2 of MPRIS.
+		this.update_state(state);
+		construct_widgets();
+		establish_mpris_connection();
+		update_layout();
+	}
+
+	public void update_state(int new_state)
+	{
+		debug("update_state : new state %i", new_state);
+		this.current_state = new_state;
+	}
+	
+	public void activate()
+	{
+		this.establish_mpris_connection();	
+		this.custom_items[METADATA].property_set_bool(MENUITEM_PROP_VISIBLE, true);		
+	}
+
+	/*
+	 instantiate()
+	 The user should be able to start the app from the transport bar when in an offline state
+	 There is a need to wait before the application is on DBus before attempting to access its mpris address
+	 Hence only when the it has registered with us via libindicate do we attempt to kick off mpris communication
+	 */
+	public void instantiate()
+	{
+ 	  this.app_info.launch(null, null);
+		this.update_state(INSTANTIATING);
+	}
+	
+	private void establish_mpris_connection()
+	{		
+		if(this.current_state != READY){
+			debug("establish_mpris_connection - Not ready to connect");
+			return;
+		}
 		if(this.name == "Vlc"){
 			this.mpris_adaptor = new MprisControllerV2(this.name, this);
 		}
 		else{
 			this.mpris_adaptor = new MprisController(this.name, this);
-		}			
-		this.custom_items[TRANSPORT].set_adaptor(this.mpris_adaptor);
-
-		// At start up if there is no metadata then hide the item.
-		// TODO: NOT working -> dbus menu bug ?
-		//((MetadataMenuitem)this.custom_items[METADATA]).check_layout();
+		}
+		if(this.mpris_adaptor.connected() == true){
+			this.update_state(CONNECTED);
+		}
+		else{
+			this.update_state(DISCONNECTED);
+		}
+		this.update_layout();
 	}
-
+	
 	public void vanish()
 	{
 		foreach(Dbusmenu.Menuitem item in this.custom_items){
 			root_menu.child_delete(item);			
 		}
 	}
-	
-	private bool self_construct()
+
+	private void update_layout()
+	{
+		bool visibility = true;
+		if(this.current_state != CONNECTED){
+			visibility = false;
+		}
+		this.custom_items[TRANSPORT].property_set_bool(MENUITEM_PROP_VISIBLE, visibility);
+		this.custom_items[METADATA].property_set_bool(MENUITEM_PROP_VISIBLE, visibility);
+	}
+	
+	
+	private void construct_widgets()
 	{
 		// Separator item
-		this.custom_items.add(PlayerItem.new_separator_item());
+		this.custom_items.add(new PlayerItem(CLIENT_TYPES_SEPARATOR));
 
 		// Title item
-		this.custom_items.add(PlayerItem.new_title_item(this.name));
+		TitleMenuitem title_menu_item = new TitleMenuitem(this, this.name);
+		this.custom_items.add(title_menu_item);
 
 		// Metadata item
 		MetadataMenuitem metadata_item = new MetadataMenuitem();
 		this.custom_items.add(metadata_item);
 		
 		// Transport item
-		TransportMenuitem transport_item = new TransportMenuitem();
+		TransportMenuitem transport_item = new TransportMenuitem(this);
 		this.custom_items.add(transport_item);
 
 		int offset = 2;
 		foreach(PlayerItem 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;
@@ -94,5 +147,5 @@
 		}		
 		return formatted;
 	}
-	
+
 }
\ No newline at end of file

=== modified file 'src/player-item.vala'
--- src/player-item.vala	2010-06-25 11:59:15 +0000
+++ src/player-item.vala	2010-07-07 14:11:42 +0000
@@ -22,10 +22,16 @@
 
 public class PlayerItem : Dbusmenu.Menuitem
 {
-	public MprisController mpris_adaptor;
-	
-	public PlayerItem()
+	public PlayerController owner {get; construct;}
+	public string item_type { get; construct; }
+
+	public PlayerItem(string type)
 	{		
+		Object(item_type: type);
+	}
+	
+	construct {
+		this.property_set(MENUITEM_PROP_TYPE, item_type);
 	}
 
 	public void reset(HashSet<string> attrs){		
@@ -60,15 +66,8 @@
 				this.property_set_bool(property, v.get_boolean());
 			}
 		}
-		// TODO: not working
-		//this.check_layout();
 	}	
 
-	public void set_adaptor(MprisController adaptor)
-	{
-		this.mpris_adaptor = adaptor;		
-	}
-
 	private static bool ensure_valid_updates(HashTable<string, Value?> data, HashSet<string> attributes)
 	{
 		if(data == null){
@@ -91,27 +90,5 @@
 		return result;
 	}
 
-	
-	//----- Custom constructors for player items ----------------//
-	// Title item
-	public static PlayerItem new_title_item(dynamic string name)
-	{
-		PlayerItem item = new PlayerItem();
-		item.property_set(MENUITEM_PROP_LABEL, name);					
-		item.property_set(MENUITEM_PROP_ICON_NAME, "applications-multimedia");			
-		return item;		
-	}
-
-	// Separator item
-	public static PlayerItem new_separator_item()
-	{
-		PlayerItem separator = new PlayerItem();
-		separator.property_set(MENUITEM_PROP_TYPE, CLIENT_TYPES_SEPARATOR);					
-		return separator;
-	}	
-
-	public virtual void check_layout(){
-		warning("this should not be hit");
-	}
 }
 

=== added file 'src/title-menu-item.vala'
--- src/title-menu-item.vala	1970-01-01 00:00:00 +0000
+++ src/title-menu-item.vala	2010-07-07 14:11:42 +0000
@@ -0,0 +1,48 @@
+/*
+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/>.
+*/
+
+using Dbusmenu;
+using DbusmenuTitle;
+using Gee;
+
+public class TitleMenuitem : PlayerItem
+{
+	public TitleMenuitem(PlayerController parent, string name)
+	{
+		Object(item_type: MENUITEM_TYPE, owner: parent);
+		this.property_set(MENUITEM_TEXT_NAME, name);		
+	}
+
+	public override void handle_event(string name, GLib.Value input_value, uint timestamp)
+	{
+		debug("handle_event with bool value %s", input_value.get_boolean().to_string());
+		if(this.owner.current_state == PlayerController.OFFLINE)
+		{
+			this.owner.instantiate();
+		}
+	}
+	
+
+	public static HashSet<string> attributes_format()
+	{
+		HashSet<string> attrs = new HashSet<string>();		
+		attrs.add(MENUITEM_TEXT_NAME);
+ 		return attrs;
+	}	
+}
\ No newline at end of file

=== added file 'src/title-widget.c'
--- src/title-widget.c	1970-01-01 00:00:00 +0000
+++ src/title-widget.c	2010-07-07 14:11:42 +0000
@@ -0,0 +1,191 @@
+/*
+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 "title-widget.h"
+#include "common-defs.h"
+#include <gtk/gtk.h>
+
+static DbusmenuMenuitem* twin_item;
+
+typedef struct _TitleWidgetPrivate TitleWidgetPrivate;
+
+struct _TitleWidgetPrivate
+{
+	GtkWidget* hbox;
+	GtkWidget* name;
+	GtkWidget* player_icon;	
+};
+
+#define TITLE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TITLE_WIDGET_TYPE, TitleWidgetPrivate))
+
+/* Prototypes */
+static void title_widget_class_init (TitleWidgetClass *klass);
+static void title_widget_init       (TitleWidget *self);
+static void title_widget_dispose    (GObject *object);
+static void title_widget_finalize   (GObject *object);
+
+// keyevent consumers
+static gboolean title_widget_button_press_event (GtkWidget *menuitem, 
+                                  							 GdkEventButton *event);
+static gboolean title_widget_button_release_event (GtkWidget *menuitem, 
+                                  								 GdkEventButton *event);
+static gboolean title_widget_expose_event(GtkWidget* widget, 
+                                          GdkEventExpose* event);
+
+// Dbusmenuitem properties update callback
+static void title_widget_property_update(DbusmenuMenuitem* item, gchar* property, 
+                                       GValue* value, gpointer userdata);
+static void style_name_text(TitleWidget* self);
+G_DEFINE_TYPE (TitleWidget, title_widget, GTK_TYPE_MENU_ITEM);
+
+
+
+static void
+title_widget_class_init (TitleWidgetClass *klass)
+{
+	GObjectClass 			*gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
+
+	widget_class->button_press_event = title_widget_button_press_event;
+  widget_class->button_release_event = title_widget_button_release_event;
+	widget_class->expose_event = title_widget_expose_event;
+	
+	g_type_class_add_private (klass, sizeof (TitleWidgetPrivate));
+
+	gobject_class->dispose = title_widget_dispose;
+	gobject_class->finalize = title_widget_finalize;
+
+}
+
+static void
+title_widget_init (TitleWidget *self)
+{
+	g_debug("TitleWidget::title_widget_init");
+
+	TitleWidgetPrivate * priv = TITLE_WIDGET_GET_PRIVATE(self);
+
+	GtkWidget *hbox;
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	priv->hbox = hbox;
+	g_signal_connect(G_OBJECT(twin_item), "property-changed", 
+	                 G_CALLBACK(title_widget_property_update), self);
+	// TODO - waiting theme icon name for correct usage
+	priv->player_icon = gtk_image_new_from_file("/home/ronoc/branches/sound-menu-v2/finish-indicate/indicator-sound/data/sound_icon.png"); 
+	gtk_box_pack_start(GTK_BOX (priv->hbox), priv->player_icon, FALSE, FALSE, 0);		
+	
+	priv->name = gtk_label_new(dbusmenu_menuitem_property_get(twin_item, 
+	                                                          DBUSMENU_TITLE_MENUITEM_TEXT_NAME));
+	gtk_misc_set_padding(GTK_MISC(priv->name), 10, 0);
+	gtk_box_pack_start (GTK_BOX (priv->hbox), priv->name, FALSE, FALSE, 0);		
+
+	style_name_text(self);
+	
+	gtk_widget_show_all (priv->hbox);
+  gtk_container_add (GTK_CONTAINER (self), hbox);
+	
+}
+
+static void
+title_widget_dispose (GObject *object)
+{
+	G_OBJECT_CLASS (title_widget_parent_class)->dispose (object);
+}
+
+static void
+title_widget_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (title_widget_parent_class)->finalize (object);
+}
+
+/* Suppress/consume keyevents */
+static gboolean
+title_widget_button_press_event (GtkWidget *menuitem, 
+                                  GdkEventButton *event)
+{
+	g_debug("TitleWidget::menu_press_event");
+
+	GValue value = {0};
+  g_value_init(&value, G_TYPE_BOOLEAN);
+
+	g_value_set_boolean(&value, TRUE);	
+	dbusmenu_menuitem_handle_event (twin_item, "Title menu event", &value, 0);
+	
+	return TRUE;
+}
+
+static gboolean
+title_widget_button_release_event (GtkWidget *menuitem, 
+                                  GdkEventButton *event)
+{
+	g_debug("TitleWidget::menu_release_event");
+	return TRUE;
+}
+
+static gboolean
+title_widget_expose_event(GtkWidget* widget, GdkEventExpose* event)
+{
+	TitleWidgetPrivate * priv = TITLE_WIDGET_GET_PRIVATE(widget);
+		
+	gtk_container_propagate_expose(GTK_CONTAINER(widget), priv->hbox, event); 
+	return TRUE;
+}
+
+static void 
+title_widget_property_update(DbusmenuMenuitem* item, gchar* property, 
+                                       GValue* value, gpointer userdata)
+{
+	g_return_if_fail (IS_TITLE_WIDGET (userdata));	
+	TitleWidget* mitem = TITLE_WIDGET(userdata);
+	TitleWidgetPrivate * priv = TITLE_WIDGET_GET_PRIVATE(mitem);
+	
+	if(g_ascii_strcasecmp(DBUSMENU_TITLE_MENUITEM_TEXT_NAME, property) == 0){  
+		gtk_label_set_text(GTK_LABEL(priv->name), g_value_get_string(value));
+		style_name_text(mitem);
+	}
+}
+
+static void
+style_name_text(TitleWidget* self)
+{
+	TitleWidgetPrivate * priv = TITLE_WIDGET_GET_PRIVATE(self);
+
+	char* markup;
+	markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>",
+	                                  gtk_label_get_text(GTK_LABEL(priv->name)));
+	gtk_label_set_markup (GTK_LABEL (priv->name), markup);
+	g_free(markup);
+}
+
+ /**
+ * transport_new:
+ * @returns: a new #TitleWidget.
+ **/
+GtkWidget* 
+title_widget_new(DbusmenuMenuitem *item)
+{
+	twin_item = item;
+	return g_object_new(TITLE_WIDGET_TYPE, NULL);
+}
+

=== added file 'src/title-widget.h'
--- src/title-widget.h	1970-01-01 00:00:00 +0000
+++ src/title-widget.h	2010-07-07 14:11:42 +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 __TITLE_WIDGET_H__
+#define __TITLE_WIDGET_H__
+
+#include <gtk/gtkmenuitem.h>
+#include <libdbusmenu-gtk/menu.h>
+
+G_BEGIN_DECLS
+
+#define TITLE_WIDGET_TYPE            (title_widget_get_type ())
+#define TITLE_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TITLE_WIDGET_TYPE, TitleWidget))
+#define TITLE_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TITLE_WIDGET_TYPE, TitleWidgetClass))
+#define IS_TITLE_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TITLE_WIDGET_TYPE))
+#define IS_TITLE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TITLE_WIDGET_TYPE))
+#define TITLE_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TITLE_WIDGET_TYPE, TitleWidgetClass))
+
+typedef struct _TitleWidget      TitleWidget;
+typedef struct _TitleWidgetClass TitleWidgetClass;
+
+struct _TitleWidgetClass {
+	  GtkMenuItemClass parent_class;
+};
+
+struct _TitleWidget {
+	  GtkMenuItem parent;
+};
+
+GType title_widget_get_type (void);
+GtkWidget* title_widget_new(DbusmenuMenuitem *twin_item);
+
+G_END_DECLS
+
+#endif
+

=== modified file 'src/transport-menu-item.vala'
--- src/transport-menu-item.vala	2010-06-24 16:23:39 +0000
+++ src/transport-menu-item.vala	2010-07-07 14:11:42 +0000
@@ -24,10 +24,9 @@
 public class TransportMenuitem : PlayerItem
 {
 	
-	public TransportMenuitem()
+	public TransportMenuitem(PlayerController parent)
   {
-		this.property_set(MENUITEM_PROP_TYPE, MENUITEM_TYPE);
-		debug("transport on the vala side");
+		Object(item_type: MENUITEM_TYPE, owner: parent); 
 	}
 
 	public void change_play_state(int state)
@@ -38,13 +37,8 @@
 	public override void handle_event(string name, GLib.Value input_value, uint timestamp)
 	{
 		debug("handle_event with bool value %s", input_value.get_boolean().to_string());
-		this.mpris_adaptor.toggle_playback(input_value.get_boolean());
-	}
-
-	public override void check_layout(){
-		// nothing to be done for this item - always active
-	}
-	
+		this.owner.mpris_adaptor.toggle_playback(input_value.get_boolean());	
+	}	
 
 	public static HashSet<string> attributes_format()
 	{

=== modified file 'src/transport-widget.c'
--- src/transport-widget.c	2010-06-22 13:54:03 +0000
+++ src/transport-widget.c	2010-07-07 14:11:42 +0000
@@ -26,8 +26,6 @@
 #include "common-defs.h"
 #include <gtk/gtk.h>
 
-// TODO: think about leakage: ted !
-
 static DbusmenuMenuitem* twin_item;
 
 typedef struct _TransportWidgetPrivate TransportWidgetPrivate;
@@ -136,7 +134,7 @@
 	hbox = gtk_hbox_new(TRUE, 2);
 	gchar* symbol = transport_widget_toggle_play_label(dbusmenu_menuitem_property_get_int(twin_item, DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE));
 	priv->play_button =	gtk_button_new_with_label(symbol);
-	//g_free(symbol);
+
 	gtk_box_pack_start (GTK_BOX (hbox), priv->play_button, FALSE, TRUE, 0);
 
 	priv->hbox = hbox;

=== modified file 'vapi/common-defs.vapi'
--- vapi/common-defs.vapi	2010-06-22 11:43:24 +0000
+++ vapi/common-defs.vapi	2010-07-07 14:11:42 +0000
@@ -30,4 +30,10 @@
 namespace DbusmenuTransport{
 	public const string MENUITEM_TYPE;
 	public const string MENUITEM_PLAY_STATE;
+}
+
+[CCode (cheader_filename = "common-defs.h")]
+namespace DbusmenuTitle{
+	public const string MENUITEM_TYPE;
+	public const string MENUITEM_TEXT_NAME;
 }
\ No newline at end of file


Follow ups