aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am11
-rw-r--r--src/common-defs.h13
-rwxr-xr-xsrc/indicator-sound.c339
-rw-r--r--src/metadata-menu-item.vala29
-rw-r--r--src/metadata-widget.c177
-rw-r--r--src/metadata-widget.h51
-rw-r--r--src/music-player-bridge.vala12
-rw-r--r--src/player-controller.vala67
-rw-r--r--src/slider-menu-item.c18
-rw-r--r--src/sound-service.c6
-rw-r--r--src/transport-menu-item.vala19
-rw-r--r--src/transport-widget.c216
-rw-r--r--src/transport-widget.h51
13 files changed, 840 insertions, 169 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index a1ecece..2a19c3d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,10 @@ soundmenulib_LTLIBRARIES = libsoundmenu.la
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 @@ sound-service-server.h: $(srcdir)/sound-service.xml
# 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 @@ indicator_sound_service_SOURCES = \
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)
#########################
diff --git a/src/common-defs.h b/src/common-defs.h
index 9be1da5..014d864 100644
--- a/src/common-defs.h
+++ b/src/common-defs.h
@@ -24,6 +24,13 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#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"
diff --git a/src/indicator-sound.c b/src/indicator-sound.c
index 18f48d8..9f7e136 100755
--- a/src/indicator-sound.c
+++ b/src/indicator-sound.c
@@ -38,6 +38,8 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#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 @@ struct _IndicatorSoundClass {
//GObject instance struct
struct _IndicatorSound {
IndicatorObject parent;
- GtkWidget *slider;
+ GtkWidget *slider;
IndicatorServiceManager *service;
};
// GObject Boiler plate
@@ -91,6 +93,10 @@ static void slider_grabbed(GtkWidget *widget, gpointer user_data);
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 @@ indicator_sound_class_init (IndicatorSoundClass *klass)
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 @@ indicator_sound_init (IndicatorSound *self)
{
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 @@ indicator_sound_dispose (GObject *object)
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 @@ indicator_sound_dispose (GObject *object)
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 @@ get_label (IndicatorObject * io)
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 @@ get_icon (IndicatorObject * io)
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);
+ 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);
- 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);
+ // 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 @@ new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuC
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 @@ connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer u
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 @@ Only called at startup.
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));
+ 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);
+ 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);
+ 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);
+ 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 @@ prepare_blocked_animation()
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 @@ update_state(const gint state)
{
/* 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 @@ catch_signal_sink_availability_update(DBusGProxy *proxy, gboolean available_valu
/*******************************************************************/
//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 @@ key_press_cb:
static gboolean
key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
{
- gboolean digested = FALSE;
+ 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)
+ 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)
{
- 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);
- }
+ 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;
+ }
+ 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);
}
diff --git a/src/metadata-menu-item.vala b/src/metadata-menu-item.vala
new file mode 100644
index 0000000..ef72143
--- /dev/null
+++ b/src/metadata-menu-item.vala
@@ -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
diff --git a/src/metadata-widget.c b/src/metadata-widget.c
new file mode 100644
index 0000000..a451ad7
--- /dev/null
+++ b/src/metadata-widget.c
@@ -0,0 +1,177 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@canonical.com>
+
+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);
+}
+
diff --git a/src/metadata-widget.h b/src/metadata-widget.h
new file mode 100644
index 0000000..ce7df5f
--- /dev/null
+++ b/src/metadata-widget.h
@@ -0,0 +1,51 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@canonical.com>
+
+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
+
diff --git a/src/music-player-bridge.vala b/src/music-player-bridge.vala
index c2cfc7a..89f633b 100644
--- a/src/music-player-bridge.vala
+++ b/src/music-player-bridge.vala
@@ -7,11 +7,11 @@ public class MusicPlayerBridge : GLib.Object
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 @@ public class MusicPlayerBridge : GLib.Object
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 @@ public class MusicPlayerBridge : GLib.Object
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);
}
diff --git a/src/player-controller.vala b/src/player-controller.vala
new file mode 100644
index 0000000..dcb428b
--- /dev/null
+++ b/src/player-controller.vala
@@ -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
diff --git a/src/slider-menu-item.c b/src/slider-menu-item.c
index cb72524..8a21fcf 100644
--- a/src/slider-menu-item.c
+++ b/src/slider-menu-item.c
@@ -53,8 +53,8 @@ static void slider_menu_item_class_init (SliderMenuItemClass *klass)
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 @@ static void
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 @@ handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, g
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;
}
diff --git a/src/sound-service.c b/src/sound-service.c
index e20de30..38e5fba 100644
--- a/src/sound-service.c
+++ b/src/sound-service.c
@@ -44,9 +44,9 @@ service_shutdown (IndicatorService *service, gpointer user_data)
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;
}
diff --git a/src/transport-menu-item.vala b/src/transport-menu-item.vala
new file mode 100644
index 0000000..7687a92
--- /dev/null
+++ b/src/transport-menu-item.vala
@@ -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
diff --git a/src/transport-widget.c b/src/transport-widget.c
new file mode 100644
index 0000000..2c32315
--- /dev/null
+++ b/src/transport-widget.c
@@ -0,0 +1,216 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@canonical.com>
+
+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* play_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 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->play_button = gtk_button_new_with_label(">");
+
+ gtk_box_pack_start (GTK_BOX (hbox), priv->play_button, FALSE, TRUE, 0);
+
+
+ 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);
+
+ 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)))));
+
+ return TRUE;
+}
+
+static gboolean
+transport_widget_button_release_event (GtkWidget *menuitem,
+ GdkEventButton *event)
+{
+ g_debug("TransportWidget::menu_release_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 = "||";
+ }
+ 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);
+}
+
diff --git a/src/transport-widget.h b/src/transport-widget.h
new file mode 100644
index 0000000..1d1aa6e
--- /dev/null
+++ b/src/transport-widget.h
@@ -0,0 +1,51 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@canonical.com>
+
+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
+