diff options
-rw-r--r-- | src/Makefile.am | 11 | ||||
-rw-r--r-- | src/common-defs.h | 13 | ||||
-rwxr-xr-x | src/indicator-sound.c | 339 | ||||
-rw-r--r-- | src/metadata-menu-item.vala | 29 | ||||
-rw-r--r-- | src/metadata-widget.c | 177 | ||||
-rw-r--r-- | src/metadata-widget.h | 51 | ||||
-rw-r--r-- | src/music-player-bridge.vala | 12 | ||||
-rw-r--r-- | src/player-controller.vala | 67 | ||||
-rw-r--r-- | src/slider-menu-item.c | 18 | ||||
-rw-r--r-- | src/sound-service.c | 6 | ||||
-rw-r--r-- | src/transport-menu-item.vala | 19 | ||||
-rw-r--r-- | src/transport-widget.c | 216 | ||||
-rw-r--r-- | src/transport-widget.h | 51 |
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 + |