diff options
-rw-r--r-- | configure.ac | 83 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/indicator-sound.c | 120 | ||||
-rw-r--r-- | src/metadata-widget.c | 323 | ||||
-rw-r--r-- | src/metadata-widget.h | 6 | ||||
-rw-r--r-- | src/mpris2-controller.vala | 78 | ||||
-rw-r--r-- | src/mute-menu-item.c | 5 | ||||
-rw-r--r-- | src/mute-widget.c | 141 | ||||
-rw-r--r-- | src/mute-widget.h | 67 | ||||
-rw-r--r-- | src/sound-service-marshal.list | 2 | ||||
-rw-r--r-- | src/transport-widget.c | 216 | ||||
-rw-r--r-- | src/transport-widget.h | 5 | ||||
-rw-r--r-- | src/voip-input-widget.h | 5 | ||||
-rw-r--r-- | src/volume-widget.h | 5 |
14 files changed, 767 insertions, 295 deletions
diff --git a/configure.ac b/configure.ac index b7525d7..ed34cb4 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ -AC_INIT(indicator-sound, 0.7.1, conor.curran@canonical.com) +AC_INIT(indicator-sound, 0.7.4.1, conor.curran@canonical.com) AC_PREREQ(2.53) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(indicator-sound, 0.7.1) +AM_INIT_AUTOMAKE(indicator-sound, 0.7.4.1) AM_MAINTAINER_MODE @@ -26,10 +26,21 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal]) ########################### +# GTK+ version option +########################### + +AC_ARG_WITH([gtk], + [AS_HELP_STRING([--with-gtk], + [Which version of gtk to use for the indicator @<:@default=3@:>@])], + [], + [with_gtk=3]) + +########################### # Dependencies ########################### -GTK_REQUIRED_VERSION=2.12 +GTK_REQUIRED_VERSION=2.22 +GTK3_REQUIRED_VERSION=3.0 INDICATOR_REQUIRED_VERSION=0.3.19 DBUSMENUGTK_REQUIRED_VERSION=0.3.101 POLKIT_REQUIRED_VERSION=0.92 @@ -39,26 +50,42 @@ DBUSMENUGLIB_REQUIRED_VERSION=0.3.101 GIO_2_0_REQUIRED_VERSION=2.25.13 LIBNOTIFY_REQUIRED_VERSION=0.7.0 -PKG_CHECK_MODULES(APPLET, gtk+-2.0 >= $GTK_REQUIRED_VERSION - indicator >= $INDICATOR_REQUIRED_VERSION - dbusmenu-gtk-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION - libido-0.1 >= $INDICATOR_DISPLAY_OBJECTS - libnotify >= $LIBNOTIFY_REQUIRED_VERSION) - -AC_SUBST(APPLET_CFLAGS) -AC_SUBST(APPLET_LIBS) - - PKG_CHECK_MODULES(PULSEAUDIO, libpulse-mainloop-glib >= $PULSE_AUDIO_REQUIRED_VERSION gio-unix-2.0) AC_SUBST(PULSEAUDIO_CFLAGS) AC_SUBST(PULSEAUDIO_LIBS) -PKG_CHECK_MODULES(SOUNDSERVICE, dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION - indicator >= $INDICATOR_REQUIRED_VERSION - gee-1.0 - gio-unix-2.0 - libxml-2.0) +AS_IF([test "x$with_gtk" = x3], + [PKG_CHECK_MODULES(APPLET, gtk+-3.0 >= $GTK3_REQUIRED_VERSION + indicator3-0.4 >= $INDICATOR_REQUIRED_VERSION + dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION + libido3-0.1 >= $INDICATOR_DISPLAY_OBJECTS + libnotify >= $LIBNOTIFY_REQUIRED_VERSION) + + PKG_CHECK_MODULES(SOUNDSERVICE, dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION + indicator3-0.4 + gee-1.0 + gio-unix-2.0 + libxml-2.0) + ], + [test "x$with_gtk" = x2], + [PKG_CHECK_MODULES(APPLET, gtk+-2.0 >= $GTK_REQUIRED_VERSION + indicator-0.4 >= $INDICATOR_REQUIRED_VERSION + dbusmenu-gtk-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION + libido-0.1 >= $INDICATOR_DISPLAY_OBJECTS + libnotify >= $LIBNOTIFY_REQUIRED_VERSION) + + PKG_CHECK_MODULES(SOUNDSERVICE, dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION + indicator-0.4 + gee-1.0 + gio-unix-2.0 + libxml-2.0) + ], + [AC_MSG_FAILURE([Value for --with-gtk was neither 2 nor 3])] +) +AC_SUBST(APPLET_CFLAGS) +AC_SUBST(APPLET_LIBS) + AC_SUBST(SOUNDSERVICE_CFLAGS) AC_SUBST(SOUNDSERVICE_LIBS) @@ -83,13 +110,20 @@ AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all # Indicator Info ########################### -if test "x$with_localinstall" = "xyes"; then +AS_IF([test "x$with_localinstall" = "xyes"], + [ INDICATORDIR="${libdir}/indicators/2/" - INDICATORICONSDIR="${datadir}/indicator-applet/icons/" -else - INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator` - INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir indicator` -fi + INDICATORICONSDIR="${datadir}/indicator-sound/icons/" + ], + [AS_IF([test "x$with_gtk" = "x2"], + [ + INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator-0.4` + INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir indicator-0.4` + ], + [ + INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator3-0.4` + INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir indicator3-0.4` + ])]) AC_SUBST(INDICATORDIR) AC_SUBST(INDICATORICONSDIR) @@ -163,4 +197,5 @@ AC_MSG_NOTICE([ SUS Indicator Configuration: Prefix: $prefix + GTK+: $with_gtk ]) diff --git a/src/Makefile.am b/src/Makefile.am index 6917aee..2381429 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,8 @@ libsoundmenu_la_SOURCES = \ transport-widget.h \ metadata-widget.c \ metadata-widget.h \ + mute-widget.c \ + mute-widget.h \ volume-widget.c \ volume-widget.h \ voip-input-widget.c \ @@ -125,11 +127,11 @@ DBUS_SPECS = \ gen-%.xml.h: %.xml @echo "Building $@ from $<" - @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $<)));" > $@ + @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<))));" > $@ gen-%.xml.c: %.xml @echo "Building $@ from $<" - @echo "const char * _$(subst -,_,$(subst .,_,$(basename $<))) = " > $@ + @echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@ @sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@ @echo ";" >> $@ diff --git a/src/indicator-sound.c b/src/indicator-sound.c index f2ec0a2..7c72900 100644 --- a/src/indicator-sound.c +++ b/src/indicator-sound.c @@ -23,7 +23,11 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib/gi18n.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> +#if GTK_CHECK_VERSION(3, 0, 0) +#include <libdbusmenu-gtk3/menu.h> +#else #include <libdbusmenu-gtk/menu.h> +#endif #include <libido/idoscalemenuitem.h> #include <gio/gio.h> @@ -35,6 +39,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include "voip-input-widget.h" #include "dbus-shared-names.h" #include "sound-state-manager.h" +#include "mute-widget.h" #include "gen-sound-service.xml.h" #include "common-defs.h" @@ -45,6 +50,7 @@ struct _IndicatorSoundPrivate { GtkWidget* volume_widget; GtkWidget* voip_widget; + MuteWidget *mute_widget; GList* transport_widgets_list; GDBusProxy *dbus_proxy; SoundStateManager* state_manager; @@ -68,9 +74,12 @@ static GtkLabel * get_label (IndicatorObject * io); static GtkImage * get_icon (IndicatorObject * io); static GtkMenu * get_menu (IndicatorObject * io); static const gchar * get_accessible_desc (IndicatorObject * io); -static void indicator_sound_scroll (IndicatorObject* io, - gint delta, +static void indicator_sound_scroll (IndicatorObject * io, + IndicatorObjectEntry * entry, gint delta, IndicatorScrollDirection direction); +static void indicator_sound_middle_click (IndicatorObject * io, + IndicatorObjectEntry * entry, + guint time, gpointer data); //key/moust event handlers static gboolean key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data); @@ -93,6 +102,10 @@ static gboolean new_metadata_widget (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data); +static gboolean new_mute_widget (DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client, + gpointer user_data); // DBUS communication static GDBusNodeInfo *node_info = NULL; @@ -120,7 +133,8 @@ indicator_sound_class_init (IndicatorSoundClass *klass) io_class->get_image = get_icon; io_class->get_menu = get_menu; io_class->get_accessible_desc = get_accessible_desc; - io_class->scroll = indicator_sound_scroll; + io_class->entry_scrolled = indicator_sound_scroll; + io_class->secondary_activate = indicator_sound_middle_click; } static void @@ -133,6 +147,7 @@ indicator_sound_init (IndicatorSound *self) IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); priv->volume_widget = NULL; priv->voip_widget = NULL; + priv->mute_widget = NULL; priv->dbus_proxy = NULL; GList* t_list = NULL; priv->transport_widgets_list = t_list; @@ -203,6 +218,9 @@ get_menu (IndicatorObject * io) dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client), DBUSMENU_METADATA_MENUITEM_TYPE, new_metadata_widget); + dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client), + DBUSMENU_MUTE_MENUITEM_TYPE, + new_mute_widget); // Note: Not ideal but all key handling needs to be managed here and then // delegated to the appropriate widget. g_signal_connect (menu, "key-press-event", G_CALLBACK(key_press_cb), io); @@ -455,10 +473,60 @@ new_voip_slider_widget (DbusmenuMenuitem * newitem, return TRUE; } +static gboolean +new_mute_widget(DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client, + gpointer user_data) +{ + IndicatorObject *io = NULL; + + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); + g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); + + io = g_object_get_data (G_OBJECT (client), "indicator"); + IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); + + if (priv->mute_widget != NULL){ + g_object_unref (priv->mute_widget); + priv->mute_widget = NULL; + } + + priv->mute_widget = mute_widget_new(newitem); + GtkMenuItem *item = mute_widget_get_menu_item (priv->mute_widget); + + dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), + newitem, + item, + parent); + + return TRUE; +} + /*******************************************************************/ //UI callbacks /******************************************************************/ +static GtkWidget * +get_current_item (GtkContainer * container) +{ + GList *children = gtk_container_get_children (container); + GList *iter; + GtkWidget *rv = NULL; + + /* Suprisingly, GTK+ doesn't really let us query "what is the currently + selected item?". But it does note it internally by prelighting the + widget, so we watch for that. */ + for (iter = children; iter; iter = iter->next) { + if (gtk_widget_get_state (GTK_WIDGET (iter->data)) & GTK_STATE_PRELIGHT) { + rv = GTK_WIDGET (iter->data); + break; + } + } + + return rv; +} + /** key_press_cb: **/ @@ -473,7 +541,7 @@ key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(indicator); GtkWidget *menuitem; - menuitem = GTK_MENU_SHELL (widget)->active_menu_item; + menuitem = get_current_item (GTK_CONTAINER (widget)); if (IDO_IS_SCALE_MENU_ITEM(menuitem) == TRUE){ gdouble current_value = 0; @@ -502,19 +570,19 @@ key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) } switch (event->keyval) { - case GDK_Right: + case GDK_KEY_Right: digested = TRUE; new_value = current_value + five_percent; break; - case GDK_Left: + case GDK_KEY_Left: digested = TRUE; new_value = current_value - five_percent; break; - case GDK_plus: + case GDK_KEY_plus: digested = TRUE; new_value = current_value + five_percent; break; - case GDK_minus: + case GDK_KEY_minus: digested = TRUE; new_value = current_value - five_percent; break; @@ -542,12 +610,12 @@ key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) } switch (event->keyval) { - case GDK_Right: + case GDK_KEY_Right: transport_widget_react_to_key_press_event ( transport_widget, TRANSPORT_ACTION_NEXT ); digested = TRUE; break; - case GDK_Left: + case GDK_KEY_Left: transport_widget_react_to_key_press_event ( transport_widget, TRANSPORT_ACTION_PREVIOUS ); digested = TRUE; @@ -557,8 +625,8 @@ key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) TRANSPORT_ACTION_PLAY_PAUSE ); digested = TRUE; break; - case GDK_Up: - case GDK_Down: + case GDK_KEY_Up: + case GDK_KEY_Down: digested = FALSE; break; default: @@ -585,7 +653,7 @@ key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) GtkWidget *menuitem; - menuitem = GTK_MENU_SHELL (widget)->active_menu_item; + menuitem = get_current_item (GTK_CONTAINER (widget)); if (IS_TRANSPORT_WIDGET(menuitem) == TRUE) { TransportWidget* transport_widget = NULL; GList* elem; @@ -597,12 +665,12 @@ key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) } switch (event->keyval) { - case GDK_Right: + case GDK_KEY_Right: transport_widget_react_to_key_release_event ( transport_widget, TRANSPORT_ACTION_NEXT ); digested = TRUE; break; - case GDK_Left: + case GDK_KEY_Left: transport_widget_react_to_key_release_event ( transport_widget, TRANSPORT_ACTION_PREVIOUS ); digested = TRUE; @@ -612,8 +680,8 @@ key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) TRANSPORT_ACTION_PLAY_PAUSE ); digested = TRUE; break; - case GDK_Up: - case GDK_Down: + case GDK_KEY_Up: + case GDK_KEY_Down: digested = FALSE; break; default: @@ -624,8 +692,8 @@ key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) } static void -indicator_sound_scroll (IndicatorObject *io, gint delta, - IndicatorScrollDirection direction) +indicator_sound_scroll (IndicatorObject * io, IndicatorObjectEntry * entry, + gint delta, IndicatorScrollDirection direction) { //g_debug("indicator-sound-scroll - current slider value"); IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); @@ -643,9 +711,9 @@ indicator_sound_scroll (IndicatorObject *io, gint delta, GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (slider)); //g_debug("indicator-sound-scroll - current slider value %f", value); if (direction == INDICATOR_OBJECT_SCROLL_UP) { - value += adj->step_increment; + value += gtk_adjustment_get_step_increment (adj); } else { - value -= adj->step_increment; + value -= gtk_adjustment_get_step_increment (adj); } //g_debug("indicator-sound-scroll - update slider with value %f", value); volume_widget_update(VOLUME_WIDGET(priv->volume_widget), value, "scroll updates"); @@ -653,6 +721,16 @@ indicator_sound_scroll (IndicatorObject *io, gint delta, sound_state_manager_show_notification (priv->state_manager, value); } +static void +indicator_sound_middle_click (IndicatorObject * io, IndicatorObjectEntry * entry, + guint time, gpointer data) +{ + IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); + g_return_if_fail (priv); + + mute_widget_toggle(priv->mute_widget); +} + void update_accessible_desc (IndicatorObject * io) { diff --git a/src/metadata-widget.c b/src/metadata-widget.c index f687d0c..ee9957f 100644 --- a/src/metadata-widget.c +++ b/src/metadata-widget.c @@ -46,6 +46,7 @@ struct _MetadataWidgetPrivate GtkWidget* player_label; GdkPixbuf* icon_buf; DbusmenuMenuitem* twin_item; + gint current_height; }; #define METADATA_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), METADATA_WIDGET_TYPE, MetadataWidgetPrivate)) @@ -55,13 +56,9 @@ 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); -static gboolean metadata_image_expose (GtkWidget *image, - GdkEventExpose *event, - gpointer user_data); static void metadata_widget_set_style (GtkWidget* button, GtkStyle* style); static void metadata_widget_set_twin_item (MetadataWidget* self, DbusmenuMenuitem* twin_item); - // keyevent consumers static gboolean metadata_widget_button_release_event (GtkWidget *menuitem, GdkEventButton *event); @@ -72,15 +69,34 @@ static void metadata_widget_property_update (DbusmenuMenuitem* item, gpointer userdata); static void metadata_widget_style_labels ( MetadataWidget* self, GtkLabel* label); -static void draw_album_art_placeholder ( GtkWidget *metadata); -static void draw_album_border ( GtkWidget *metadata, gboolean selected); +static void draw_album_art_placeholder (GtkWidget *metadata); + +static void draw_album_border (GtkWidget *metadata, gboolean selected); static void metadata_widget_selection_received_event_callback( GtkWidget *widget, GtkSelectionData *data, guint time, gpointer user_data); -static gboolean metadata_widget_icon_triangle_draw_cb ( GtkWidget *image, - GdkEventExpose *event, - gpointer user_data ); + + + +#if GTK_CHECK_VERSION(3, 0, 0) +static void metadata_widget_get_preferred_width (GtkWidget* self, + gint* minimum_width, + gint* natural_width); +static gboolean metadata_widget_icon_triangle_draw_cb_gtk_3 (GtkWidget *image, + cairo_t* cr, + gpointer user_data); +static gboolean metadata_image_expose_gtk_3 (GtkWidget *image, + cairo_t* cr, + gpointer user_data); +#else +static gboolean metadata_widget_icon_triangle_draw_cb (GtkWidget *image, + GdkEventExpose *event, + gpointer user_data); +static gboolean metadata_image_expose (GtkWidget *image, + GdkEventExpose *event, + gpointer user_data); +#endif static void metadata_widget_set_icon (MetadataWidget *self); static void metadata_widget_handle_resizing (MetadataWidget* self); @@ -95,15 +111,15 @@ metadata_widget_class_init (MetadataWidgetClass *klass) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); widget_class->button_release_event = metadata_widget_button_release_event; - + #if GTK_CHECK_VERSION(3, 0, 0) + widget_class->get_preferred_width = metadata_widget_get_preferred_width; + #endif 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) { @@ -116,20 +132,30 @@ metadata_widget_init (MetadataWidget *self) hbox = gtk_hbox_new(FALSE, 0); priv->meta_data_h_box = hbox; - + priv->current_height = 1; + // image priv->album_art = gtk_image_new(); priv->image_path = g_string_new(""); priv->old_image_path = g_string_new(""); + #if GTK_CHECK_VERSION(3, 0, 0) + g_signal_connect(priv->album_art, "draw", + G_CALLBACK(metadata_image_expose_gtk_3), + GTK_WIDGET(self)); + + g_signal_connect_after (GTK_WIDGET(self), "draw", + G_CALLBACK(metadata_widget_icon_triangle_draw_cb_gtk_3), + GTK_WIDGET(self)); + #else g_signal_connect(priv->album_art, "expose-event", G_CALLBACK(metadata_image_expose), GTK_WIDGET(self)); g_signal_connect_after (GTK_WIDGET(self), "expose-event", G_CALLBACK(metadata_widget_icon_triangle_draw_cb), - GTK_WIDGET(self)); - + GTK_WIDGET(self)); + #endif gtk_box_pack_start (GTK_BOX (priv->meta_data_h_box), priv->album_art, FALSE, @@ -144,6 +170,7 @@ metadata_widget_init (MetadataWidget *self) gtk_misc_set_alignment(GTK_MISC(artist), (gfloat)0, (gfloat)0); gtk_misc_set_padding (GTK_MISC(artist), (gfloat)10, (gfloat)0); gtk_widget_set_size_request (artist, 140, 15); + gtk_label_set_ellipsize(GTK_LABEL(artist), PANGO_ELLIPSIZE_MIDDLE); metadata_widget_style_labels(self, GTK_LABEL(artist)); priv->artist_label = artist; @@ -153,6 +180,7 @@ metadata_widget_init (MetadataWidget *self) piece = gtk_label_new(""); gtk_misc_set_alignment(GTK_MISC(piece), (gfloat)0, (gfloat)0); gtk_misc_set_padding (GTK_MISC(piece), (gfloat)10, (gfloat)-5); + gtk_widget_set_size_request (piece, 140, 15); gtk_label_set_ellipsize(GTK_LABEL(piece), PANGO_ELLIPSIZE_MIDDLE); metadata_widget_style_labels(self, GTK_LABEL(piece)); @@ -164,6 +192,7 @@ metadata_widget_init (MetadataWidget *self) gtk_misc_set_alignment(GTK_MISC(container), (gfloat)0, (gfloat)0); gtk_misc_set_padding (GTK_MISC(container), (gfloat)10, (gfloat)0); gtk_widget_set_size_request (container, 140, 15); + gtk_label_set_ellipsize(GTK_LABEL(container), PANGO_ELLIPSIZE_MIDDLE); metadata_widget_style_labels(self, GTK_LABEL(container)); priv->container_label = container; @@ -189,9 +218,15 @@ metadata_widget_init (MetadataWidget *self) priv->player_label = player_label; gtk_box_pack_start (GTK_BOX(outer_v_box), priv->player_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(outer_v_box), priv->meta_data_h_box, FALSE, FALSE, 0); gtk_container_add (GTK_CONTAINER (self), outer_v_box); + + gtk_widget_show_all (priv->meta_data_h_box); + gtk_widget_set_no_show_all (priv->meta_data_h_box, TRUE); + + gtk_widget_hide (priv->meta_data_h_box); } static void @@ -212,12 +247,128 @@ metadata_widget_finalize (GObject *object) } +#if GTK_CHECK_VERSION(3, 0, 0) +static void +metadata_widget_get_preferred_width (GtkWidget* self, + gint* minimum_width, + gint* natural_width) +{ + *minimum_width = *natural_width = 200; +} /** * We override the expose method to enable primitive drawing of the * empty album art image and rounded rectangles on the album art. */ static gboolean -metadata_image_expose (GtkWidget *metadata, GdkEventExpose *event, gpointer user_data) +metadata_image_expose_gtk_3 (GtkWidget *metadata, + cairo_t* cr, + gpointer user_data) +{ + g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); + MetadataWidget* widget = METADATA_WIDGET(user_data); + MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(widget); + + if ( TRUE == dbusmenu_menuitem_property_get_bool (DBUSMENU_MENUITEM(priv->twin_item), + DBUSMENU_METADATA_MENUITEM_HIDE_TRACK_DETAILS)) + { + return FALSE; + } + + draw_album_border (metadata, FALSE); + + if(priv->image_path->len > 0){ + if(g_string_equal(priv->image_path, priv->old_image_path) == FALSE || + priv->theme_change_occured == TRUE){ + priv->theme_change_occured = FALSE; + GdkPixbuf* pixbuf; + pixbuf = gdk_pixbuf_new_from_file_at_size(priv->image_path->str, 60, 60, NULL); + + if(GDK_IS_PIXBUF(pixbuf) == FALSE){ + gtk_image_clear ( GTK_IMAGE(priv->album_art)); + gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), 60, 60); + draw_album_art_placeholder(metadata); + return FALSE; + } + + gtk_image_set_from_pixbuf(GTK_IMAGE(priv->album_art), pixbuf); + gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), + gdk_pixbuf_get_width(pixbuf), + gdk_pixbuf_get_height(pixbuf)); + + g_string_erase (priv->old_image_path, 0, -1); + g_string_overwrite (priv->old_image_path, 0, priv->image_path->str); + g_object_unref(pixbuf); + } + return FALSE; + } + gtk_image_clear (GTK_IMAGE(priv->album_art)); + gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), 60, 60); + draw_album_art_placeholder(metadata); + return FALSE; +} + +// Draw the triangle if the player is running ... +static gboolean +metadata_widget_icon_triangle_draw_cb_gtk_3 (GtkWidget *widget, + cairo_t* cr, + gpointer user_data) +{ + + g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); + MetadataWidget* meta = METADATA_WIDGET(user_data); + MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(meta); + + GtkStyle *style; + int x, y, arrow_width, arrow_height; + + gint offset = 3; + arrow_width = 5; + arrow_height = 9; + + style = gtk_widget_get_style (widget); + + GtkAllocation allocation; + gtk_widget_get_allocation (widget, &allocation); + x = allocation.x; + y = 0; + + // Draw player icon + if (priv->icon_buf != NULL){ + gdk_cairo_set_source_pixbuf (cr, + priv->icon_buf, + x + arrow_width + 1, + y + offset); + cairo_paint (cr); + } + + // Draw triangle but only if the player is running. + if (dbusmenu_menuitem_property_get_bool (priv->twin_item, + DBUSMENU_METADATA_MENUITEM_PLAYER_RUNNING)){ + y += (double)arrow_height/2.0 + offset; + cairo_set_line_width (cr, 1.0); + + //g_debug ("triangle drawing"); + + cairo_move_to (cr, x, y); + cairo_line_to (cr, x, y + arrow_height); + cairo_line_to (cr, x + arrow_width, y + (double)arrow_height/2.0); + cairo_close_path (cr); + cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0, + style->fg[gtk_widget_get_state(widget)].green/65535.0, + style->fg[gtk_widget_get_state(widget)].blue/65535.0); + cairo_fill (cr); + } + + return FALSE; +} + +// GTK 2 Expose handler +#else + +static gboolean +metadata_image_expose (GtkWidget *metadata, + GdkEventExpose *event, + gpointer user_data) { g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); MetadataWidget* widget = METADATA_WIDGET(user_data); @@ -252,7 +403,6 @@ metadata_image_expose (GtkWidget *metadata, GdkEventExpose *event, gpointer user g_string_erase (priv->old_image_path, 0, -1); g_string_overwrite (priv->old_image_path, 0, priv->image_path->str); - g_object_unref(pixbuf); } return FALSE; @@ -263,11 +413,74 @@ metadata_image_expose (GtkWidget *metadata, GdkEventExpose *event, gpointer user return FALSE; } + +// Draw the triangle if the player is running ... +static gboolean +metadata_widget_icon_triangle_draw_cb (GtkWidget *widget, + GdkEventExpose *event, + gpointer user_data) +{ + g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); + MetadataWidget* meta = METADATA_WIDGET(user_data); + MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(meta); + + GtkStyle *style; + cairo_t *cr; + int x, y, arrow_width, arrow_height; + + gint offset = 3; + arrow_width = 5; + arrow_height = 9; + + style = gtk_widget_get_style (widget); + + cr = (cairo_t*) gdk_cairo_create (gtk_widget_get_window (widget)); + + GtkAllocation allocation; + gtk_widget_get_allocation (widget, &allocation); + x = allocation.x; + y = allocation.y; + + // Draw player icon + if (priv->icon_buf != NULL){ + gdk_cairo_set_source_pixbuf (cr, + priv->icon_buf, + x + arrow_width + 1, + y + offset); + cairo_paint (cr); + } + + // Draw triangle but only if the player is running. + if (dbusmenu_menuitem_property_get_bool (priv->twin_item, + DBUSMENU_METADATA_MENUITEM_PLAYER_RUNNING)){ + y += (double)arrow_height/2.0 + offset; + cairo_set_line_width (cr, 1.0); + + cairo_move_to (cr, x, y); + cairo_line_to (cr, x, y + arrow_height); + cairo_line_to (cr, x + arrow_width, y + (double)arrow_height/2.0); + cairo_close_path (cr); + cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0, + style->fg[gtk_widget_get_state(widget)].green/65535.0, + style->fg[gtk_widget_get_state(widget)].blue/65535.0); + cairo_fill (cr); + } + + cairo_destroy (cr); + return FALSE; +} +#endif + static void draw_album_border(GtkWidget *metadata, gboolean selected) { cairo_t *cr; - cr = gdk_cairo_create (metadata->window); + cr = gdk_cairo_create (gtk_widget_get_window (metadata)); + #if GTK_CHECK_VERSION(3, 0, 0) + gtk_style_context_add_class (gtk_widget_get_style_context (metadata), + "menu"); + #endif + GtkStyle *style; style = gtk_widget_get_style (metadata); @@ -357,7 +570,7 @@ static void draw_album_art_placeholder(GtkWidget *metadata) { cairo_t *cr; - cr = gdk_cairo_create (metadata->window); + cr = gdk_cairo_create (gtk_widget_get_window (metadata)); GtkStyle *style; style = gtk_widget_get_style (metadata); @@ -485,10 +698,7 @@ metadata_widget_property_update(DbusmenuMenuitem* item, gchar* property, else if(g_ascii_strcasecmp(DBUSMENU_METADATA_MENUITEM_ARTURL, property) == 0){ g_string_erase(priv->image_path, 0, -1); g_string_overwrite(priv->image_path, 0, g_variant_get_string (value, NULL)); - // if its a remote image queue a redraw incase the download took too long - //if (g_str_has_prefix(g_variant_get_string (value, NULL), g_get_user_cache_dir())){ gtk_widget_queue_draw(GTK_WIDGET(mitem)); - //} } else if (g_ascii_strcasecmp (DBUSMENU_METADATA_MENUITEM_PLAYER_NAME, property) == 0){ gtk_label_set_label (GTK_LABEL (priv->player_label), @@ -506,26 +716,13 @@ static void metadata_widget_handle_resizing (MetadataWidget* self) { MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(self); - + if (dbusmenu_menuitem_property_get_bool (priv->twin_item, DBUSMENU_METADATA_MENUITEM_HIDE_TRACK_DETAILS) == TRUE){ gtk_widget_hide (priv->meta_data_h_box); - gtk_widget_hide (priv->artist_label); - gtk_widget_hide (priv->piece_label); - gtk_widget_hide (priv->container_label); - gtk_widget_hide (priv->album_art); - gtk_widget_hide (priv->meta_data_v_box); - gtk_widget_set_size_request(GTK_WIDGET(self), 200, 20); } else{ - gtk_widget_show (priv->meta_data_h_box); - gtk_widget_show (priv->artist_label); - gtk_widget_show (priv->piece_label); - gtk_widget_show (priv->container_label); - gtk_widget_show (priv->album_art); - gtk_widget_show (priv->meta_data_v_box); - gtk_widget_set_size_request(GTK_WIDGET(self), 200, 95); } gtk_widget_queue_draw(GTK_WIDGET(self)); } @@ -639,60 +836,6 @@ metadata_widget_set_twin_item (MetadataWidget* self, metadata_widget_handle_resizing (self); } -// Draw the triangle if the player is running ... -static gboolean -metadata_widget_icon_triangle_draw_cb (GtkWidget *widget, - GdkEventExpose *event, - gpointer user_data) -{ - g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); - MetadataWidget* meta = METADATA_WIDGET(user_data); - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(meta); - - GtkStyle *style; - cairo_t *cr; - int x, y, arrow_width, arrow_height; - - gint offset = 3; - arrow_width = 5; - arrow_height = 9; - - style = gtk_widget_get_style (widget); - - cr = (cairo_t*) gdk_cairo_create (widget->window); - - x = widget->allocation.x; - y = widget->allocation.y; - - // Draw player icon - if (priv->icon_buf != NULL){ - gdk_cairo_set_source_pixbuf (cr, - priv->icon_buf, - x + arrow_width + 1, - y + offset); - cairo_paint (cr); - } - - // Draw triangle but only if the player is running. - if (dbusmenu_menuitem_property_get_bool (priv->twin_item, - DBUSMENU_METADATA_MENUITEM_PLAYER_RUNNING)){ - y += (double)arrow_height/2.0 + offset; - cairo_set_line_width (cr, 1.0); - - cairo_move_to (cr, x, y); - cairo_line_to (cr, x, y + arrow_height); - cairo_line_to (cr, x + arrow_width, y + (double)arrow_height/2.0); - cairo_close_path (cr); - cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0, - style->fg[gtk_widget_get_state(widget)].green/65535.0, - style->fg[gtk_widget_get_state(widget)].blue/65535.0); - cairo_fill (cr); - } - - cairo_destroy (cr); - return FALSE; -} - /** * transport_new: * @returns: a new #MetadataWidget. diff --git a/src/metadata-widget.h b/src/metadata-widget.h index 30b629c..b0123a3 100644 --- a/src/metadata-widget.h +++ b/src/metadata-widget.h @@ -19,8 +19,12 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef __METADATA_WIDGET_H__ #define __METADATA_WIDGET_H__ -#include <gtk/gtkmenuitem.h> +#include <gtk/gtk.h> +#if GTK_CHECK_VERSION(3, 0, 0) +#include <libdbusmenu-gtk3/menuitem.h> +#else #include <libdbusmenu-gtk/menuitem.h> +#endif G_BEGIN_DECLS diff --git a/src/mpris2-controller.vala b/src/mpris2-controller.vala index 2975066..8eeac08 100644 --- a/src/mpris2-controller.vala +++ b/src/mpris2-controller.vala @@ -19,10 +19,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>. using Dbusmenu; using Transport; -/* - This class will entirely replace mpris-controller.vala hence why there is no - point in trying to get encorporate both into the same object model. - */ public class Mpris2Controller : GLib.Object { public MprisRoot mpris2_root {get; construct;} @@ -60,12 +56,22 @@ public class Mpris2Controller : GLib.Object critical("Problems connecting to the session bus - %s", e.message); } } - + /* + * property_changed_cb + * Called when a property changed signal is emitted from any of mpris + * objects on the bus. + * Note that the signal will be received by each instance for each player + * and at that moment there is no way to know what player that signal + * came from therefore it is necessary to query each relevant property + * to update the respective dbusmenuitem property inorder to keep the UI in sync + * Please also note due to some race condition in the depths of gdbus + * a timeout is needed between receiving the prop update and query the respective property. + * This can be seen at various points below. + */ public void property_changed_cb ( string interface_source, HashTable<string, Variant?> changed_properties, string[] invalid ) { - //debug("properties-changed for interface %s and owner %s", interface_source, this.owner.dbus_name); if ( changed_properties == null || interface_source.has_prefix ( MPRIS_PREFIX ) == false ){ warning("Property-changed hash is null or this is an interface that doesn't concern us"); @@ -73,31 +79,18 @@ public class Mpris2Controller : GLib.Object } Variant? play_v = changed_properties.lookup("PlaybackStatus"); if(play_v != null){ - // Race condition sometimes appears with the playback status - // 200ms timeout ensures we have the correct playback status at all times. string state = this.player.PlaybackStatus; - //debug("in the property update and the playback status = %s and update = %s", state, (string)play_v); Timeout.add ( 200, ensure_correct_playback_status ); Transport.State p = (Transport.State)this.determine_play_state(state); (this.owner.custom_items[PlayerController.widget_order.TRANSPORT] as TransportMenuitem).change_play_state(p); } Variant? meta_v = changed_properties.lookup("Metadata"); - if(meta_v != null){ - GLib.HashTable<string, Variant?> changed_updates = clean_metadata(); - PlayerItem metadata = this.owner.custom_items[PlayerController.widget_order.METADATA]; - metadata.reset (MetadataMenuitem.relevant_attributes_for_ui()); - metadata.update ( changed_updates, - MetadataMenuitem.relevant_attributes_for_ui()); - MetadataMenuitem md = this.owner.custom_items[PlayerController.widget_order.METADATA] as MetadataMenuitem; - bool collapsing = !metadata.populated(MetadataMenuitem.relevant_attributes_for_ui()); - md.should_collapse(collapsing); - - debug ("Should metadata collapse %s", collapsing.to_string()); + if(meta_v != null) + { + Timeout.add ( 200, ensure_correct_metadata ); } Variant? playlist_v = changed_properties.lookup("ActivePlaylist"); if ( playlist_v != null && this.owner.use_playlists == true ){ - // Once again A GDBus race condition, the property_changed signal is sent - // before the value is set on the respective property. Timeout.add (300, this.fetch_active_playlist); } Variant? playlist_count_v = changed_properties.lookup("PlaylistCount"); @@ -116,9 +109,23 @@ public class Mpris2Controller : GLib.Object md.alter_label (this.mpris2_root.Identity); } } - - private bool ensure_correct_playback_status(){ - //debug("TEST playback status = %s", this.player.PlaybackStatus); + + private bool ensure_correct_metadata () + { + GLib.HashTable<string, Variant?> changed_updates = clean_metadata(); + PlayerItem metadata = this.owner.custom_items[PlayerController.widget_order.METADATA]; + metadata.reset (MetadataMenuitem.relevant_attributes_for_ui()); + metadata.update ( changed_updates, + MetadataMenuitem.relevant_attributes_for_ui()); + MetadataMenuitem md = this.owner.custom_items[PlayerController.widget_order.METADATA] as MetadataMenuitem; + bool collapsing = !metadata.populated(MetadataMenuitem.relevant_attributes_for_ui()); + md.should_collapse(collapsing); + + return false; + } + + private bool ensure_correct_playback_status() + { Transport.State p = (Transport.State)this.determine_play_state(this.player.PlaybackStatus); (this.owner.custom_items[PlayerController.widget_order.TRANSPORT] as TransportMenuitem).change_play_state(p); return false; @@ -130,19 +137,16 @@ public class Mpris2Controller : GLib.Object Variant? artist_v = this.player.Metadata.lookup("xesam:artist"); if(artist_v != null){ - Variant? v_artists = this.player.Metadata.lookup("xesam:artist"); - //debug("artists is of type %s", v_artists.get_type_string ()); string display_artists; - if(v_artists.get_type_string() == "s"){ - //debug("SPOTIFY is that you ?"); - display_artists = v_artists.get_string(); + // Accomodate Spotify (should return 'as' and not 's') + if(artist_v.get_type_string() == "s"){ + display_artists = artist_v.get_string(); } else{ - string[] artists = v_artists.dup_strv(); + string[] artists = artist_v.dup_strv(); display_artists = string.joinv(", ", artists); } changed_updates.replace("xesam:artist", display_artists); - //debug("artist : %s", (string)changed_updates.lookup("xesam:artist")); } return changed_updates; } @@ -181,7 +185,6 @@ public class Mpris2Controller : GLib.Object public void transport_update(Transport.Action command) { - //debug("transport_event input = %i", (int)command); if(command == Transport.Action.PLAY_PAUSE){ this.player.PlayPause.begin(); } @@ -192,11 +195,9 @@ public class Mpris2Controller : GLib.Object this.player.Next.begin(); } else if(command == Transport.Action.REWIND){ - //debug("transport_event rewind = %i", (int)command); this.player.Seek.begin(-500000); } else if(command == Transport.Action.FORWIND){ - //debug("transport_event input = %i", (int)command); this.player.Seek.begin(400000); } } @@ -230,12 +231,10 @@ public class Mpris2Controller : GLib.Object false); } catch (IOError e){ - //debug("Could not fetch playlists because %s", e.message); return; } if( current_playlists != null ){ - //debug( "Size of the playlist array = %i", current_playlists.length ); PlaylistsMenuitem playlists_item = this.owner.custom_items[PlayerController.widget_order.PLAYLISTS] as PlaylistsMenuitem; playlists_item.update(current_playlists); } @@ -248,7 +247,8 @@ public class Mpris2Controller : GLib.Object private bool fetch_active_playlist() { if (this.playlists.ActivePlaylist.valid == false){ - //debug(" We don't have an active playlist"); + // TODO + // What happens here ? } PlaylistsMenuitem playlists_item = this.owner.custom_items[PlayerController.widget_order.PLAYLISTS] as PlaylistsMenuitem; playlists_item.active_playlist_update ( this.playlists.ActivePlaylist.details ); @@ -261,7 +261,7 @@ public class Mpris2Controller : GLib.Object this.playlists.ActivatePlaylist.begin(path); } catch(IOError e){ - //debug("Could not activate playlist %s because %s", (string)path, e.message); + warning ("Could not activate playlist %s because %s", (string)path, e.message); } } } diff --git a/src/mute-menu-item.c b/src/mute-menu-item.c index 2876be3..0e6a46f 100644 --- a/src/mute-menu-item.c +++ b/src/mute-menu-item.c @@ -63,6 +63,11 @@ mute_menu_item_init (MuteMenuItem *self) MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE(self); priv->button = NULL; priv->button = dbusmenu_menuitem_new(); + + dbusmenu_menuitem_property_set(priv->button, + DBUSMENU_MENUITEM_PROP_TYPE, + DBUSMENU_MUTE_MENUITEM_TYPE); + dbusmenu_menuitem_property_set_bool (priv->button, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); diff --git a/src/mute-widget.c b/src/mute-widget.c new file mode 100644 index 0000000..97c87ff --- /dev/null +++ b/src/mute-widget.c @@ -0,0 +1,141 @@ +/* +Copyright 2011 Canonical Ltd. + +Authors: + Marco Trevisan (Treviño) <mail@3v1n0.net> + +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 <glib.h> +#include "mute-widget.h" +#include "common-defs.h" +#include "indicator-sound.h" + +typedef struct _MuteWidgetPrivate MuteWidgetPrivate; + +struct _MuteWidgetPrivate +{ + DbusmenuMenuitem *item; + GtkMenuItem *gitem; +}; + +#define MUTE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MUTE_WIDGET_TYPE, MuteWidgetPrivate)) + +/* Prototypes */ +static void mute_widget_class_init (MuteWidgetClass *klass); +static void mute_widget_init (MuteWidget *self); +static void mute_widget_dispose (GObject *object); +static void mute_widget_finalize (GObject *object); + +G_DEFINE_TYPE (MuteWidget, mute_widget, G_TYPE_OBJECT); + +static void +mute_widget_class_init (MuteWidgetClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->dispose = mute_widget_dispose; + gobject_class->finalize = mute_widget_finalize; + g_type_class_add_private (klass, sizeof (MuteWidgetPrivate)); +} + +static void +mute_widget_init (MuteWidget *self) +{ + MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); + priv->item = NULL; + priv->gitem = GTK_MENU_ITEM(gtk_menu_item_new ()); +} + +static void +mute_widget_dispose (GObject *object) +{ + G_OBJECT_CLASS (mute_widget_parent_class)->dispose (object); +} + +static void +mute_widget_finalize (GObject *object) +{ + MuteWidget *self = MUTE_WIDGET (object); + MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); + + g_object_unref (priv->item); + g_object_unref (G_OBJECT (priv->gitem)); + G_OBJECT_CLASS (mute_widget_parent_class)->finalize (object); +} + +GtkMenuItem * +mute_widget_get_menu_item(MuteWidget *self) +{ + MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); + return priv->gitem; +} + +MuteStatus +mute_widget_get_status (MuteWidget *self) +{ + g_return_val_if_fail(self, MUTE_STATUS_UNAVAILABLE); + MuteStatus status = MUTE_STATUS_UNAVAILABLE; + MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); + + GVariant *vstatus = dbusmenu_menuitem_property_get_variant(priv->item, + DBUSMENU_MUTE_MENUITEM_VALUE); + + if (g_variant_is_of_type (vstatus, G_VARIANT_TYPE_BOOLEAN)) + { + if (g_variant_get_boolean (vstatus)) + status = MUTE_STATUS_MUTED; + else + status = MUTE_STATUS_UNMUTED; + } + + return status; +} + +void mute_widget_toggle (MuteWidget *self) +{ + g_return_if_fail (self); + MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); + gtk_menu_item_activate (priv->gitem); +} + +/** + * mute_widget_new: + * @returns: a new #MuteWidget. + **/ +MuteWidget * +mute_widget_new (DbusmenuMenuitem *item) +{ + MuteWidget* widget = g_object_new(MUTE_WIDGET_TYPE, NULL); + MuteWidgetPrivate* priv = MUTE_WIDGET_GET_PRIVATE(widget); + priv->item = g_object_ref(item); + + GVariant *label = dbusmenu_menuitem_property_get_variant(priv->item, + DBUSMENU_MENUITEM_PROP_LABEL); + + if (g_variant_is_of_type(label, G_VARIANT_TYPE_STRING)) + gtk_menu_item_set_label(priv->gitem, g_variant_get_string(label, NULL)); + + if (label) + { + g_debug("Added a new Mute Widget %s", g_variant_print(label, FALSE)); + g_variant_unref(label); + } + + return widget; +} diff --git a/src/mute-widget.h b/src/mute-widget.h new file mode 100644 index 0000000..95130a1 --- /dev/null +++ b/src/mute-widget.h @@ -0,0 +1,67 @@ +/* +Copyright 2011 Canonical Ltd. + +Authors: + Marco Trevisan (Treviño) <mail@3v1n0.net> + +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 __MUTE_WIDGET_H__ +#define __MUTE_WIDGET_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> +#if GTK_CHECK_VERSION(3, 0, 0) +#include <libdbusmenu-gtk3/menuitem.h> +#else +#include <libdbusmenu-gtk/menuitem.h> +#endif +#include <libindicator/indicator-object.h> + +G_BEGIN_DECLS + +#define MUTE_WIDGET_TYPE (mute_widget_get_type ()) +#define MUTE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MUTE_WIDGET_TYPE, MuteWidget)) +#define MUTE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MUTE_WIDGET_TYPE, MuteWidgetClass)) +#define IS_MUTE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MUTE_WIDGET_TYPE)) +#define IS_MUTE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MUTE_WIDGET_TYPE)) +#define MUTE_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MUTE_WIDGET_TYPE, MuteWidgetClass)) + +typedef struct _MuteWidget MuteWidget; +typedef struct _MuteWidgetClass MuteWidgetClass; + +struct _MuteWidgetClass { + GObjectClass parent_class; +}; + +struct _MuteWidget { + GObject parent; +}; + +typedef enum { + MUTE_STATUS_UNAVAILABLE, + MUTE_STATUS_MUTED, + MUTE_STATUS_UNMUTED +} MuteStatus; + +GType mute_widget_get_type (void) G_GNUC_CONST; +MuteWidget* mute_widget_new (DbusmenuMenuitem *item); +MuteStatus mute_widget_get_status (MuteWidget *self); +void mute_widget_toggle (MuteWidget *self); +GtkMenuItem *mute_widget_get_menu_item (MuteWidget *self); + +G_END_DECLS + +#endif + diff --git a/src/sound-service-marshal.list b/src/sound-service-marshal.list new file mode 100644 index 0000000..4c756d4 --- /dev/null +++ b/src/sound-service-marshal.list @@ -0,0 +1,2 @@ +VOID:STRING,STRING + diff --git a/src/transport-widget.c b/src/transport-widget.c index f05c4c1..564b76f 100644 --- a/src/transport-widget.c +++ b/src/transport-widget.c @@ -31,7 +31,7 @@ Uses code from ctk #define RECT_WIDTH 130.0f #define Y 7.0f -#define X 80.0f +#define X 70.0f #define INNER_RADIUS 12.5 #define MIDDLE_RADIUS 13.0f #define OUTER_RADIUS 14.5f @@ -43,16 +43,16 @@ Uses code from ctk #define TRI_WIDTH 11.0f #define TRI_HEIGHT 13.0f #define TRI_OFFSET 6.0f -#define PREV_X 78.0f +#define PREV_X 68.0f #define PREV_Y 13.0f -#define NEXT_X 156.0f +#define NEXT_X 146.0f #define NEXT_Y 13.0f //prev_y #define PAUSE_WIDTH 21.0f #define PAUSE_HEIGHT 27.0f #define BAR_WIDTH 4.5f #define BAR_HEIGHT 24.0f #define BAR_OFFSET 10.0f -#define PAUSE_X 121.0f +#define PAUSE_X 111.0f #define PAUSE_Y 7.0f #define PLAY_WIDTH 28.0f #define PLAY_HEIGHT 29.0f @@ -85,11 +85,14 @@ struct _TransportWidgetPrivate gboolean has_focus; gint hold_timer; gint skip_frequency; - gint launching_timer; - gdouble launching_transparency; - gboolean fade_out; }; +#if GTK_CHECK_VERSION(3, 0, 0) +static GList *transport_widget_list = NULL; +static GtkStyleContext *spinner_style_context = NULL; +static GtkWidgetPath *spinner_widget_path = NULL; +#endif + // TODO refactor the UI handlers, consolidate functionality between key press /release // and button press / release. #define TRANSPORT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRANSPORT_WIDGET_TYPE, TransportWidgetPrivate)) @@ -104,8 +107,10 @@ G_DEFINE_TYPE (TransportWidget, transport_widget, GTK_TYPE_MENU_ITEM); /* essentials */ static void transport_widget_set_twin_item ( TransportWidget* self, DbusmenuMenuitem* twin_item); +#if ! GTK_CHECK_VERSION(3, 0, 0) static gboolean transport_widget_expose ( GtkWidget *button, GdkEventExpose *event); -static void draw (GtkWidget* button, cairo_t *cr); +#endif +static gboolean draw (GtkWidget* button, cairo_t *cr); /* UI and dbusmenu callbacks */ static gboolean transport_widget_button_press_event (GtkWidget *menuitem, @@ -133,13 +138,12 @@ static void transport_widget_react_to_button_release ( TransportWidget* button, TransportAction command); static void transport_widget_toggle_play_pause ( TransportWidget* button, TransportState update); -static void transport_widget_select (GtkItem* menu, gpointer Userdata); -static void transport_widget_deselect (GtkItem* menu, gpointer Userdata); +static void transport_widget_select (GtkWidget* menu, gpointer Userdata); +static void transport_widget_deselect (GtkWidget* menu, gpointer Userdata); static TransportAction transport_widget_collision_detection (gint x, gint y); static void transport_widget_start_timing (TransportWidget* widget); static gboolean transport_widget_trigger_seek (gpointer userdata); static gboolean transport_widget_seek (gpointer userdata); -static gboolean transport_widget_fade_playbutton (gpointer userdata); /// Init functions ////////////////////////////////////////////////////////// @@ -155,7 +159,11 @@ transport_widget_class_init (TransportWidgetClass *klass) widget_class->button_release_event = transport_widget_button_release_event; widget_class->motion_notify_event = transport_widget_motion_notify_event; widget_class->leave_notify_event = transport_widget_leave_notify_event; +#if GTK_CHECK_VERSION(3, 0, 0) + widget_class->draw = draw; +#else widget_class->expose_event = transport_widget_expose; +#endif gobject_class->dispose = transport_widget_dispose; gobject_class->finalize = transport_widget_finalize; @@ -164,7 +172,25 @@ transport_widget_class_init (TransportWidgetClass *klass) static void transport_widget_init (TransportWidget *self) { - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self); + TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self); + #if GTK_CHECK_VERSION(3, 0, 0) + if (transport_widget_list == NULL){ + /* append the object to the static linked list. */ + transport_widget_list = g_list_append (transport_widget_list, self); + + /* create widget path */ + spinner_widget_path = gtk_widget_path_new(); + + gtk_widget_path_iter_set_name (spinner_widget_path, -1 , "IndicatorSoundSpinner"); + gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_SPINNER); + + /* create style context and append path */ + spinner_style_context = gtk_style_context_new(); + + gtk_style_context_set_path (spinner_style_context, spinner_widget_path); + gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_SPINNER); + } + #endif priv->current_command = TRANSPORT_ACTION_NO_ACTION; priv->current_state = TRANSPORT_STATE_PAUSED; priv->key_event = TRANSPORT_ACTION_NO_ACTION; @@ -172,9 +198,6 @@ transport_widget_init (TransportWidget *self) priv->has_focus = FALSE; priv->hold_timer = 0; priv->skip_frequency = 0; - priv->launching_timer = 0; - priv->launching_transparency = 1.0f; - priv->fade_out = TRUE; priv->command_coordinates = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, @@ -212,11 +235,11 @@ transport_widget_init (TransportWidget *self) "notify", G_CALLBACK (transport_widget_notify), NULL); - g_signal_connect (GTK_ITEM(self), + g_signal_connect (G_OBJECT(self), "select", G_CALLBACK (transport_widget_select), NULL); - g_signal_connect (GTK_ITEM(self), + g_signal_connect (G_OBJECT(self), "deselect", G_CALLBACK (transport_widget_deselect), NULL); @@ -227,22 +250,39 @@ transport_widget_init (TransportWidget *self) static void transport_widget_dispose (GObject *object) { + #if GTK_CHECK_VERSION(3, 0, 0) + transport_widget_list = g_list_remove (transport_widget_list, object); + + if (transport_widget_list == NULL){ + if (spinner_widget_path != NULL){ + gtk_widget_path_free (spinner_widget_path); + spinner_widget_path = NULL; + } + + if (spinner_style_context != NULL){ + g_object_unref (spinner_style_context); + spinner_style_context = NULL; + } + } + #endif 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); } +#if ! GTK_CHECK_VERSION(3, 0, 0) static gboolean transport_widget_expose (GtkWidget *button, GdkEventExpose *event) { cairo_t *cr; - cr = gdk_cairo_create (button->window); + cr = gdk_cairo_create (gtk_widget_get_window (button)); - //g_debug("In the playbutton's expose method, x = %i, y=%i and width: %i and height: %i'"); cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height); @@ -253,6 +293,7 @@ transport_widget_expose (GtkWidget *button, GdkEventExpose *event) cairo_destroy (cr); return FALSE; } +#endif gboolean transport_widget_is_selected ( TransportWidget* widget ) @@ -267,7 +308,6 @@ transport_widget_toggle_play_pause(TransportWidget* button, { TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button); priv->current_state = update; - //g_debug("TransportWidget::toggle play state : %i", priv->current_state); gtk_widget_queue_draw (GTK_WIDGET(button)); } @@ -298,6 +338,8 @@ static gboolean transport_widget_motion_notify_event (GtkWidget *menuitem, GdkEventMotion *event) { + //g_debug("transport_widget_motion_notify_event()"); + g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE ); TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) ); TransportAction result = transport_widget_determine_motion_event ( TRANSPORT_WIDGET(menuitem), @@ -319,6 +361,8 @@ static gboolean transport_widget_leave_notify_event (GtkWidget *menuitem, GdkEventCrossing *event) { + //g_debug("transport_widget_leave_notify_event()"); + g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE ); TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) ); @@ -333,6 +377,8 @@ static gboolean transport_widget_button_press_event (GtkWidget *menuitem, GdkEventButton *event) { + //g_debug("transport_widget_button_press_event()"); + g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE ); TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) ); TransportAction result = transport_widget_determine_button_event ( TRANSPORT_WIDGET(menuitem), @@ -434,7 +480,7 @@ transport_widget_button_release_event (GtkWidget *menuitem, } static void -transport_widget_select (GtkItem* item, gpointer Userdata) +transport_widget_select (GtkWidget* item, gpointer Userdata) { TransportWidget* transport = TRANSPORT_WIDGET(item); TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); @@ -442,7 +488,7 @@ transport_widget_select (GtkItem* item, gpointer Userdata) } static void -transport_widget_deselect (GtkItem* item, gpointer Userdata) +transport_widget_deselect (GtkWidget* item, gpointer Userdata) { TransportWidget* transport = TRANSPORT_WIDGET(item); TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); @@ -1214,23 +1260,30 @@ _surface_blur (cairo_surface_t* surface, cairo_surface_mark_dirty (surface); } -static void +static gboolean draw (GtkWidget* button, cairo_t *cr) { - g_return_if_fail(IS_TRANSPORT_WIDGET(button)); - g_return_if_fail( cr != NULL ); + g_return_val_if_fail(IS_TRANSPORT_WIDGET(button), FALSE); + g_return_val_if_fail(cr != NULL, FALSE); TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button); - + + //g_debug("transport-widget draw()"); + cairo_surface_t* surf = NULL; cairo_t* cr_surf = NULL; - cairo_translate (cr, button->allocation.x, button->allocation.y); - - //g_debug("button x allocation = %i", button->allocation.x); - //g_debug("button y allocation = %i", button->allocation.y); +#if ! GTK_CHECK_VERSION(3, 0, 0) + GtkAllocation allocation; + gtk_widget_get_allocation (button, &allocation); + cairo_translate (cr, allocation.x, allocation.y); +#endif GtkStyle *style; - + +#if GTK_CHECK_VERSION(3, 0, 0) + gtk_style_context_add_class (gtk_widget_get_style_context (button), + "menu"); +#endif CairoColorRGB bg_color, fg_color, bg_selected, bg_prelight; CairoColorRGB color_middle[2], color_middle_prelight[2], color_outer[2], color_outer_prelight[2], color_play_outer[2], color_play_outer_prelight[2], @@ -1740,61 +1793,14 @@ draw (GtkWidget* button, cairo_t *cr) FALSE); _finalize (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y); } + #if GTK_CHECK_VERSION(3, 0, 0) else if(priv->current_state == TRANSPORT_STATE_LAUNCHING) { -/* - g_debug ("launching in draw"); -*/ - _setup (&cr_surf, &surf, PLAY_WIDTH+6, PLAY_HEIGHT+6); - _mask_play (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING)); - - double BUTTON_SHADOW_LAUNCHING[] = {color_button[3].r, - color_button[3].g, - color_button[3].b, - priv->launching_transparency}; - double BUTTON_LAUNCHING_END[] = {color_button[0].r, - color_button[0].g, - color_button[0].b, - priv->launching_transparency}; - double BUTTON_LAUNCHING_START[] = {color_button[1].r, - color_button[1].g, - color_button[1].b, - priv->launching_transparency}; - _fill (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING), - BUTTON_SHADOW_LAUNCHING, - BUTTON_SHADOW_LAUNCHING, - FALSE); - _surface_blur (surf, 3); - _finalize_repaint (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y + 0.5f, 3); - // draw play-button - _setup (&cr_surf, &surf, PLAY_WIDTH, PLAY_HEIGHT); - cairo_set_line_width (cr, 10.5); - cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - _mask_play (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING)); - _fill (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING), - BUTTON_LAUNCHING_START, - BUTTON_LAUNCHING_END, - FALSE); - _finalize (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y); + gtk_render_activity (spinner_style_context, cr, 106, 6 , 30, 30); } + #endif + return FALSE; } static void @@ -1812,30 +1818,6 @@ transport_widget_set_twin_item(TransportWidget* self, (TransportState)initial_state); } -static gboolean -transport_widget_fade_playbutton (gpointer userdata) -{ - TransportWidget* bar = (TransportWidget*)userdata; - g_return_val_if_fail(IS_TRANSPORT_WIDGET(bar), FALSE); -/* - g_debug ("fade in /out timeout"); -*/ - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(bar); - if (priv->launching_transparency == 1.0f){ - priv->fade_out = TRUE; - } - else if (priv->launching_transparency <= 0.3F){ - priv->fade_out = FALSE; - } - if (priv->fade_out == TRUE){ - priv->launching_transparency -= 0.05f; - } - else{ - priv->launching_transparency += 0.05f; - } - gtk_widget_queue_draw (GTK_WIDGET(bar)); - return TRUE; -} /** * transport_widget_update_state() * Callback for updates from the other side of dbus @@ -1852,21 +1834,21 @@ transport_widget_property_update(DbusmenuMenuitem* item, gchar* property, if(g_ascii_strcasecmp(DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE, property) == 0) { TransportState new_state = (TransportState)g_variant_get_int32(value); - //g_debug("transport_widget_update_state - with value %i", update_value); + //g_debug("transport_widget_update_state - with value %i", new_state); if (new_state == TRANSPORT_STATE_LAUNCHING){ + #if GTK_CHECK_VERSION(3, 0, 0) + gtk_style_context_notify_state_change (spinner_style_context, + gtk_widget_get_window ( GTK_WIDGET(userdata)), + NULL, + GTK_STATE_FLAG_ACTIVE, + TRUE); + gtk_style_context_set_state (spinner_style_context, GTK_STATE_FLAG_ACTIVE); + #endif + priv->current_state = TRANSPORT_STATE_LAUNCHING; - priv->launching_timer = g_timeout_add (100, - transport_widget_fade_playbutton, - bar); - //g_debug("TransportWidget::toggle play state : %i", priv->current_state); + g_debug("TransportWidget::toggle play state : %i", priv->current_state); } else{ - if (priv->launching_timer != 0){ - g_source_remove (priv->launching_timer); - priv->launching_timer = 0; - priv->fade_out = TRUE; - priv->launching_transparency = 1.0f; - } transport_widget_toggle_play_pause(bar, new_state); } } diff --git a/src/transport-widget.h b/src/transport-widget.h index e5e91dc..8c2ce48 100644 --- a/src/transport-widget.h +++ b/src/transport-widget.h @@ -20,8 +20,11 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #define __TRANSPORT_WIDGET_H__ #include <gtk/gtk.h> -#include <gtk/gtkmenuitem.h> +#if GTK_CHECK_VERSION(3, 0, 0) +#include <libdbusmenu-gtk3/menuitem.h> +#else #include <libdbusmenu-gtk/menuitem.h> +#endif #include "common-defs.h" diff --git a/src/voip-input-widget.h b/src/voip-input-widget.h index 29912f0..0e90665 100644 --- a/src/voip-input-widget.h +++ b/src/voip-input-widget.h @@ -21,7 +21,12 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib.h> #include <glib-object.h> +#include <gtk/gtk.h> +#if GTK_CHECK_VERSION(3, 0, 0) +#include <libdbusmenu-gtk3/menuitem.h> +#else #include <libdbusmenu-gtk/menuitem.h> +#endif G_BEGIN_DECLS diff --git a/src/volume-widget.h b/src/volume-widget.h index 7012473..3deb99c 100644 --- a/src/volume-widget.h +++ b/src/volume-widget.h @@ -21,7 +21,12 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib.h> #include <glib-object.h> +#include <gtk/gtk.h> +#if GTK_CHECK_VERSION(3, 0, 0) +#include <libdbusmenu-gtk3/menuitem.h> +#else #include <libdbusmenu-gtk/menuitem.h> +#endif #include <libindicator/indicator-object.h> G_BEGIN_DECLS |