[Xfce4-commits] [panel-plugins/xfce4-pulseaudio-plugin] 01/01: Add support for controlling media players via MPRIS2

noreply at xfce.org noreply at xfce.org
Sun Aug 27 17:59:29 CEST 2017


This is an automated email from the git hooks/post-receive script.

b   l   u   e   s   a   b   r   e       p   u   s   h   e   d       a       c   o   m   m   i   t       t   o       b   r   a   n   c   h       m   a   s   t   e   r   
   in repository panel-plugins/xfce4-pulseaudio-plugin.

commit d3d8cf91e2bd326113e57b57215e8306d9c92e64
Author: Sean Davis <smd.seandavis at gmail.com>
Date:   Sun Aug 27 11:58:25 2017 -0400

    Add support for controlling media players via MPRIS2
    
    Squashed commit of the following:
    
    commit f417887baba9da6df599de05de8eed8654465ddb
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sun Aug 27 05:57:31 2017 -0400
    
        Add myself to AUTHORS
    
    commit c520c520d8824f1c4f3a8c2c151b03a7440f40a8
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 22:39:41 2017 -0400
    
        Add ~ files to .gitignore
    
    commit a5b8fb79ef52eb351552ff280c91c00b373fcb7e
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 22:38:46 2017 -0400
    
        Consistent spacing
    
    commit e4c2db8f2337a8a4c10f98ff73f27e2ad1f52489
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 22:31:32 2017 -0400
    
        Port glade file to GTK 3.6
    
    commit 9c4fee2ca0b04d771a2c3d6e7ca0db484d5d8fe3
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 22:04:49 2017 -0400
    
        GTK+ support through 3.22, ignore deprecations on essential functionality
    
    commit b1f526ef5def96e4ea1e41b04feb59e1b52049c8
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 21:58:20 2017 -0400
    
        Silently discard const qualifier with pulseaudio-volume
    
    commit b6eed1262a088c0c4e6008b66e7b2b68adaaba29
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 20:48:57 2017 -0400
    
        Remove default player, depend on broadcasting MPRIS2 players
    
    commit 7aa7d42e98a2d469e77928c4697e31e6baaaf3e8
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 20:36:09 2017 -0400
    
        Fix intended icon size loading: GTK_ICON_SIZE_LARGE_TOOLBAR / 24px
    
    commit f90850b2292a61483e8fdd89cc2557704a2d8e8f
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 9 20:15:21 2017 -0400
    
        Add mprismenuitem.c to POTFILES.in
    
    commit 1760ea626025de6c853a5e8ae4b87324a5c01928
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue Aug 8 21:41:14 2017 -0400
    
        Better handling of stopped state, consistent status tracking
    
    commit 11b9875bb6717b67b79a915809193e23b6f6b3e2
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue Aug 8 19:18:19 2017 -0400
    
        Drop unused properties and signals
    
    commit b3b59527d5c7164a6246ac1623c4667aef9fd582
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue Aug 8 06:21:57 2017 -0400
    
        Sort players
    
    commit a5b26216c3036d3f3e8525a73633b796b436a33b
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue Aug 8 05:34:28 2017 -0400
    
        Disconnect from MPRIS when menu items are destroyed, fixes another crash
    
    commit a5268c8cf16805530e85c3751e90c797a19c4dec
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sun Aug 6 12:10:46 2017 -0400
    
        Fix several crashes and build cleaner with debug enabled
    
    commit a9248cd3ca2dd337c208f6dbbc6848c2e10df127
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sun Aug 6 07:37:26 2017 -0400
    
        Consistent whitespace and const usage
    
    commit 798f6101261eef6cc7b1545ed6cc5c73b98dcba6
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sun Aug 6 06:54:04 2017 -0400
    
        Handle null string array
    
    commit f5bd2e38082308854b4fe5e0fb68b3a297814b51
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sat Aug 5 13:20:56 2017 -0400
    
        Add ability to raise or launch (launch crashes plugin) players
    
    commit 358f862c332ee4e255cbc4309b78203be126112b
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sat Aug 5 11:35:10 2017 -0400
    
        Fix crash on player exit
    
    commit f856d67def5b6e81845ef35585d39bd2103d0b60
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sat Aug 5 08:18:18 2017 -0400
    
        Add support for reverse domain name formatted launchers (Fixes integration with Parole)
    
    commit 3797f7b37c885eab58da40a7799cc50128cb463e
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Thu Aug 3 23:50:55 2017 -0400
    
        Fix a few defaults, add storage for known players
    
    commit 96c336661fb6832c63fba06c46abc46a4ba83b86
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 2 06:41:08 2017 -0400
    
        Add .gitignore
    
    commit ce61a7725ad3eb362e9d0bdaf65ad9b0b66b0c7e
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Aug 2 06:40:06 2017 -0400
    
        Add initial MPRIS2 support.
        This still requires some cleanup and a bit of refactoring, but should be at a usable point.
---
 .gitignore                             |  29 ++
 AUTHORS                                |   1 +
 configure.ac.in                        |   2 +
 panel-plugin/Makefile.am               |   8 +-
 panel-plugin/mprismenuitem.c           | 746 ++++++++++++++++++++++++++++++++
 panel-plugin/mprismenuitem.h           | 103 +++++
 panel-plugin/pulseaudio-button.c       |  16 +-
 panel-plugin/pulseaudio-button.h       |   2 +
 panel-plugin/pulseaudio-config.c       | 108 +++++
 panel-plugin/pulseaudio-config.h       |   6 +
 panel-plugin/pulseaudio-dialog.glade   | 129 +++---
 panel-plugin/pulseaudio-menu.c         | 181 +++++++-
 panel-plugin/pulseaudio-menu.h         |   2 +
 panel-plugin/pulseaudio-mpris-player.c | 759 +++++++++++++++++++++++++++++++++
 panel-plugin/pulseaudio-mpris-player.h |  79 ++++
 panel-plugin/pulseaudio-mpris.c        | 308 +++++++++++++
 panel-plugin/pulseaudio-mpris.h        |  62 +++
 panel-plugin/pulseaudio-plugin.c       |  14 +-
 panel-plugin/pulseaudio-volume.c       |   8 +-
 panel-plugin/scalemenuitem.c           |  46 +-
 panel-plugin/scalemenuitem.h           |   7 +-
 po/POTFILES.in                         |   1 +
 22 files changed, 2509 insertions(+), 108 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..88c93fb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+*~
+*.la
+*.lo
+*_ui.h
+.deps
+.libs
+Makefile
+Makefile.in
+Makefile.in.in
+aclocal.m4
+autom4te.cache/
+compile
+config.*
+configure
+configure.ac
+depcomp
+install-sh
+intltool*
+libtool
+ltmain.sh
+missing
+panel-plugin/pulseaudio.desktop
+panel-plugin/pulseaudio.desktop.in
+po/*.gmo
+po/.intltool-merge-cache
+po/Makefile.in.in
+po/POTFILES
+po/stamp-it
+stamp-h1
diff --git a/AUTHORS b/AUTHORS
index 373891d..840e31f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,3 +3,4 @@ Guido Berhoerster <guido+xfce at berhoerster.name>
 Simon Steinbeiss <ochosi at xfce.org>
 Viktor Odintsev <zakhams at gmail.com>
 Matthieu Mota <matthieumota at gmail.com>
+Sean Davis <bluesabre at xfce.org>
diff --git a/configure.ac.in b/configure.ac.in
index e079f25..c737c8d 100644
--- a/configure.ac.in
+++ b/configure.ac.in
@@ -84,6 +84,8 @@ XDT_CHECK_PACKAGE([LIBXFCE4UI], [libxfce4ui-2], [4.11.0])
 XDT_CHECK_PACKAGE([LIBXFCE4PANEL], [libxfce4panel-${LIBXFCE4PANEL_VERSION_API}], [4.11.0])
 XDT_CHECK_PACKAGE([XFCONF], [libxfconf-0], [4.6.0])
 
+XDT_CHECK_PACKAGE([GIO], [gio-2.0], [2.30])
+
 dnl **********************************
 dnl *** Optional keybinder Support ***
 dnl **********************************
diff --git a/panel-plugin/Makefile.am b/panel-plugin/Makefile.am
index 360f4d3..286d80f 100644
--- a/panel-plugin/Makefile.am
+++ b/panel-plugin/Makefile.am
@@ -35,10 +35,16 @@ libpulseaudio_plugin_la_SOURCES = \
 	pulseaudio-dialog.h \
 	pulseaudio-menu.c \
 	pulseaudio-menu.h \
+	pulseaudio-mpris.c \
+	pulseaudio-mpris.h \
+	pulseaudio-mpris-player.c \
+	pulseaudio-mpris-player.h \
 	pulseaudio-notify.c \
 	pulseaudio-notify.h \
 	scalemenuitem.c \
-	scalemenuitem.h
+	scalemenuitem.h \
+	mprismenuitem.c \
+	mprismenuitem.h
 
 
 libpulseaudio_plugin_la_CFLAGS = \
diff --git a/panel-plugin/mprismenuitem.c b/panel-plugin/mprismenuitem.c
new file mode 100644
index 0000000..938d8cf
--- /dev/null
+++ b/panel-plugin/mprismenuitem.c
@@ -0,0 +1,746 @@
+/* -*- c-basic-offset: 2 -*- vi:set ts=2 sts=2 sw=2:
+ * * 2017 Sean Davis <bluesabre at xfce.org>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+/*
+ * Based on the scale menu item implementation of the indicator applet:
+ * Authors:
+ *    Cody Russell <crussell at canonical.com>
+ * http://bazaar.launchpad.net/~indicator-applet-developers/ido/trunk.14.10/view/head:/src/idoscalemenuitem.h
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "mprismenuitem.h"
+
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gio/gdesktopappinfo.h>
+
+/* for DBG/TRACE */
+#include <libxfce4util/libxfce4util.h>
+
+
+static gboolean mpris_menu_item_button_press_event      (GtkWidget          *menuitem,
+                                                         GdkEventButton     *event);
+static gboolean mpris_menu_item_button_release_event    (GtkWidget          *menuitem,
+                                                         GdkEventButton     *event);
+static void     update_packing                          (MprisMenuItem      *self);
+
+
+
+struct _MprisMenuItemPrivate {
+  GtkWidget *title_label;
+  GtkWidget *artist_label;
+
+  GtkWidget *go_previous;
+  GtkWidget *play_pause;
+  GtkWidget *go_next;
+
+  gboolean   can_go_previous;
+  gboolean   can_play;
+  gboolean   can_pause;
+  gboolean   can_go_next;
+  gboolean   can_raise;
+
+  gboolean   is_running;
+  gboolean   is_playing;
+  gboolean   is_stopped;
+
+  gchar     *player;
+  gchar     *title;
+  gchar     *filename;
+
+  GtkWidget *vbox;
+  GtkWidget *hbox;
+  GtkWidget *button_box;
+};
+
+enum {
+  MEDIA_NOTIFY,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+G_DEFINE_TYPE (MprisMenuItem, mpris_menu_item, GTK_TYPE_IMAGE_MENU_ITEM)
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+#define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_MPRIS_MENU_ITEM, MprisMenuItemPrivate))
+
+
+
+static void
+mpris_menu_item_class_init (MprisMenuItemClass *item_class)
+{
+  GtkWidgetClass    *widget_class =    GTK_WIDGET_CLASS    (item_class);
+
+  widget_class->button_press_event   = mpris_menu_item_button_press_event;
+  widget_class->button_release_event = mpris_menu_item_button_release_event;
+
+  /**
+   * MprisMenuItem::media-notify:
+   * @menuitem: the #MprisMenuItem for which the value changed
+   * @value: the mpris signal to emit
+   *
+   * Emitted whenever the a media button is clicked.
+   */
+  signals[MEDIA_NOTIFY] = g_signal_new ("media-notify",
+                                        TYPE_MPRIS_MENU_ITEM,
+                                        G_SIGNAL_RUN_LAST,
+                                        0, NULL, NULL,
+                                        g_cclosure_marshal_VOID__STRING,
+                                        G_TYPE_NONE,
+                                        1, G_TYPE_STRING);
+
+  g_type_class_add_private (item_class, sizeof (MprisMenuItemPrivate));
+}
+
+static void
+remove_children (GtkContainer *container)
+{
+  GList * children;
+  GList * l;
+
+  g_return_if_fail (GTK_IS_CONTAINER (container));
+
+  children = gtk_container_get_children (container);
+
+  for (l=children; l!=NULL; l=l->next)
+    gtk_container_remove (container, l->data);
+  g_list_free (children);
+}
+
+static void
+gtk_label_set_markup_printf_escaped (GtkLabel    *label,
+                                     const gchar *format,
+                                     ...)
+{
+  va_list args;
+  gchar *str;
+
+  va_start (args, format);
+  str = g_markup_vprintf_escaped (format, args);
+  gtk_label_set_markup (label, str);
+  va_end (args);
+
+  g_free (str);
+}
+
+static void
+media_notify (MprisMenuItem *item, gchar *message)
+{
+  g_signal_emit (item, signals[MEDIA_NOTIFY], 0, message);
+}
+
+static void
+media_play_pause_clicked_event (GtkButton *button, gpointer user_data)
+{
+  MprisMenuItem *item = user_data;
+  media_notify (item, "PlayPause");
+}
+
+static void
+media_go_previous_clicked_event (GtkButton *button, gpointer user_data)
+{
+  MprisMenuItem *item = user_data;
+  media_notify (item, "Previous");
+}
+
+static void
+media_go_next_clicked_event (GtkButton *button, gpointer user_data)
+{
+  MprisMenuItem *item = user_data;
+  media_notify (item, "Next");
+}
+
+static void
+mpris_menu_item_raise (MprisMenuItem *item)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  if (priv->is_running)
+    {
+      if (priv->can_raise)
+        {
+          media_notify (item, "Raise");
+        }
+    }
+}
+
+static void
+mpris_menu_item_launch (MprisMenuItem *item)
+{
+  MprisMenuItemPrivate *priv;
+  GAppInfo             *app_info;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  if (priv->is_running)
+    return;
+
+  app_info = (GAppInfo*)g_desktop_app_info_new_from_filename (priv->filename);
+  if (app_info != NULL)
+    {
+      g_app_info_launch (app_info, NULL, NULL, NULL);
+      g_object_unref (app_info);
+    }
+}
+
+static void
+mpris_menu_item_raise_or_launch (MprisMenuItem *item)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  if (priv->is_running)
+    mpris_menu_item_raise (item);
+  else
+    mpris_menu_item_launch (item);
+}
+
+static void
+menu_item_activate_event (GtkMenuItem *mi, gpointer user_data)
+{
+  mpris_menu_item_raise_or_launch (MPRIS_MENU_ITEM (mi));
+}
+
+static GtkWidget *
+track_info_label_new (void)
+{
+  GtkWidget *label;
+
+  label = gtk_label_new (NULL);
+  gtk_label_set_width_chars (GTK_LABEL (label), 25);
+  gtk_label_set_max_width_chars (GTK_LABEL (label), 25);
+  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
+
+  #if GTK_CHECK_VERSION (3, 16, 0)
+    gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+    gtk_widget_set_halign (label, GTK_ALIGN_START);
+  #else
+    gtk_misc_set_alignment (GTK_MISC(label), 0, 0);
+  #endif
+
+  return label;
+}
+
+static void
+update_packing (MprisMenuItem *self)
+{
+  MprisMenuItemPrivate *priv;
+  GtkBox               *hbox;
+  GtkBox               *vbox;
+  GtkBox               *button_box;
+  GtkStyleContext      *ctx;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (self));
+
+  priv = GET_PRIVATE (self);
+  hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0));
+  vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
+
+  button_box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0));
+  ctx = gtk_widget_get_style_context (GTK_WIDGET (button_box));
+  gtk_style_context_add_class (ctx, "linked");
+
+  TRACE("entering");
+
+  if (priv->vbox)
+    remove_children (GTK_CONTAINER (priv->vbox));
+  if (priv->hbox)
+  {
+    remove_children (GTK_CONTAINER (priv->hbox));
+    gtk_container_remove (GTK_CONTAINER (self), priv->hbox);
+  }
+
+  priv->hbox = GTK_WIDGET(hbox);
+  priv->vbox = GTK_WIDGET(vbox);
+  priv->button_box = GTK_WIDGET(button_box);
+
+  /* add the new layout */
+  /* [IC]  Title   [  CON  ]
+   * [ON]  Artist  [ TROLS ]
+   */
+  priv->go_previous = gtk_button_new_from_icon_name ("media-skip-backward-symbolic", GTK_ICON_SIZE_MENU);
+  priv->play_pause = gtk_button_new_from_icon_name ("media-playback-start-symbolic", GTK_ICON_SIZE_MENU);
+  priv->go_next = gtk_button_new_from_icon_name ("media-skip-forward-symbolic", GTK_ICON_SIZE_MENU);
+
+  g_signal_connect (priv->play_pause, "clicked", G_CALLBACK (media_play_pause_clicked_event), self);
+  g_signal_connect (priv->go_previous, "clicked", G_CALLBACK (media_go_previous_clicked_event), self);
+  g_signal_connect (priv->go_next, "clicked", G_CALLBACK (media_go_next_clicked_event), self);
+  g_signal_connect (self, "activate", G_CALLBACK (menu_item_activate_event), self);
+
+  priv->title_label = track_info_label_new ();
+  priv->artist_label = track_info_label_new ();
+
+  gtk_box_pack_start (button_box, priv->go_previous, FALSE, FALSE, 0);
+  gtk_box_pack_start (button_box, priv->play_pause, FALSE, FALSE, 0);
+  gtk_box_pack_start (button_box, priv->go_next, FALSE, FALSE, 0);
+
+  gtk_box_pack_start (vbox, priv->title_label, FALSE, FALSE, 0);
+  gtk_box_pack_start (vbox, priv->artist_label, FALSE, FALSE, 0);
+
+  gtk_box_pack_start (hbox, GTK_WIDGET (vbox), TRUE, TRUE, 0);
+  gtk_box_pack_start (hbox, GTK_WIDGET (button_box), FALSE, FALSE, 0);
+
+  mpris_menu_item_set_title (self, priv->player);
+  mpris_menu_item_set_artist (self, _("Not currently playing"));
+
+  gtk_widget_show_all (priv->button_box);
+  gtk_widget_show_all (priv->hbox);
+  gtk_widget_show_all (priv->vbox);
+
+  gtk_container_add (GTK_CONTAINER (self), priv->hbox);
+}
+
+static void
+mpris_menu_item_init (MprisMenuItem *self)
+{
+}
+
+static GtkWidget *
+mpris_menu_item_get_widget_at_event (MprisMenuItem  *menuitem,
+                                     GdkEventButton *event)
+{
+  MprisMenuItemPrivate *priv;
+  GtkAllocation         alloc;
+  gint                  x, y;
+
+  g_return_val_if_fail (IS_MPRIS_MENU_ITEM (menuitem), NULL);
+
+  priv = GET_PRIVATE (menuitem);
+
+  gtk_widget_get_allocation (priv->button_box, &alloc);
+  gtk_widget_translate_coordinates (GTK_WIDGET (menuitem), priv->button_box, event->x, event->y, &x, &y);
+
+  if (x > 0 && x < alloc.width && y > 0 && y < alloc.height)
+    {
+      gtk_widget_get_allocation (priv->go_previous, &alloc);
+      gtk_widget_translate_coordinates (GTK_WIDGET (menuitem), priv->go_previous, event->x, event->y, &x, &y);
+
+      if (x > 0 && x < alloc.width && y > 0 && y < alloc.height)
+        return GTK_WIDGET (priv->go_previous);
+
+      gtk_widget_get_allocation (priv->play_pause, &alloc);
+      gtk_widget_translate_coordinates (GTK_WIDGET (menuitem), priv->play_pause, event->x, event->y, &x, &y);
+
+      if (x > 0 && x < alloc.width && y > 0 && y < alloc.height)
+        return GTK_WIDGET (priv->play_pause);
+
+      gtk_widget_get_allocation (priv->go_next, &alloc);
+      gtk_widget_translate_coordinates (GTK_WIDGET (menuitem), priv->go_next, event->x, event->y, &x, &y);
+
+      if (x > 0 && x < alloc.width && y > 0 && y < alloc.height)
+        return GTK_WIDGET (priv->go_next);
+    }
+
+  return GTK_WIDGET (menuitem);
+}
+
+static gboolean
+mpris_menu_item_button_press_event (GtkWidget      *menuitem,
+                                    GdkEventButton *event)
+{
+  GtkWidget            *widget;
+
+  g_return_val_if_fail (IS_MPRIS_MENU_ITEM (menuitem), FALSE);
+
+  widget = mpris_menu_item_get_widget_at_event (MPRIS_MENU_ITEM (menuitem), event);
+
+  if (widget == NULL)
+    return FALSE;
+
+  if (widget == menuitem)
+    return FALSE;
+
+  gtk_widget_event (widget, (GdkEvent*) event);
+  return TRUE;
+}
+
+static gboolean
+mpris_menu_item_button_release_event (GtkWidget      *menuitem,
+                                      GdkEventButton *event)
+{
+  GtkWidget            *widget;
+
+  g_return_val_if_fail (IS_MPRIS_MENU_ITEM (menuitem), FALSE);
+
+  widget = mpris_menu_item_get_widget_at_event (MPRIS_MENU_ITEM (menuitem), event);
+
+  if (widget == NULL)
+    return FALSE;
+
+  if (widget == menuitem)
+    return FALSE;
+
+  gtk_widget_event (widget, (GdkEvent*) event);
+  return TRUE;
+}
+
+const gchar *
+mpris_menu_item_get_player (MprisMenuItem *mi)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_val_if_fail (IS_MPRIS_MENU_ITEM (mi), NULL);
+
+  priv = GET_PRIVATE (mi);
+
+  return priv->player;
+}
+
+void
+mpris_menu_item_set_title (MprisMenuItem *item,
+                           const gchar   *title)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  if (title == NULL || *title == '\0')
+    gtk_label_set_markup_printf_escaped (GTK_LABEL (priv->title_label), "<b>%s</b>", priv->title);
+  else
+    gtk_label_set_markup_printf_escaped (GTK_LABEL (priv->title_label), "<b>%s</b>", title);
+}
+
+void
+mpris_menu_item_set_artist (MprisMenuItem *item,
+                            const gchar   *artist)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  if (artist == NULL || *artist == '\0')
+    gtk_label_set_label (GTK_LABEL (priv->artist_label), NULL);
+  else
+    gtk_label_set_label (GTK_LABEL (priv->artist_label), artist);
+}
+
+void
+mpris_menu_item_set_can_go_previous (MprisMenuItem *item,
+                                     gboolean       enabled)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->can_go_previous = enabled;
+
+  if (priv->is_running)
+    gtk_widget_set_sensitive (priv->go_previous, priv->can_go_previous);
+  else
+    gtk_widget_set_sensitive (priv->go_previous, FALSE);
+}
+
+void
+mpris_menu_item_set_can_play (MprisMenuItem *item,
+                              gboolean       enabled)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->can_play = enabled;
+
+  if (priv->is_running)
+    gtk_widget_set_sensitive (priv->play_pause, priv->can_play);
+  else
+    gtk_widget_set_sensitive (priv->play_pause, FALSE);
+}
+
+void
+mpris_menu_item_set_can_pause (MprisMenuItem *item,
+                               gboolean       enabled)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->can_pause = enabled;
+
+  if (priv->is_running)
+    {
+      if (priv->is_playing)
+        {
+          gtk_widget_set_sensitive (priv->play_pause, priv->can_pause);
+        }
+    }
+  else
+    gtk_widget_set_sensitive (priv->play_pause, FALSE);
+}
+
+void
+mpris_menu_item_set_can_go_next (MprisMenuItem *item,
+                                 gboolean       enabled)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->can_go_next = enabled;
+
+  if (priv->is_running)
+    gtk_widget_set_sensitive (priv->go_next, priv->can_go_next);
+  else
+    gtk_widget_set_sensitive (priv->go_next, FALSE);
+}
+
+void
+mpris_menu_item_set_is_playing (MprisMenuItem *item,
+                                gboolean       playing)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->is_playing = playing;
+
+  if (priv->is_playing)
+    {
+      gtk_image_set_from_icon_name (GTK_IMAGE (gtk_button_get_image (GTK_BUTTON (priv->play_pause))), "media-playback-pause-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
+      gtk_widget_set_sensitive (priv->play_pause, priv->can_pause);
+      priv->is_stopped = FALSE;
+    }
+  else
+    {
+      gtk_image_set_from_icon_name (GTK_IMAGE (gtk_button_get_image (GTK_BUTTON (priv->play_pause))), "media-playback-start-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
+      gtk_widget_set_sensitive (priv->play_pause, priv->can_play);
+    }
+
+  if (!priv->is_running)
+    {
+      gtk_widget_set_sensitive (priv->play_pause, FALSE);
+    }
+}
+
+void
+mpris_menu_item_set_is_stopped (MprisMenuItem *item,
+                                gboolean       stopped)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->is_stopped = stopped;
+
+  if (priv->is_stopped)
+    {
+      if (priv->is_playing)
+        {
+          mpris_menu_item_set_is_playing (item, FALSE);
+        }
+
+      mpris_menu_item_set_title (item, NULL);
+      mpris_menu_item_set_artist (item, _("Not currently playing"));
+    }
+}
+
+void
+mpris_menu_item_set_is_running (MprisMenuItem *item,
+                                gboolean       running)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->is_running = running;
+
+  if (!priv->is_running)
+    {
+      mpris_menu_item_set_title (item, NULL);
+      mpris_menu_item_set_artist (item, _("Not currently playing"));
+      mpris_menu_item_set_can_play (item, FALSE);
+      mpris_menu_item_set_can_pause (item, FALSE);
+      mpris_menu_item_set_can_go_previous (item, FALSE);
+      mpris_menu_item_set_can_go_next (item, FALSE);
+      mpris_menu_item_set_is_playing (item, FALSE);
+      mpris_menu_item_set_is_stopped (item, TRUE);
+    }
+  else
+    {
+      mpris_menu_item_set_can_play (item, priv->can_play);
+      mpris_menu_item_set_can_pause (item, priv->can_pause);
+      mpris_menu_item_set_can_go_next (item, priv->can_go_next);
+      mpris_menu_item_set_can_go_previous (item, priv->can_go_previous);
+      mpris_menu_item_set_is_playing (item, priv->is_playing);
+      mpris_menu_item_set_is_stopped (item, priv->is_stopped);
+    }
+}
+
+void
+mpris_menu_item_set_can_raise (MprisMenuItem *item,
+                               gboolean       can_raise)
+{
+  MprisMenuItemPrivate *priv;
+
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (item));
+
+  priv = GET_PRIVATE (item);
+
+  priv->can_raise = can_raise;
+}
+
+GtkWidget*
+mpris_menu_item_new_with_player (const gchar *player,
+                                 const gchar *title,
+                                 const gchar *icon_name,
+                                 const gchar *filename)
+{
+  MprisMenuItem        *menu_item;
+  MprisMenuItemPrivate *priv;
+  GtkWidget            *img;
+
+  TRACE("entering");
+
+  menu_item = MPRIS_MENU_ITEM (g_object_new (TYPE_MPRIS_MENU_ITEM, NULL));
+
+  priv = GET_PRIVATE (menu_item);
+
+  priv->player = g_strdup(player);
+  if (title != NULL)
+    priv->title = g_strdup(title);
+  else
+    priv->title = g_strdup(player);
+  priv->filename = g_strdup(filename);
+
+  priv->vbox = NULL;
+  priv->hbox = NULL;
+  priv->button_box = NULL;
+
+  update_packing (menu_item);
+
+  gtk_widget_add_events (GTK_WIDGET(menu_item), GDK_SCROLL_MASK|GDK_POINTER_MOTION_MASK|GDK_BUTTON_MOTION_MASK);
+
+  img = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), img);
+G_GNUC_END_IGNORE_DEPRECATIONS
+  gtk_image_set_pixel_size (GTK_IMAGE (img), 24);
+
+  return GTK_WIDGET(menu_item);
+}
+
+static gchar *
+find_desktop_entry (const gchar *player_name)
+{
+  GKeyFile  *key_file;
+  gchar     *file;
+  gchar     *filename = NULL;
+  gchar     *full_path;
+
+  file = g_strconcat ("applications/", player_name, ".desktop", NULL);
+
+  key_file = g_key_file_new();
+  if (g_key_file_load_from_data_dirs (key_file, file, &full_path, G_KEY_FILE_NONE, NULL))
+    {
+      filename = g_strconcat (player_name, ".desktop", NULL);
+    }
+  else
+    {
+      /* Support reverse domain name (RDN) formatted launchers. */
+      gchar ***results = g_desktop_app_info_search (player_name);
+      gint i, j;
+
+      for (i = 0; results[i]; i++)
+        {
+          for (j = 0; results[i][j]; j++)
+            {
+              if (filename == NULL)
+                {
+                  filename = g_strdup (results[i][j]);
+                }
+            }
+          g_strfreev (results[i]);
+      }
+      g_free (results);
+    }
+
+  g_key_file_free (key_file);
+  g_free (file);
+
+  return filename;
+}
+
+GtkWidget*
+mpris_menu_item_new_from_player_name (const gchar *player)
+{
+  GtkWidget *widget = NULL;
+  GKeyFile  *key_file;
+  gchar     *file;
+  gchar     *filename;
+  gchar     *full_path;
+
+  filename = find_desktop_entry (player);
+  if (filename == NULL)
+    {
+      g_free (filename);
+      return NULL;
+    }
+
+  file = g_strconcat("applications/", filename, NULL);
+  g_free (filename);
+
+  key_file = g_key_file_new();
+  if (g_key_file_load_from_data_dirs (key_file, file, &full_path, G_KEY_FILE_NONE, NULL))
+    {
+      gchar *name = g_key_file_get_string (key_file, "Desktop Entry", "Name", NULL);
+      gchar *icon_name = g_key_file_get_string (key_file, "Desktop Entry", "Icon", NULL);
+
+      widget = mpris_menu_item_new_with_player (player, name, icon_name, full_path);
+
+      g_free (name);
+      g_free (icon_name);
+    }
+
+  g_key_file_free (key_file);
+  g_free (file);
+
+  return widget;
+}
diff --git a/panel-plugin/mprismenuitem.h b/panel-plugin/mprismenuitem.h
new file mode 100644
index 0000000..80b1c12
--- /dev/null
+++ b/panel-plugin/mprismenuitem.h
@@ -0,0 +1,103 @@
+/* -*- c-basic-offset: 2 -*- vi:set ts=2 sts=2 sw=2:
+ * * Copyright (C) 2017 Sean Davis <bluesabre at xfce.org>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+/*
+ * Based on the scale menu item implementation of the indicator applet:
+ * Authors:
+ *    Cody Russell <crussell at canonical.com>
+ * http://bazaar.launchpad.net/~indicator-applet-developers/ido/trunk.14.10/view/head:/src/idoscalemenuitem.h
+ */
+
+
+#ifndef _MPRIS_MENU_ITEM_H_
+#define _MPRIS_MENU_ITEM_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_MPRIS_MENU_ITEM         (mpris_menu_item_get_type ())
+#define MPRIS_MENU_ITEM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TYPE_MPRIS_MENU_ITEM, MprisMenuItem))
+#define MPRIS_MENU_ITEM_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), TYPE_MPRIS_MENU_ITEM, MprisMenuItemClass))
+#define IS_MPRIS_MENU_ITEM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TYPE_MPRIS_MENU_ITEM))
+#define IS_MPRIS_MENU_ITEM_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), TYPE_MPRIS_MENU_ITEM))
+#define MPRIS_MENU_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_MPRIS_MENU_ITEM, MprisMenuItemClass))
+
+
+typedef struct _MprisMenuItem        MprisMenuItem;
+typedef struct _MprisMenuItemClass   MprisMenuItemClass;
+typedef struct _MprisMenuItemPrivate MprisMenuItemPrivate;
+
+struct _MprisMenuItem
+{
+  GtkImageMenuItem parent_instance;
+
+  MprisMenuItemPrivate *priv;
+};
+
+struct _MprisMenuItemClass
+{
+  GtkImageMenuItemClass parent_class;
+};
+
+
+GType        mpris_menu_item_get_type              (void) G_GNUC_CONST;
+
+GtkWidget   *mpris_menu_item_new_with_player       (const gchar *player,
+                                                    const gchar *title,
+                                                    const gchar *icon_name,
+                                                    const gchar *filename);
+
+GtkWidget   *mpris_menu_item_new_from_player_name  (const gchar *player);
+
+const gchar *mpris_menu_item_get_player            (MprisMenuItem *mi);
+
+void         mpris_menu_item_set_title             (MprisMenuItem *mi,
+                                                    const gchar   *title);
+
+void         mpris_menu_item_set_artist            (MprisMenuItem *mi,
+                                                    const gchar   *artist);
+
+void         mpris_menu_item_set_can_go_previous   (MprisMenuItem *mi,
+                                                    gboolean enabled);
+
+void         mpris_menu_item_set_can_play          (MprisMenuItem *mi,
+                                                    gboolean enabled);
+
+void         mpris_menu_item_set_can_pause         (MprisMenuItem *mi,
+                                                    gboolean enabled);
+
+void         mpris_menu_item_set_can_go_next       (MprisMenuItem *mi,
+                                                    gboolean enabled);
+
+void         mpris_menu_item_set_can_raise         (MprisMenuItem *mi,
+                                                    gboolean can_raise);
+
+void         mpris_menu_item_set_is_running        (MprisMenuItem *mi,
+                                                    gboolean running);
+
+void         mpris_menu_item_set_is_playing        (MprisMenuItem *mi,
+                                                    gboolean playing);
+
+void         mpris_menu_item_set_is_stopped        (MprisMenuItem *mi,
+                                                    gboolean stopped);
+
+G_END_DECLS
+
+#endif /* _MPRIS_MENU_ITEM_H_ */
diff --git a/panel-plugin/pulseaudio-button.c b/panel-plugin/pulseaudio-button.c
index 8dd1639..692a651 100644
--- a/panel-plugin/pulseaudio-button.c
+++ b/panel-plugin/pulseaudio-button.c
@@ -43,6 +43,7 @@
 #include "pulseaudio-plugin.h"
 #include "pulseaudio-config.h"
 #include "pulseaudio-menu.h"
+#include "pulseaudio-mpris.h"
 #include "pulseaudio-button.h"
 
 #define V_MUTED  0
@@ -80,6 +81,7 @@ struct _PulseaudioButton
 
   PulseaudioPlugin     *plugin;
   PulseaudioConfig     *config;
+  PulseaudioMpris      *mpris;
   PulseaudioVolume     *volume;
 
   GtkWidget            *image;
@@ -129,7 +131,11 @@ pulseaudio_button_init (PulseaudioButton *button)
   gtk_widget_set_can_default (GTK_WIDGET (button), FALSE);
   gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
   gtk_button_set_use_underline (GTK_BUTTON (button),TRUE);
+#if GTK_CHECK_VERSION (3, 20, 0)
+  gtk_widget_set_focus_on_click (GTK_WIDGET (button), FALSE);
+#else
   gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+#endif
   gtk_widget_set_name (GTK_WIDGET (button), "pulseaudio-button");
 
   /* Preload icons */
@@ -193,7 +199,7 @@ pulseaudio_button_button_press (GtkWidget      *widget,
   if(event->button == 1 && button->menu == NULL) /* left button */
     {
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
-      button->menu = pulseaudio_menu_new (button->volume, button->config, widget);
+      button->menu = pulseaudio_menu_new (button->volume, button->config, button->mpris, widget);
       gtk_menu_attach_to_widget (GTK_MENU (button->menu), widget, NULL);
 
       if (button->deactivate_id == 0)
@@ -203,12 +209,15 @@ pulseaudio_button_button_press (GtkWidget      *widget,
              G_CALLBACK (pulseaudio_button_menu_deactivate), button);
         }
 
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
       gtk_menu_popup (GTK_MENU (button->menu),
                       NULL, NULL,
                       xfce_panel_plugin_position_menu, button->plugin,
                       //NULL, NULL,
                       1,
                       event->time);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
       return TRUE;
     }
 
@@ -364,12 +373,14 @@ pulseaudio_button_volume_changed (PulseaudioButton  *button,
 PulseaudioButton *
 pulseaudio_button_new (PulseaudioPlugin *plugin,
                        PulseaudioConfig *config,
+                       PulseaudioMpris  *mpris,
                        PulseaudioVolume *volume)
 {
   PulseaudioButton *button;
 
   g_return_val_if_fail (IS_PULSEAUDIO_PLUGIN (plugin), NULL);
   g_return_val_if_fail (IS_PULSEAUDIO_CONFIG (config), NULL);
+  g_return_val_if_fail (IS_PULSEAUDIO_MPRIS (mpris), NULL);
   g_return_val_if_fail (IS_PULSEAUDIO_VOLUME (volume), NULL);
 
   button = g_object_new (TYPE_PULSEAUDIO_BUTTON, NULL);
@@ -377,6 +388,7 @@ pulseaudio_button_new (PulseaudioPlugin *plugin,
   button->plugin = plugin;
   button->volume = volume;
   button->config = config;
+  button->mpris = mpris;
   button->volume_changed_id =
     g_signal_connect_swapped (G_OBJECT (button->volume), "volume-changed",
                               G_CALLBACK (pulseaudio_button_volume_changed), button);
@@ -385,5 +397,3 @@ pulseaudio_button_new (PulseaudioPlugin *plugin,
 
   return button;
 }
-
-
diff --git a/panel-plugin/pulseaudio-button.h b/panel-plugin/pulseaudio-button.h
index b393eb2..7e16f70 100644
--- a/panel-plugin/pulseaudio-button.h
+++ b/panel-plugin/pulseaudio-button.h
@@ -25,6 +25,7 @@
 #include "pulseaudio-config.h"
 #include "pulseaudio-volume.h"
 #include "pulseaudio-menu.h"
+#include "pulseaudio-mpris.h"
 
 G_BEGIN_DECLS
 
@@ -42,6 +43,7 @@ typedef struct          _PulseaudioButtonClass         PulseaudioButtonClass;
 
 PulseaudioButton       *pulseaudio_button_new         (PulseaudioPlugin *plugin,
                                                        PulseaudioConfig *config,
+                                                       PulseaudioMpris  *mpris,
                                                        PulseaudioVolume *volume);
 
 void                    pulseaudio_button_set_size    (PulseaudioButton *button,
diff --git a/panel-plugin/pulseaudio-config.c b/panel-plugin/pulseaudio-config.c
index faeb378..70e81c7 100644
--- a/panel-plugin/pulseaudio-config.c
+++ b/panel-plugin/pulseaudio-config.c
@@ -49,6 +49,7 @@
 #define DEFAULT_SHOW_NOTIFICATIONS                TRUE
 #define DEFAULT_VOLUME_STEP                       6
 #define DEFAULT_VOLUME_MAX                        153
+#define DEFAULT_MPRIS_PLAYERS                     ""
 
 
 
@@ -78,6 +79,7 @@ struct _PulseaudioConfig
   guint            volume_step;
   guint            volume_max;
   gchar           *mixer_command;
+  gchar           *mpris_players;
 };
 
 
@@ -90,6 +92,7 @@ enum
     PROP_VOLUME_STEP,
     PROP_VOLUME_MAX,
     PROP_MIXER_COMMAND,
+    PROP_MPRIS_PLAYERS,
     N_PROPERTIES,
   };
 
@@ -161,6 +164,17 @@ pulseaudio_config_class_init (PulseaudioConfigClass *klass)
                                                         G_PARAM_STATIC_STRINGS));
 
 
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_MPRIS_PLAYERS,
+                                   g_param_spec_string ("mpris-players",
+                                                        NULL, NULL,
+                                                        DEFAULT_MPRIS_PLAYERS,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+
+
   pulseaudio_config_signals[CONFIGURATION_CHANGED] =
     g_signal_new (g_intern_static_string ("configuration-changed"),
                   G_TYPE_FROM_CLASS (gobject_class),
@@ -180,6 +194,7 @@ pulseaudio_config_init (PulseaudioConfig *config)
   config->volume_step               = DEFAULT_VOLUME_STEP;
   config->volume_max                = DEFAULT_VOLUME_MAX;
   config->mixer_command             = g_strdup (DEFAULT_MIXER_COMMAND);
+  config->mpris_players             = g_strdup (DEFAULT_MPRIS_PLAYERS);
 }
 
 
@@ -227,6 +242,10 @@ pulseaudio_config_get_property (GObject    *object,
       g_value_set_string (value, config->mixer_command);
       break;
 
+    case PROP_MPRIS_PLAYERS:
+      g_value_set_string (value, config->mpris_players);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -292,6 +311,13 @@ pulseaudio_config_set_property (GObject      *object,
       config->mixer_command = g_value_dup_string (value);
       break;
 
+    case PROP_MPRIS_PLAYERS:
+      g_free (config->mpris_players);
+      config->mpris_players = g_value_dup_string (value);
+      g_object_notify (G_OBJECT (config), "mpris-players");
+      g_signal_emit (G_OBJECT (config), pulseaudio_config_signals [CONFIGURATION_CHANGED], 0);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -356,6 +382,84 @@ pulseaudio_config_get_mixer_command (PulseaudioConfig *config)
 
 
 
+gchar **
+pulseaudio_config_get_mpris_players (PulseaudioConfig *config)
+{
+  if (!IS_PULSEAUDIO_CONFIG (config))
+    {
+      return g_strsplit (DEFAULT_MPRIS_PLAYERS, ";", 1);
+    }
+
+  return g_strsplit (config->mpris_players, ";", 0);
+}
+
+static gint
+compare_players (gconstpointer item1, gconstpointer item2)
+{
+  return g_ascii_strcasecmp (item1, item2);
+}
+
+void
+pulseaudio_config_set_mpris_players (PulseaudioConfig  *config,
+                                     gchar            **players)
+{
+  GSList *player_array;
+  gchar  *player_string;
+  GValue  src = { 0, };
+  guint   index = 0;
+
+  g_return_if_fail (IS_PULSEAUDIO_CONFIG (config));
+
+  player_array = NULL;
+  for (guint i = 0; i < g_strv_length (players); i++) {
+    player_array = g_slist_prepend (player_array, players[i]);
+  }
+  player_array = g_slist_sort (player_array, (GCompareFunc) compare_players);
+
+  for (GSList *list = player_array; list != NULL; list = g_slist_next (list)) {
+    players[index] = list->data;
+    index++;
+  }
+
+  g_slist_free (player_array);
+
+  player_string = g_strjoinv (";", players);
+
+  g_value_init(&src, G_TYPE_STRING);
+  g_value_set_static_string(&src, player_string);
+
+  pulseaudio_config_set_property (G_OBJECT (config), PROP_MPRIS_PLAYERS, &src, NULL);
+}
+
+void
+pulseaudio_config_add_mpris_player (PulseaudioConfig *config,
+                                    gchar            *player)
+{
+  gchar **players;
+  gchar **player_list;
+  gchar  *players_string;
+  gchar  *player_string;
+
+  players = pulseaudio_config_get_mpris_players (config);
+  if (g_strv_contains ((const char * const *) players, player)) {
+      return;
+  }
+
+  players_string = g_strjoinv (";", players);
+  player_string = g_strjoin (";", players_string, player, NULL);
+  player_list = g_strsplit(player_string, ";", 0);
+
+  pulseaudio_config_set_mpris_players (config, player_list);
+
+  g_strfreev (player_list);
+  g_free (player_string);
+  g_free (players_string);
+  g_strfreev (players);
+}
+
+
+
+
 PulseaudioConfig *
 pulseaudio_config_new (const gchar     *property_base)
 {
@@ -389,6 +493,10 @@ pulseaudio_config_new (const gchar     *property_base)
       xfconf_g_property_bind (channel, property, G_TYPE_STRING, config, "mixer-command");
       g_free (property);
 
+      property = g_strconcat (property_base, "/mpris-players", NULL);
+      xfconf_g_property_bind (channel, property, G_TYPE_STRING, config, "mpris-players");
+      g_free (property);
+
       g_object_notify (G_OBJECT (config), "enable-keyboard-shortcuts");
       g_signal_emit (G_OBJECT (config), pulseaudio_config_signals [CONFIGURATION_CHANGED], 0);
     }
diff --git a/panel-plugin/pulseaudio-config.h b/panel-plugin/pulseaudio-config.h
index b00b48d..cad1054 100644
--- a/panel-plugin/pulseaudio-config.h
+++ b/panel-plugin/pulseaudio-config.h
@@ -43,6 +43,12 @@ gboolean           pulseaudio_config_get_show_notifications         (PulseaudioC
 guint              pulseaudio_config_get_volume_step                (PulseaudioConfig     *config);
 guint              pulseaudio_config_get_volume_max                 (PulseaudioConfig     *config);
 const gchar       *pulseaudio_config_get_mixer_command              (PulseaudioConfig     *config);
+gchar            **pulseaudio_config_get_mpris_players              (PulseaudioConfig     *config);
+
+void               pulseaudio_config_set_mpris_players              (PulseaudioConfig     *config,
+                                                                     gchar               **players);
+void               pulseaudio_config_add_mpris_player               (PulseaudioConfig     *config,
+                                                                     gchar                *player);
 
 G_END_DECLS
 
diff --git a/panel-plugin/pulseaudio-dialog.glade b/panel-plugin/pulseaudio-dialog.glade
index ad7d014..8adbde0 100644
--- a/panel-plugin/pulseaudio-dialog.glade
+++ b/panel-plugin/pulseaudio-dialog.glade
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
 <interface>
-  <!-- interface-requires libxfce4ui 0.0 -->
-  <requires lib="gtk+" version="2.14"/>
-  <!-- interface-naming-policy toplevel-contextual -->
+  <requires lib="gtk+" version="3.6"/>
+  <requires lib="libxfce4ui-2" version="4.11"/>
   <object class="GtkImage" id="image1">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -16,12 +16,13 @@
     <property name="icon_name">gtk-properties</property>
     <property name="type_hint">normal</property>
     <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox2">
+      <object class="GtkBox" id="dialog-vbox2">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
         <property name="spacing">2</property>
         <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area2">
+          <object class="GtkButtonBox" id="dialog-action_area2">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="layout_style">end</property>
@@ -63,11 +64,12 @@
           </packing>
         </child>
         <child>
-          <object class="GtkVBox" id="vbox1">
+          <object class="GtkBox" id="vbox1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="border_width">6</property>
-            <property name="spacing">6</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
             <child>
               <object class="GtkFrame" id="frame1">
                 <property name="visible">True</property>
@@ -78,12 +80,14 @@
                   <object class="GtkAlignment" id="alignment1">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="top_padding">6</property>
                     <property name="left_padding">12</property>
+                    <property name="right_padding">12</property>
                     <child>
-                      <object class="GtkVBox" id="vbox2">
+                      <object class="GtkBox" id="vbox2">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="border_width">6</property>
+                        <property name="orientation">vertical</property>
                         <property name="spacing">6</property>
                         <child>
                           <object class="GtkCheckButton" id="checkbutton-keyboard-shortcuts">
@@ -92,6 +96,7 @@
                             <property name="can_focus">True</property>
                             <property name="receives_default">False</property>
                             <property name="tooltip_text" translatable="yes">Enables volume control using multimedia keys. Make sure no other application that listens to these keys (e.g. xfce4-volumed) is running in the background.</property>
+                            <property name="halign">start</property>
                             <property name="use_underline">True</property>
                             <property name="draw_indicator">True</property>
                           </object>
@@ -108,6 +113,7 @@
                             <property name="can_focus">True</property>
                             <property name="receives_default">False</property>
                             <property name="tooltip_text" translatable="yes">Enables on-screen volume notifications.</property>
+                            <property name="halign">start</property>
                             <property name="use_underline">True</property>
                             <property name="draw_indicator">True</property>
                           </object>
@@ -148,79 +154,56 @@
                   <object class="GtkAlignment" id="alignment2">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="top_padding">6</property>
                     <property name="left_padding">12</property>
+                    <property name="right_padding">12</property>
                     <child>
-                      <object class="GtkVBox" id="vbox4">
+                      <object class="GtkGrid">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="border_width">6</property>
-                        <property name="spacing">6</property>
+                        <property name="row_spacing">6</property>
+                        <property name="column_spacing">12</property>
                         <child>
-                          <object class="GtkTable" id="table1">
+                          <object class="GtkLabel" id="label2">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="n_rows">2</property>
-                            <property name="n_columns">2</property>
-                            <property name="column_spacing">12</property>
-                            <property name="row_spacing">6</property>
-                            <child>
-                              <object class="GtkLabel" id="label2">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="label" translatable="yes">Audio _Mixer</property>
-                                <property name="use_underline">True</property>
-                              </object>
-                              <packing>
-                                <property name="x_options">GTK_FILL</property>
-                                <property name="y_options">GTK_FILL</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkEntry" id="entry-mixer-command">
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="tooltip_text" translatable="yes">Audio mixer command that can be executed from the context menu, e.g. "pavucontrol", "unity-control-center sound".</property>
-                                <property name="invisible_char">•</property>
-                                <property name="primary_icon_activatable">False</property>
-                                <property name="secondary_icon_activatable">False</property>
-                                <property name="primary_icon_sensitive">True</property>
-                                <property name="secondary_icon_sensitive">True</property>
-                              </object>
-                              <packing>
-                                <property name="left_attach">1</property>
-                                <property name="right_attach">2</property>
-                                <property name="y_options">GTK_FILL</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkAlignment" id="alignment3">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="xalign">0</property>
-                                <property name="xscale">0</property>
-                                <child>
-                                  <object class="GtkButton" id="button-run-mixer">
-                                    <property name="label" translatable="yes">_Run Audio Mixer...</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">True</property>
-                                    <property name="image">image1</property>
-                                    <property name="use_underline">True</property>
-                                  </object>
-                                </child>
-                              </object>
-                              <packing>
-                                <property name="right_attach">2</property>
-                                <property name="top_attach">1</property>
-                                <property name="bottom_attach">2</property>
-                                <property name="y_options">GTK_FILL</property>
-                              </packing>
-                            </child>
+                            <property name="label" translatable="yes">Audio _Mixer</property>
+                            <property name="use_underline">True</property>
                           </object>
                           <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">True</property>
-                            <property name="position">0</property>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="entry-mixer-command">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="tooltip_text" translatable="yes">Audio mixer command that can be executed from the context menu, e.g. "pavucontrol", "unity-control-center sound".</property>
+                            <property name="hexpand">True</property>
+                            <property name="invisible_char">•</property>
+                            <property name="primary_icon_activatable">False</property>
+                            <property name="secondary_icon_activatable">False</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="button-run-mixer">
+                            <property name="label" translatable="yes">_Run Audio Mixer...</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="halign">start</property>
+                            <property name="image">image1</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                            <property name="width">2</property>
                           </packing>
                         </child>
                       </object>
diff --git a/panel-plugin/pulseaudio-menu.c b/panel-plugin/pulseaudio-menu.c
index e0a8c21..3f0982d 100644
--- a/panel-plugin/pulseaudio-menu.c
+++ b/panel-plugin/pulseaudio-menu.c
@@ -35,6 +35,8 @@
 #include <libxfce4ui/libxfce4ui.h>
 
 #include "pulseaudio-menu.h"
+#include "pulseaudio-mpris.h"
+#include "mprismenuitem.h"
 #include "scalemenuitem.h"
 
 
@@ -44,6 +46,7 @@ struct _PulseaudioMenu
 
   PulseaudioVolume     *volume;
   PulseaudioConfig     *config;
+  PulseaudioMpris      *mpris;
   GtkWidget            *button;
   GtkWidget            *range_output;
   GtkWidget            *mute_output_item;
@@ -81,6 +84,7 @@ pulseaudio_menu_init (PulseaudioMenu *menu)
 {
   menu->volume                         = NULL;
   menu->config                         = NULL;
+  menu->mpris                          = NULL;
   menu->button                         = NULL;
   menu->range_output                   = NULL;
   menu->mute_output_item               = NULL;
@@ -106,6 +110,7 @@ pulseaudio_menu_finalize (GObject *object)
 
   menu->volume                         = NULL;
   menu->config                         = NULL;
+  menu->mpris                          = NULL;
   menu->button                         = NULL;
   menu->range_output                   = NULL;
   menu->mute_output_item               = NULL;
@@ -279,19 +284,114 @@ pulseaudio_menu_volume_changed (PulseaudioMenu   *menu,
   gtk_range_set_value (GTK_RANGE (menu->range_input), pulseaudio_volume_get_volume_mic (menu->volume) * 100.0);
 }
 
+static void
+media_notify_cb (GtkWidget  *widget,
+                 gchar      *message,
+                 gpointer    user_data)
+{
+  PulseaudioMenu *menu = user_data;
 
+  g_return_if_fail (IS_PULSEAUDIO_MENU (menu));
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (widget));
+
+  pulseaudio_mpris_notify_player (menu->mpris, mpris_menu_item_get_player (MPRIS_MENU_ITEM (widget)), message);
+}
+
+static void
+mpris_update_cb (PulseaudioMpris *mpris,
+                 gchar           *player,
+                 gpointer         user_data)
+{
+  MprisMenuItem *menu_item = user_data;
+
+  gchar          *title;
+  gchar          *artist;
+  gboolean        is_playing;
+  gboolean        is_stopped;
+  gboolean        can_play;
+  gboolean        can_pause;
+  gboolean        can_go_previous;
+  gboolean        can_go_next;
+  gboolean        can_raise;
+
+  g_return_if_fail (IS_PULSEAUDIO_MPRIS (mpris));
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (menu_item));
+
+  if (mpris_menu_item_get_player (menu_item) == NULL)
+    {
+      return;
+    }
+
+    if (g_strcmp0 (player, mpris_menu_item_get_player (menu_item)) == 0)
+      {
+        if (pulseaudio_mpris_get_player_snapshot (mpris,
+                                                  player,
+                                                  &title,
+                                                  &artist,
+                                                  &is_playing,
+                                                  &is_stopped,
+                                                  &can_play,
+                                                  &can_pause,
+                                                  &can_go_previous,
+                                                  &can_go_next,
+                                                  &can_raise))
+          {
+            mpris_menu_item_set_is_running (menu_item, TRUE);
+            mpris_menu_item_set_title (menu_item, title);
+            mpris_menu_item_set_artist (menu_item, artist);
+
+            mpris_menu_item_set_can_play (menu_item, can_play);
+            mpris_menu_item_set_can_pause (menu_item, can_pause);
+
+            mpris_menu_item_set_can_go_previous (menu_item, can_go_previous);
+            mpris_menu_item_set_can_go_next (menu_item, can_go_next);
+
+            mpris_menu_item_set_is_playing (menu_item, is_playing);
+            mpris_menu_item_set_is_stopped (menu_item, is_stopped);
+          }
+
+        if (title != NULL)
+          g_free (title);
+        if (artist != NULL)
+          g_free (artist);
+    }
+}
+
+static void
+item_destroy_cb (GtkWidget  *widget,
+                 gpointer    user_data)
+{
+  PulseaudioMenu *menu = user_data;
+
+  g_return_if_fail (IS_PULSEAUDIO_MENU (menu));
+  g_return_if_fail (IS_MPRIS_MENU_ITEM (widget));
+
+  g_signal_handlers_disconnect_by_func (G_OBJECT (menu->mpris), G_CALLBACK (mpris_update_cb), widget);
+}
 
 PulseaudioMenu *
 pulseaudio_menu_new (PulseaudioVolume *volume,
                      PulseaudioConfig *config,
+                     PulseaudioMpris  *mpris,
                      GtkWidget        *widget)
 {
   PulseaudioMenu *menu;
   GdkScreen      *gscreen;
   GtkWidget      *mi;
-  GtkWidget      *img = NULL;
   gdouble         volume_max;
 
+  gchar         **players;
+
+  gchar          *title = NULL;
+  gchar          *artist = NULL;
+  gboolean        is_playing;
+  gboolean        is_stopped;
+  gboolean        can_play;
+  gboolean        can_pause;
+  gboolean        can_go_previous;
+  gboolean        can_go_next;
+  gboolean        can_raise;
+
   g_return_val_if_fail (IS_PULSEAUDIO_VOLUME (volume), NULL);
   g_return_val_if_fail (IS_PULSEAUDIO_CONFIG (config), NULL);
   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
@@ -301,12 +401,12 @@ pulseaudio_menu_new (PulseaudioVolume *volume,
   else
     gscreen = gdk_display_get_default_screen (gdk_display_get_default ());
 
-
   menu = g_object_new (TYPE_PULSEAUDIO_MENU, NULL);
   gtk_menu_set_screen (GTK_MENU (menu), gscreen);
 
   menu->volume = volume;
   menu->config = config;
+  menu->mpris = mpris;
   menu->button = widget;
   menu->volume_changed_id =
     g_signal_connect_swapped (G_OBJECT (menu->volume), "volume-changed",
@@ -319,11 +419,7 @@ pulseaudio_menu_new (PulseaudioVolume *volume,
 
   /* output volume slider */
   mi = scale_menu_item_new_with_range (0.0, volume_max, 1.0);
-
-  img = gtk_image_new_from_icon_name ("audio-volume-high-symbolic", GTK_ICON_SIZE_DND);
-  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), img);
-  gtk_image_set_pixel_size (GTK_IMAGE (img), 24);
-
+  scale_menu_item_set_image_from_icon_name (SCALE_MENU_ITEM (mi), "audio-volume-high-symbolic");
   scale_menu_item_set_description_label (SCALE_MENU_ITEM (mi), _("<b>Audio output volume</b>"));
 
   /* range slider */
@@ -347,11 +443,7 @@ pulseaudio_menu_new (PulseaudioVolume *volume,
 
   /* input volume slider */
   mi = scale_menu_item_new_with_range (0.0, volume_max, 1.0);
-
-  img = gtk_image_new_from_icon_name ("microphone-sensitivity-high-symbolic", GTK_ICON_SIZE_DND);
-  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), img);
-  gtk_image_set_pixel_size (GTK_IMAGE (img), 24);
-
+  scale_menu_item_set_image_from_icon_name (SCALE_MENU_ITEM (mi), "microphone-sensitivity-high-symbolic");
   scale_menu_item_set_description_label (SCALE_MENU_ITEM (mi), _("<b>Audio input volume</b>"));
 
   /* range slider */
@@ -373,6 +465,68 @@ pulseaudio_menu_new (PulseaudioVolume *volume,
   gtk_widget_show (mi);
   gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
 
+  /* MPRIS2 */
+  players = pulseaudio_config_get_mpris_players (menu->config);
+  if (players != NULL)
+    {
+      for (guint i = 0; i < g_strv_length (players); i++)
+        {
+          mi = mpris_menu_item_new_from_player_name (players[i]);
+          if (mi != NULL)
+            {
+              if (pulseaudio_mpris_get_player_snapshot (menu->mpris,
+                                                        players[i],
+                                                        &title,
+                                                        &artist,
+                                                        &is_playing,
+                                                        &is_stopped,
+                                                        &can_play,
+                                                        &can_pause,
+                                                        &can_go_previous,
+                                                        &can_go_next,
+                                                        &can_raise))
+                {
+                  mpris_menu_item_set_is_running (MPRIS_MENU_ITEM (mi), TRUE);
+                  mpris_menu_item_set_title (MPRIS_MENU_ITEM (mi), title);
+                  mpris_menu_item_set_artist (MPRIS_MENU_ITEM (mi), artist);
+
+                  mpris_menu_item_set_can_raise (MPRIS_MENU_ITEM (mi), can_raise);
+
+                  mpris_menu_item_set_can_play (MPRIS_MENU_ITEM (mi), can_play);
+                  mpris_menu_item_set_can_pause (MPRIS_MENU_ITEM (mi), can_pause);
+
+                  mpris_menu_item_set_can_go_previous (MPRIS_MENU_ITEM (mi), can_go_previous);
+                  mpris_menu_item_set_can_go_next (MPRIS_MENU_ITEM (mi), can_go_next);
+
+                  mpris_menu_item_set_is_playing (MPRIS_MENU_ITEM (mi), is_playing);
+                  mpris_menu_item_set_is_stopped (MPRIS_MENU_ITEM (mi), is_stopped);
+
+                  if (title != NULL)
+                    g_free (title);
+                  if (artist != NULL)
+                    g_free (artist);
+                }
+              else
+                {
+                  mpris_menu_item_set_is_running (MPRIS_MENU_ITEM (mi), FALSE);
+                  mpris_menu_item_set_is_stopped (MPRIS_MENU_ITEM (mi), TRUE);
+                }
+
+              g_signal_connect (mi, "media-notify", G_CALLBACK (media_notify_cb), menu);
+              g_signal_connect (menu->mpris, "update", G_CALLBACK (mpris_update_cb), mi);
+              g_signal_connect (mi, "destroy", G_CALLBACK(item_destroy_cb), menu);
+
+              gtk_widget_show (mi);
+              gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+
+              /* separator */
+              mi = gtk_separator_menu_item_new ();
+              gtk_widget_show (mi);
+              gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+            }
+        }
+    }
+
   /* Audio mixers */
   mi = gtk_menu_item_new_with_mnemonic (_("_Audio mixer..."));
   gtk_widget_show (mi);
@@ -381,8 +535,5 @@ pulseaudio_menu_new (PulseaudioVolume *volume,
 
   pulseaudio_menu_volume_changed (menu, FALSE, menu->volume);
 
-
   return menu;
 }
-
-
diff --git a/panel-plugin/pulseaudio-menu.h b/panel-plugin/pulseaudio-menu.h
index 724e82a..3849b79 100644
--- a/panel-plugin/pulseaudio-menu.h
+++ b/panel-plugin/pulseaudio-menu.h
@@ -23,6 +23,7 @@
 
 #include "pulseaudio-volume.h"
 #include "pulseaudio-config.h"
+#include "pulseaudio-mpris.h"
 
 G_BEGIN_DECLS
 
@@ -40,6 +41,7 @@ typedef struct          _PulseaudioMenuClass         PulseaudioMenuClass;
 
 PulseaudioMenu         *pulseaudio_menu_new         (PulseaudioVolume *volume,
                                                      PulseaudioConfig *config,
+                                                     PulseaudioMpris  *mpris,
                                                      GtkWidget        *widget);
 
 G_END_DECLS
diff --git a/panel-plugin/pulseaudio-mpris-player.c b/panel-plugin/pulseaudio-mpris-player.c
new file mode 100644
index 0000000..c07eb37
--- /dev/null
+++ b/panel-plugin/pulseaudio-mpris-player.c
@@ -0,0 +1,759 @@
+/*  Copyright (c) 2017 Sean Davis <bluesabre at xfce.org>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "pulseaudio-mpris-player.h"
+
+struct _PulseaudioMprisPlayer
+{
+  GObject          __parent__;
+
+  GDBusConnection  *dbus_connection;
+  GDBusProxy       *dbus_props_proxy;
+  GDBusProxy       *dbus_player_proxy;
+  gchar            *dbus_name;
+
+  gchar            *player;
+  gchar            *player_label;
+  gchar            *icon_name;
+
+  gboolean          connected;
+
+  gchar            *title;
+  gchar            *artist;
+
+  gboolean          can_go_next;
+  gboolean          can_go_previous;
+  gboolean          can_pause;
+  gboolean          can_play;
+  gboolean          can_raise;
+
+  PlaybackStatus    playback_status;
+
+  guint             watch_id;
+};
+
+struct _PulseaudioMprisPlayerClass
+{
+  GObjectClass            __parent__;
+  void (*connection)      (PulseaudioMprisPlayer *player, gboolean        connected);
+  void (*playback_status) (PulseaudioMprisPlayer *player, PlaybackStatus  playback_status);
+  void (*metadata)        (PulseaudioMprisPlayer *player);
+};
+
+
+static void pulseaudio_mpris_player_finalize     (GObject               *object);
+static void pulseaudio_mpris_player_dbus_connect (PulseaudioMprisPlayer *player);
+
+
+enum
+{
+  CONNECTION,
+  PLAYBACK_STATUS,
+  METADATA,
+  VOLUME,
+  LAST_SIGNAL
+};
+static int signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (PulseaudioMprisPlayer, pulseaudio_mpris_player, G_TYPE_OBJECT)
+
+static void
+pulseaudio_mpris_player_class_init (PulseaudioMprisPlayerClass *klass)
+{
+  GObjectClass      *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = pulseaudio_mpris_player_finalize;
+
+  signals[CONNECTION] =
+    g_signal_new ("connection",
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (PulseaudioMprisPlayerClass, connection),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__BOOLEAN,
+                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+  signals[PLAYBACK_STATUS] =
+    g_signal_new ("playback-status",
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (PulseaudioMprisPlayerClass, playback_status),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__ENUM,
+                  G_TYPE_NONE, 1, G_TYPE_INT);
+
+  signals[METADATA] =
+    g_signal_new ("metadata",
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (PulseaudioMprisPlayerClass, metadata),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+pulseaudio_mpris_player_parse_metadata (PulseaudioMprisPlayer *player,
+                                        GVariant              *dictionary)
+{
+  GVariantIter iter;
+  GVariant *value;
+  gchar *key;
+
+  gchar **artists;
+
+  g_variant_iter_init (&iter, dictionary);
+  while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
+    {
+      if (0 == g_ascii_strcasecmp (key, "xesam:title"))
+        player->title = g_strdup (g_variant_get_string (value, NULL));
+      else if (0 == g_ascii_strcasecmp (key, "xesam:artist"))
+        {
+          artists = g_variant_dup_strv (value, NULL);
+          if (artists != NULL)
+            {
+              if (g_strv_length (artists) > 0)
+                {
+                  player->artist = g_strdup (artists[0]);
+                }
+              else
+                {
+                  player->artist = g_strdup ("");
+                }
+              g_strfreev (artists);
+            }
+          }
+    }
+}
+
+void
+pulseaudio_mpris_player_call_player_method (PulseaudioMprisPlayer *player,
+                                            const gchar           *method)
+{
+  GDBusMessage *message;
+  GError       *error = NULL;
+  gchar        *iface = NULL;
+
+  if (g_strcmp0 (method, "Raise") == 0)
+    iface = "org.mpris.MediaPlayer2";
+  else if (g_strcmp0 (method, "Quit") == 0)
+    iface = "org.mpris.MediaPlayer2";
+  else
+    iface = "org.mpris.MediaPlayer2.Player";
+
+  message = g_dbus_message_new_method_call (player->dbus_name,
+                                            "/org/mpris/MediaPlayer2",
+                                            iface,
+                                            method);
+
+  g_dbus_connection_send_message (player->dbus_connection,
+                                  message,
+                                  G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                  NULL,
+                                  &error);
+  if (error != NULL)
+    {
+      g_warning ("unable to send message: %s", error->message);
+      g_clear_error (&error);
+      error = NULL;
+    }
+
+  g_dbus_connection_flush_sync (player->dbus_connection, NULL, &error);
+  if (error != NULL)
+    {
+      g_warning ("unable to flush message queue: %s", error->message);
+      g_clear_error (&error);
+    }
+
+  g_object_unref (message);
+}
+
+static GVariant *
+pulseaudio_mpris_player_get_all_player_properties (PulseaudioMprisPlayer *player)
+{
+  GVariantIter iter;
+  GVariant *result, *child = NULL;
+
+  result = g_dbus_connection_call_sync (player->dbus_connection,
+                                        player->dbus_name,
+                                        "/org/mpris/MediaPlayer2",
+                                        "org.freedesktop.DBus.Properties",
+                                        "GetAll",
+                                        g_variant_new ("(s)", "org.mpris.MediaPlayer2.Player"),
+                                        G_VARIANT_TYPE ("(a{sv})"),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        -1,
+                                        NULL,
+                                        NULL);
+
+  if (result)
+    {
+      g_variant_iter_init (&iter, result);
+      child = g_variant_iter_next_value (&iter);
+    }
+
+  return child;
+}
+
+static GVariant *
+pulseaudio_mpris_player_get_all_media_player_properties (PulseaudioMprisPlayer *player)
+{
+  GVariantIter iter;
+  GVariant *result, *child = NULL;
+
+  result = g_dbus_connection_call_sync (player->dbus_connection,
+                                        player->dbus_name,
+                                        "/org/mpris/MediaPlayer2",
+                                        "org.freedesktop.DBus.Properties",
+                                        "GetAll",
+                                        g_variant_new ("(s)", "org.mpris.MediaPlayer2"),
+                                        G_VARIANT_TYPE ("(a{sv})"),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        -1,
+                                        NULL,
+                                        NULL);
+
+  if (result)
+    {
+      g_variant_iter_init (&iter, result);
+      child = g_variant_iter_next_value (&iter);
+    }
+
+  return child;
+}
+
+static void
+pulseaudio_mpris_player_parse_playback_status (PulseaudioMprisPlayer *player,
+                                               const gchar           *playback_status)
+{
+  if (0 == g_ascii_strcasecmp(playback_status, "Playing"))
+    player->playback_status = PLAYING;
+  else if (0 == g_ascii_strcasecmp(playback_status, "Paused"))
+    player->playback_status = PAUSED;
+  else
+    player->playback_status = STOPPED;
+}
+
+static void
+pulseaudio_mpris_player_parse_player_properties (PulseaudioMprisPlayer *player,
+                                                 GVariant              *properties)
+{
+  GVariantIter  iter;
+  GVariant     *value;
+  const gchar  *key;
+  const gchar  *playback_status = NULL;
+
+  g_variant_iter_init (&iter, properties);
+
+  while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
+    if (0 == g_ascii_strcasecmp (key, "PlaybackStatus"))
+      {
+        playback_status = g_variant_get_string(value, NULL);
+      }
+    else if (0 == g_ascii_strcasecmp (key, "CanGoNext"))
+      {
+        player->can_go_next = g_variant_get_boolean(value);
+      }
+    else if (0 == g_ascii_strcasecmp (key, "CanGoPrevious"))
+      {
+        player->can_go_previous = g_variant_get_boolean(value);
+      }
+    else if (0 == g_ascii_strcasecmp (key, "CanPlay"))
+      {
+        player->can_play = g_variant_get_boolean(value);
+      }
+    else if (0 == g_ascii_strcasecmp (key, "CanPause"))
+      {
+        player->can_pause = g_variant_get_boolean(value);
+      }
+    else if (0 == g_ascii_strcasecmp (key, "Metadata"))
+      {
+        pulseaudio_mpris_player_parse_metadata (player, value);
+        g_signal_emit (player, signals[METADATA], 0, NULL);
+      }
+    }
+
+  if (playback_status != NULL)
+    {
+      pulseaudio_mpris_player_parse_playback_status (player, playback_status);
+      g_signal_emit (player, signals[PLAYBACK_STATUS], 0, player->playback_status);
+    }
+}
+
+static void
+pulseaudio_mpris_player_parse_media_player_properties (PulseaudioMprisPlayer *player,
+                                                       GVariant              *properties)
+{
+  GVariantIter  iter;
+  GVariant     *value;
+  const gchar  *key;
+
+  g_variant_iter_init (&iter, properties);
+
+  while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
+    {
+      if (0 == g_ascii_strcasecmp (key, "CanRaise"))
+        {
+          player->can_raise = g_variant_get_boolean(value);
+        }
+    }
+}
+
+static void
+pulseaudio_mpris_player_on_dbus_property_signal (GDBusProxy *proxy,
+                                                 gchar      *sender_name,
+                                                 gchar      *signal_name,
+                                                 GVariant   *parameters,
+                                                 gpointer    user_data)
+{
+  GVariantIter iter;
+  GVariant *child;
+
+  PulseaudioMprisPlayer *player = user_data;
+
+  if (g_ascii_strcasecmp (signal_name, "PropertiesChanged"))
+    return;
+
+  g_variant_iter_init (&iter, parameters);
+
+  child = g_variant_iter_next_value (&iter); /* Interface name. */
+  g_variant_unref (child);
+
+  child = g_variant_iter_next_value (&iter); /* Property name. */
+  pulseaudio_mpris_player_parse_player_properties (player, child);
+  g_variant_unref (child);
+}
+
+static void
+pulseaudio_mpris_player_on_dbus_connected (GDBusConnection *connection,
+                                           const gchar     *name,
+                                           const gchar     *name_owner,
+                                           gpointer         user_data)
+{
+  GVariant *reply;
+
+  PulseaudioMprisPlayer *player = user_data;
+
+  player->connected = TRUE;
+
+  /* Notify that connect to a player.*/
+  g_signal_emit (player, signals[CONNECTION], 0, player->connected);
+
+  /* And informs the current status of the player */
+  reply = pulseaudio_mpris_player_get_all_player_properties (player);
+  pulseaudio_mpris_player_parse_player_properties (player, reply);
+  g_variant_unref (reply);
+
+  reply = pulseaudio_mpris_player_get_all_media_player_properties (player);
+  pulseaudio_mpris_player_parse_media_player_properties (player, reply);
+  g_variant_unref (reply);
+}
+
+static void
+pulseaudio_mpris_player_on_dbus_lost (GDBusConnection *connection,
+                                      const gchar     *name,
+                                      gpointer         user_data)
+{
+  PulseaudioMprisPlayer *player = user_data;
+
+  /* Interface MediaPlayer2.Player */
+  player->playback_status = STOPPED;
+  player->can_go_next     = FALSE;
+  player->can_go_previous = FALSE;
+  player->can_play        = FALSE;
+  player->can_pause       = FALSE;
+  player->can_raise       = FALSE;
+  player->connected       = FALSE;
+
+  if (player->title != NULL)
+    g_free (player->title);
+  if (player->artist != NULL)
+    g_free (player->artist);
+
+  player->title           = NULL;
+  player->artist          = NULL;
+
+  g_signal_emit (player, signals[CONNECTION], 0, player->connected);
+}
+
+static gchar *
+find_desktop_entry (const gchar *player_name)
+{
+  GKeyFile  *key_file;
+  gchar     *file;
+  gchar     *filename = NULL;
+  gchar     *full_path;
+
+  file = g_strconcat ("applications/", player_name, ".desktop", NULL);
+
+  key_file = g_key_file_new();
+  if (g_key_file_load_from_data_dirs (key_file, file, &full_path, G_KEY_FILE_NONE, NULL))
+    {
+      filename = g_strconcat (player_name, ".desktop", NULL);
+    }
+  else
+    {
+      /* Support reverse domain name (RDN) formatted launchers. */
+      gchar ***results = g_desktop_app_info_search (player_name);
+      gint i, j;
+
+      for (i = 0; results[i]; i++)
+        {
+          for (j = 0; results[i][j]; j++)
+            {
+              if (filename == NULL)
+                {
+                  filename = g_strdup (results[i][j]);
+                }
+            }
+          g_strfreev (results[i]);
+        }
+      g_free (results);
+    }
+
+  g_key_file_free (key_file);
+  g_free (file);
+
+  return filename;
+}
+
+static void
+pulseaudio_mpris_player_set_details_from_desktop (PulseaudioMprisPlayer *player,
+                                                  const gchar           *player_name)
+{
+  GKeyFile  *key_file;
+  gchar     *file;
+  gchar     *full_path;
+  gchar     *filename;
+
+  filename = find_desktop_entry (player_name);
+
+  if (filename == NULL)
+    {
+      player->player_label = g_strdup(player->player);
+      player->icon_name = "applications-multimedia";
+
+      g_free (filename);
+      return;
+    }
+
+  file = g_strconcat("applications/", filename, NULL);
+  g_free (filename);
+
+  key_file = g_key_file_new();
+  if (g_key_file_load_from_data_dirs (key_file, file, &full_path, G_KEY_FILE_NONE, NULL))
+    {
+      gchar *name = g_key_file_get_string (key_file, "Desktop Entry", "Name", NULL);
+      gchar *icon_name = g_key_file_get_string (key_file, "Desktop Entry", "Icon", NULL);
+
+      player->player_label = g_strdup(name);
+      player->icon_name = g_strdup(icon_name);
+
+      g_free (name);
+      g_free (icon_name);
+    }
+  else
+    {
+      player->player_label = g_strdup(player->player);
+      player->icon_name = "applications-multimedia";
+    }
+
+  g_key_file_free (key_file);
+  g_free (file);
+}
+
+static void
+pulseaudio_mpris_player_set_player (PulseaudioMprisPlayer *player,
+                                    const gchar           *player_name)
+{
+  /* Disconnect dbus */
+  if (player->watch_id)
+    {
+      g_bus_unwatch_name (player->watch_id);
+      player->watch_id = 0;
+    }
+  if (player->dbus_props_proxy != NULL)
+    {
+      g_object_unref (player->dbus_props_proxy);
+      player->dbus_props_proxy = NULL;
+    }
+  if (player->dbus_player_proxy != NULL)
+    {
+      g_object_unref (player->dbus_player_proxy);
+      player->dbus_player_proxy = NULL;
+    }
+
+  /* Clean player */
+  if (player->player != NULL)
+    {
+      g_free (player->player);
+      player->player = NULL;
+    }
+
+  /* Set new player and connect again */
+  if (player != NULL)
+    {
+      player->player = g_strdup(player_name);
+
+      pulseaudio_mpris_player_set_details_from_desktop (player, player_name);
+      pulseaudio_mpris_player_dbus_connect (player);
+    }
+}
+
+static void
+pulseaudio_mpris_player_dbus_connect (PulseaudioMprisPlayer *player)
+{
+  GDBusProxy *proxy;
+  GError     *gerror = NULL;
+  guint       watch_id;
+
+  if (player->player == NULL)
+    return;
+
+  g_free(player->dbus_name);
+  player->dbus_name = g_strdup_printf("org.mpris.MediaPlayer2.%s", player->player);
+
+  watch_id = g_bus_watch_name_on_connection(player->dbus_connection,
+                                            player->dbus_name,
+                                            G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                                            pulseaudio_mpris_player_on_dbus_connected,
+                                            pulseaudio_mpris_player_on_dbus_lost,
+                                            player,
+                                            NULL);
+
+  proxy = g_dbus_proxy_new_sync (player->dbus_connection,
+                                 G_DBUS_PROXY_FLAGS_NONE,
+                                 NULL,
+                                 player->dbus_name,
+                                 "/org/mpris/MediaPlayer2",
+                                 "org.freedesktop.DBus.Properties",
+                                 NULL, /* GCancellable */
+                                 &gerror);
+
+  if (proxy == NULL)
+    {
+      g_printerr ("Error creating proxy: %s\n", gerror->message);
+      g_error_free (gerror);
+      gerror = NULL;
+    }
+  else
+    {
+      g_signal_connect (proxy, "g-signal",
+                        G_CALLBACK (pulseaudio_mpris_player_on_dbus_property_signal), player);
+      player->dbus_props_proxy = proxy;
+    }
+
+  /* interface=org.mpris.MediaPlayer2.Player */
+  proxy = g_dbus_proxy_new_sync (player->dbus_connection,
+                                 G_DBUS_PROXY_FLAGS_NONE,
+                                 NULL,
+                                 player->dbus_name,
+                                 "/org/mpris/MediaPlayer2",
+                                 "org.mpris.MediaPlayer2.Player",
+                                 NULL, /* GCancellable */
+                                 &gerror);
+
+  if (proxy == NULL)
+    {
+      g_printerr ("Error creating proxy: %s\n", gerror->message);
+      g_error_free (gerror);
+      gerror = NULL;
+    }
+  else
+    {
+      player->dbus_player_proxy = proxy;
+    }
+
+  player->watch_id = watch_id;
+}
+
+const gchar *
+pulseaudio_mpris_player_get_player (PulseaudioMprisPlayer *player)
+{
+  return player->player;
+}
+
+const gchar *
+pulseaudio_mpris_player_get_player_title (PulseaudioMprisPlayer *player)
+{
+  return player->player_label;
+}
+
+const gchar *
+pulseaudio_mpris_player_get_icon_name (PulseaudioMprisPlayer *player)
+{
+  return player->icon_name;
+}
+
+const gchar *
+pulseaudio_mpris_player_get_title (PulseaudioMprisPlayer *player)
+{
+
+  return player->title;
+}
+
+const gchar *
+pulseaudio_mpris_player_get_artist (PulseaudioMprisPlayer *player)
+{
+  return player->artist;
+}
+
+gboolean
+pulseaudio_mpris_player_is_connected (PulseaudioMprisPlayer *player)
+{
+  return player->connected;
+}
+
+gboolean
+pulseaudio_mpris_player_is_playing (PulseaudioMprisPlayer *player)
+{
+  return player->playback_status == PLAYING;
+}
+
+gboolean
+pulseaudio_mpris_player_is_stopped (PulseaudioMprisPlayer *player)
+{
+  return player->playback_status == STOPPED;
+}
+
+gboolean
+pulseaudio_mpris_player_can_play (PulseaudioMprisPlayer *player)
+{
+  return player->can_play;
+}
+
+gboolean
+pulseaudio_mpris_player_can_pause (PulseaudioMprisPlayer *player)
+{
+  return player->can_pause;
+}
+
+gboolean
+pulseaudio_mpris_player_can_go_previous (PulseaudioMprisPlayer *player)
+{
+  return player->can_go_previous;
+}
+
+gboolean
+pulseaudio_mpris_player_can_go_next (PulseaudioMprisPlayer *player)
+{
+  return player->can_go_next;
+}
+
+gboolean
+pulseaudio_mpris_player_can_raise (PulseaudioMprisPlayer *player)
+{
+  return player->can_raise;
+}
+
+gboolean
+pulseaudio_mpris_player_is_equal (PulseaudioMprisPlayer *a,
+                                  PulseaudioMprisPlayer *b)
+{
+    return g_strcmp0 (pulseaudio_mpris_player_get_player_title (a), pulseaudio_mpris_player_get_player_title (b)) == 0;
+}
+
+static void
+pulseaudio_mpris_player_init (PulseaudioMprisPlayer *player)
+{
+  player->dbus_connection   = NULL;
+  player->dbus_name         = NULL;
+  player->dbus_props_proxy  = NULL;
+  player->dbus_player_proxy = NULL;
+  player->connected         = FALSE;
+
+  player->title             = NULL;
+  player->artist            = NULL;
+
+  player->can_go_next       = FALSE;
+  player->can_go_previous   = FALSE;
+  player->can_pause         = FALSE;
+  player->can_play          = FALSE;
+  player->can_raise         = FALSE;
+
+  player->playback_status   = STOPPED;
+
+  player->watch_id          = 0;
+}
+
+
+static void
+pulseaudio_mpris_player_finalize (GObject *object)
+{
+  PulseaudioMprisPlayer *player;
+
+  player = PULSEAUDIO_MPRIS_PLAYER (object);
+
+  player->dbus_connection   = NULL;
+  player->dbus_name         = NULL;
+  player->dbus_props_proxy  = NULL;
+  player->dbus_player_proxy = NULL;
+  player->connected         = FALSE;
+
+  player->title             = NULL;
+  player->artist            = NULL;
+
+  player->can_go_next       = FALSE;
+  player->can_go_previous   = FALSE;
+  player->can_pause         = FALSE;
+  player->can_play          = FALSE;
+  player->can_raise         = FALSE;
+
+  player->playback_status   = STOPPED;
+
+  player->watch_id          = 0;
+
+  (*G_OBJECT_CLASS (pulseaudio_mpris_player_parent_class)->finalize) (object);
+}
+
+PulseaudioMprisPlayer *
+pulseaudio_mpris_player_new (gchar *name)
+{
+  PulseaudioMprisPlayer *player;
+  GDBusConnection       *gconnection;
+  GError                *gerror = NULL;
+
+  gconnection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &gerror);
+  if (gconnection == NULL)
+    {
+      g_message ("Failed to get session bus: %s", gerror->message);
+      g_error_free (gerror);
+      gerror = NULL;
+    }
+
+  player = g_object_new (TYPE_PULSEAUDIO_MPRIS_PLAYER, NULL);
+
+  player->dbus_connection = gconnection;
+
+  pulseaudio_mpris_player_dbus_connect (player);
+  pulseaudio_mpris_player_set_player (player, name);
+
+  return player;
+}
diff --git a/panel-plugin/pulseaudio-mpris-player.h b/panel-plugin/pulseaudio-mpris-player.h
new file mode 100644
index 0000000..b4e8b8a
--- /dev/null
+++ b/panel-plugin/pulseaudio-mpris-player.h
@@ -0,0 +1,79 @@
+/*  Copyright (c) 2017 Sean Davis <bluesabre at xfce.org>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __PULSEAUDIO_MPRIS_PLAYER_PLAYER_H__
+#define __PULSEAUDIO_MPRIS_PLAYER_PLAYER_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PlaybackStatus:
+ * @PLAYING: A track is currently playing.
+ * @PAUSED: A track is currently paused.
+ * @STOPPED: There is no track currently playing.
+ *
+ * The current playback status:
+ * See mpris2 specification <ulink url="http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Property:PlaybackStatus">PlaybackStatus</ulink>
+ */
+typedef enum {
+  PLAYING = 1,
+  PAUSED,
+  STOPPED
+} PlaybackStatus;
+
+GType pulseaudio_mpris_player_get_type (void);
+
+#define TYPE_PULSEAUDIO_MPRIS_PLAYER            (pulseaudio_mpris_player_get_type())
+#define PULSEAUDIO_MPRIS_PLAYER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PULSEAUDIO_MPRIS_PLAYER, PulseaudioMprisPlayer))
+#define PULSEAUDIO_MPRIS_PLAYER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  TYPE_PULSEAUDIO_MPRIS_PLAYER, PulseaudioMprisPlayerClass))
+#define IS_PULSEAUDIO_MPRIS_PLAYER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_PULSEAUDIO_MPRIS_PLAYER))
+#define IS_PULSEAUDIO_MPRIS_PLAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  TYPE_PULSEAUDIO_MPRIS_PLAYER))
+#define PULSEAUDIO_MPRIS_PLAYER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  TYPE_PULSEAUDIO_MPRIS_PLAYER, PulseaudioMprisPlayerClass))
+
+typedef struct          _PulseaudioMprisPlayer                     PulseaudioMprisPlayer;
+typedef struct          _PulseaudioMprisPlayerClass                PulseaudioMprisPlayerClass;
+
+PulseaudioMprisPlayer  *pulseaudio_mpris_player_new                (gchar *name);
+
+const gchar            *pulseaudio_mpris_player_get_player         (PulseaudioMprisPlayer *player);
+const gchar            *pulseaudio_mpris_player_get_player_title   (PulseaudioMprisPlayer *player);
+const gchar            *pulseaudio_mpris_player_get_icon_name      (PulseaudioMprisPlayer *player);
+const gchar            *pulseaudio_mpris_player_get_title          (PulseaudioMprisPlayer *player);
+const gchar            *pulseaudio_mpris_player_get_artist         (PulseaudioMprisPlayer *player);
+
+gboolean                pulseaudio_mpris_player_is_connected       (PulseaudioMprisPlayer *player);
+gboolean                pulseaudio_mpris_player_is_playing         (PulseaudioMprisPlayer *player);
+gboolean                pulseaudio_mpris_player_is_stopped         (PulseaudioMprisPlayer *player);
+
+gboolean                pulseaudio_mpris_player_can_play           (PulseaudioMprisPlayer *player);
+gboolean                pulseaudio_mpris_player_can_pause          (PulseaudioMprisPlayer *player);
+gboolean                pulseaudio_mpris_player_can_go_previous    (PulseaudioMprisPlayer *player);
+gboolean                pulseaudio_mpris_player_can_go_next        (PulseaudioMprisPlayer *player);
+gboolean                pulseaudio_mpris_player_can_raise          (PulseaudioMprisPlayer *player);
+
+gboolean                pulseaudio_mpris_player_is_equal           (PulseaudioMprisPlayer *a,
+                                                                    PulseaudioMprisPlayer *b);
+
+void                    pulseaudio_mpris_player_call_player_method (PulseaudioMprisPlayer *player,
+                                                                    const gchar           *method);
+
+G_END_DECLS
+
+#endif /* !__PULSEAUDIO_MPRIS_PLAYER_H__ */
diff --git a/panel-plugin/pulseaudio-mpris.c b/panel-plugin/pulseaudio-mpris.c
new file mode 100644
index 0000000..4ceac95
--- /dev/null
+++ b/panel-plugin/pulseaudio-mpris.c
@@ -0,0 +1,308 @@
+/*  Copyright (c) 2017 Sean Davis <bluesabre at xfce.org>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "pulseaudio-mpris.h"
+#include "pulseaudio-mpris-player.h"
+
+struct _PulseaudioMpris
+{
+  GObject           __parent__;
+  PulseaudioConfig *config;
+
+  GDBusConnection  *dbus_connection;
+  GHashTable       *players;
+
+  guint             player_timer_id;
+};
+
+struct _PulseaudioMprisClass
+{
+  GObjectClass          __parent__;
+  void (*update)        (PulseaudioMpris *mpris);
+};
+
+
+static void             pulseaudio_mpris_finalize         (GObject         *object);
+
+
+enum
+{
+  UPDATE,
+  LAST_SIGNAL
+};
+static int signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (PulseaudioMpris, pulseaudio_mpris, G_TYPE_OBJECT)
+
+static void
+pulseaudio_mpris_class_init (PulseaudioMprisClass *klass)
+{
+  GObjectClass      *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = pulseaudio_mpris_finalize;
+
+  signals[UPDATE] =
+      g_signal_new ("update",
+                    G_TYPE_FROM_CLASS (gobject_class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (PulseaudioMprisClass, update),
+                    NULL, NULL,
+                    g_cclosure_marshal_VOID__STRING,
+                    G_TYPE_NONE, 1, G_TYPE_STRING);
+
+}
+
+
+gchar **
+pulseaudio_mpris_get_available_players (PulseaudioMpris *mpris)
+{
+  GError *error = NULL;
+  GVariant *v;
+  GVariantIter *iter;
+  const gchar *str = NULL;
+  gchar **res = NULL;
+  guint items = 0;
+
+  v = g_dbus_connection_call_sync (mpris->dbus_connection,
+                                   "org.freedesktop.DBus",
+                                   "/org/freedesktop/DBus",
+                                   "org.freedesktop.DBus",
+                                   "ListNames",
+                                   NULL,
+                                   G_VARIANT_TYPE ("(as)"),
+                                   G_DBUS_CALL_FLAGS_NONE,
+                                   -1,
+                                   NULL,
+                                   &error);
+  if (error)
+    {
+      g_critical ("Could not get a list of names registered on the session bus, %s",
+                  error ? error->message : "no error given");
+      g_clear_error (&error);
+      return NULL;
+    }
+
+  g_variant_get (v, "(as)", &iter);
+  while (g_variant_iter_loop (iter, "&s", &str))
+    {
+      if (g_str_has_prefix(str, "org.mpris.MediaPlayer2."))
+        {
+          res = (gchar**)g_realloc(res, (items + 1) * sizeof(gchar*));
+          res[items] = g_strdup(str + 23);
+          items++;
+        }
+    }
+
+  /* Add NULL termination to the res vector */
+  if (items > 0)
+    {
+      res = g_realloc(res, (items + 1) * sizeof(gchar*));
+      res[items] = NULL;
+    }
+
+  g_variant_iter_free (iter);
+  g_variant_unref (v);
+
+  return res;
+}
+
+static void
+pulseaudio_mpris_player_update_cb (PulseaudioMprisPlayer *player,
+                                   gchar                 *sender_name,
+                                   gpointer               user_data)
+{
+  PulseaudioMpris *mpris = user_data;
+
+  g_return_if_fail (IS_PULSEAUDIO_MPRIS (mpris));
+
+  g_signal_emit (mpris, signals[UPDATE], 0, pulseaudio_mpris_player_get_player (player));
+}
+
+static void
+pulseaudio_mpris_player_metadata_cb (PulseaudioMprisPlayer *player,
+                                     gpointer               user_data)
+{
+  PulseaudioMpris *mpris = user_data;
+
+  g_return_if_fail (IS_PULSEAUDIO_MPRIS (mpris));
+
+  g_signal_emit (mpris, signals[UPDATE], 0, pulseaudio_mpris_player_get_player (player));
+}
+
+static gboolean
+pulseaudio_mpris_tick_cb (gpointer user_data)
+{
+  PulseaudioMpris        *mpris = user_data;
+  PulseaudioMprisPlayer  *player;
+  gchar                 **players;
+
+  players = pulseaudio_mpris_get_available_players (mpris);
+  if (players == NULL)
+    return TRUE;
+
+  for (guint i = 0; i < g_strv_length (players); i++)
+    {
+      if (!g_hash_table_contains (mpris->players, players[i]))
+        {
+          player = pulseaudio_mpris_player_new (players[i]);
+
+          g_signal_connect (player, "connection", G_CALLBACK (pulseaudio_mpris_player_update_cb), mpris);
+          g_signal_connect (player, "playback-status", G_CALLBACK (pulseaudio_mpris_player_update_cb), mpris);
+          g_signal_connect (player, "metadata", G_CALLBACK (pulseaudio_mpris_player_metadata_cb), mpris);
+
+          g_hash_table_insert (mpris->players, players[i], player);
+
+          pulseaudio_config_add_mpris_player (mpris->config, players[i]);
+        }
+    }
+
+  return TRUE;
+}
+
+gboolean
+pulseaudio_mpris_get_player_snapshot (PulseaudioMpris  *mpris,
+                                      const gchar      *name,
+                                      gchar           **title,
+                                      gchar           **artist,
+                                      gboolean         *is_playing,
+                                      gboolean         *is_stopped,
+                                      gboolean         *can_play,
+                                      gboolean         *can_pause,
+                                      gboolean         *can_go_previous,
+                                      gboolean         *can_go_next,
+                                      gboolean         *can_raise)
+{
+  PulseaudioMprisPlayer *player;
+  player = PULSEAUDIO_MPRIS_PLAYER (g_hash_table_lookup (mpris->players, name));
+
+  if (player != NULL)
+    {
+      if (pulseaudio_mpris_player_is_connected (player))
+        {
+          *title = g_strdup(pulseaudio_mpris_player_get_title (player));
+          *artist = g_strdup(pulseaudio_mpris_player_get_artist (player));
+
+          *is_playing         = pulseaudio_mpris_player_is_playing (player);
+          *is_stopped         = pulseaudio_mpris_player_is_stopped (player);
+          *can_play           = pulseaudio_mpris_player_can_play (player);
+          *can_pause          = pulseaudio_mpris_player_can_pause (player);
+          *can_go_previous    = pulseaudio_mpris_player_can_go_previous (player);
+          *can_go_next        = pulseaudio_mpris_player_can_go_next (player);
+          *can_raise          = pulseaudio_mpris_player_can_raise (player);
+        }
+      else
+        {
+          *title = g_strdup(pulseaudio_mpris_player_get_player_title (player));
+          *artist = g_strdup("Not currently playing");
+
+          *is_playing         = FALSE;
+          *is_stopped         = TRUE;
+          *can_play           = FALSE;
+          *can_pause          = FALSE;
+          *can_go_previous    = FALSE;
+          *can_go_next        = FALSE;
+          *can_raise          = FALSE;
+        }
+      if (*title == NULL || g_strcmp0 (*title, "") == 0)
+        *title = g_strdup(pulseaudio_mpris_player_get_player_title (player));
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+gboolean
+pulseaudio_mpris_notify_player (PulseaudioMpris  *mpris,
+                                const gchar      *name,
+                                const gchar      *message)
+{
+  PulseaudioMprisPlayer *player;
+
+  g_return_val_if_fail (IS_PULSEAUDIO_MPRIS (mpris), FALSE);
+
+  player = g_hash_table_lookup (mpris->players, name);
+
+  if (player != NULL)
+    {
+      if (pulseaudio_mpris_player_is_connected (player))
+        {
+          pulseaudio_mpris_player_call_player_method (player, message);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+pulseaudio_mpris_init (PulseaudioMpris *mpris)
+{
+  mpris->config            = NULL;
+  mpris->dbus_connection   = NULL;
+}
+
+static void
+pulseaudio_mpris_finalize (GObject *object)
+{
+  PulseaudioMpris *mpris;
+
+  mpris = PULSEAUDIO_MPRIS (object);
+
+  mpris->config            = NULL;
+  mpris->dbus_connection   = NULL;
+
+  (*G_OBJECT_CLASS (pulseaudio_mpris_parent_class)->finalize) (object);
+}
+
+PulseaudioMpris *
+pulseaudio_mpris_new (PulseaudioConfig *config)
+{
+  PulseaudioMpris *mpris;
+  GDBusConnection *gconnection;
+  GError          *gerror = NULL;
+
+  g_return_val_if_fail (IS_PULSEAUDIO_CONFIG (config), NULL);
+
+  gconnection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &gerror);
+  if (gconnection == NULL)
+    {
+      g_message ("Failed to get session bus: %s", gerror->message);
+      g_error_free (gerror);
+      gerror = NULL;
+    }
+
+  mpris = g_object_new (TYPE_PULSEAUDIO_MPRIS, NULL);
+
+  mpris->config = config;
+  mpris->dbus_connection = gconnection;
+  mpris->players = g_hash_table_new (g_str_hash, g_str_equal);
+  mpris->player_timer_id = g_timeout_add_seconds (1, pulseaudio_mpris_tick_cb, mpris);
+
+  return mpris;
+}
diff --git a/panel-plugin/pulseaudio-mpris.h b/panel-plugin/pulseaudio-mpris.h
new file mode 100644
index 0000000..c3aeabc
--- /dev/null
+++ b/panel-plugin/pulseaudio-mpris.h
@@ -0,0 +1,62 @@
+/*  Copyright (c) 2017 Sean Davis <bluesabre at xfce.org>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __PULSEAUDIO_MPRIS_H__
+#define __PULSEAUDIO_MPRIS_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "pulseaudio-config.h"
+
+G_BEGIN_DECLS
+
+GType pulseaudio_mpris_get_type (void);
+
+#define TYPE_PULSEAUDIO_MPRIS            (pulseaudio_mpris_get_type())
+#define PULSEAUDIO_MPRIS(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PULSEAUDIO_MPRIS, PulseaudioMpris))
+#define PULSEAUDIO_MPRIS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  TYPE_PULSEAUDIO_MPRIS, PulseaudioMprisClass))
+#define IS_PULSEAUDIO_MPRIS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_PULSEAUDIO_MPRIS))
+#define IS_PULSEAUDIO_MPRIS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  TYPE_PULSEAUDIO_MPRIS))
+#define PULSEAUDIO_MPRIS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  TYPE_PULSEAUDIO_MPRIS, PulseaudioMprisClass))
+
+typedef struct          _PulseaudioMpris                       PulseaudioMpris;
+typedef struct          _PulseaudioMprisClass                  PulseaudioMprisClass;
+
+PulseaudioMpris        *pulseaudio_mpris_new                   (PulseaudioConfig *config);
+
+gchar                 **pulseaudio_mpris_get_available_players (PulseaudioMpris  *mpris);
+
+gboolean                pulseaudio_mpris_get_player_snapshot   (PulseaudioMpris  *mpris,
+                                                                const gchar      *name,
+                                                                gchar           **title,
+                                                                gchar           **artist,
+                                                                gboolean         *is_playing,
+                                                                gboolean         *is_stopped,
+                                                                gboolean         *can_play,
+                                                                gboolean         *can_pause,
+                                                                gboolean         *can_go_previous,
+                                                                gboolean         *can_go_next,
+                                                                gboolean         *can_raise);
+
+gboolean                pulseaudio_mpris_notify_player         (PulseaudioMpris  *mpris,
+                                                                const gchar      *name,
+                                                                const gchar      *message);
+
+G_END_DECLS
+
+#endif /* !__PULSEAUDIO_MPRIS_H__ */
diff --git a/panel-plugin/pulseaudio-plugin.c b/panel-plugin/pulseaudio-plugin.c
index 2ef8eb4..6d6b550 100644
--- a/panel-plugin/pulseaudio-plugin.c
+++ b/panel-plugin/pulseaudio-plugin.c
@@ -44,6 +44,7 @@
 #include "pulseaudio-button.h"
 #include "pulseaudio-dialog.h"
 #include "pulseaudio-notify.h"
+#include "pulseaudio-mpris.h"
 
 #ifdef HAVE_IDO
 #include <libido/libido.h>
@@ -104,6 +105,10 @@ struct _PulseaudioPlugin
 
   /* config dialog builder */
   PulseaudioDialog    *dialog;
+
+  /* mpris */
+  PulseaudioMpris     *mpris;
+  gchar              **players;
 };
 
 
@@ -142,6 +147,8 @@ pulseaudio_plugin_init (PulseaudioPlugin *pulseaudio_plugin)
 #ifdef HAVE_LIBNOTIFY
   pulseaudio_plugin->notify            = NULL;
 #endif
+
+  pulseaudio_plugin->mpris             = NULL;
 }
 
 
@@ -413,10 +420,16 @@ pulseaudio_plugin_construct (XfcePanelPlugin *plugin)
   /* volume controller */
   pulseaudio_plugin->volume = pulseaudio_volume_new (pulseaudio_plugin->config);
 
+  /* initialize mpris support */
+  pulseaudio_plugin->mpris = pulseaudio_mpris_new (pulseaudio_plugin->config);
+  pulseaudio_plugin->players = pulseaudio_mpris_get_available_players (pulseaudio_plugin->mpris);
+
   /* instantiate a button box */
   pulseaudio_plugin->button = pulseaudio_button_new (pulseaudio_plugin,
                                                      pulseaudio_plugin->config,
+                                                     pulseaudio_plugin->mpris,
                                                      pulseaudio_plugin->volume);
+
   /* initialize notify wrapper */
 #ifdef HAVE_LIBNOTIFY
   pulseaudio_plugin->notify = pulseaudio_notify_new (pulseaudio_plugin->config,
@@ -429,4 +442,3 @@ pulseaudio_plugin_construct (XfcePanelPlugin *plugin)
 
 
 }
-
diff --git a/panel-plugin/pulseaudio-volume.c b/panel-plugin/pulseaudio-volume.c
index c263599..acdbfdd 100644
--- a/panel-plugin/pulseaudio-volume.c
+++ b/panel-plugin/pulseaudio-volume.c
@@ -213,7 +213,7 @@ pulseaudio_volume_source_info_cb (pa_context           *context,
       volume->volume_mic = vol_mic;
       g_signal_emit (G_OBJECT (volume), pulseaudio_volume_signals [VOLUME_MIC_CHANGED], 0, FALSE);
     }
-  pulseaudio_debug ("volume mic: %d, muted mic: %d", vol_mic, muted_mic);
+  pulseaudio_debug ("volume mic: %f, muted mic: %d", vol_mic, muted_mic);
 }
 
 
@@ -597,7 +597,7 @@ pulseaudio_volume_set_volume_cb2 (pa_context         *context,
   if (i == NULL) return;
 
   //pulseaudio_debug ("*** %s", pa_cvolume_snprint (st, sizeof (st), &i->volume));
-  pa_cvolume_set (&i->volume, 1, pulseaudio_volume_d2v (volume, volume->volume));
+  pa_cvolume_set ((pa_cvolume *)&i->volume, 1, pulseaudio_volume_d2v (volume, volume->volume));
   pa_context_set_sink_volume_by_index (context, i->index, &i->volume, pulseaudio_volume_sink_volume_changed, volume);
 }
 
@@ -664,7 +664,7 @@ pulseaudio_volume_set_volume_mic_cb2 (pa_context           *context,
   if (i == NULL) return;
 
   //pulseaudio_debug ("*** %s", pa_cvolume_snprint (st, sizeof (st), &i->volume));
-  pa_cvolume_set (&i->volume, 1, pulseaudio_volume_d2v (volume, volume->volume_mic));
+  pa_cvolume_set ((pa_cvolume *)&i->volume, 1, pulseaudio_volume_d2v (volume, volume->volume_mic));
   pa_context_set_source_volume_by_index (context, i->index, &i->volume, pulseaudio_volume_source_volume_changed, volume);
 }
 
@@ -719,5 +719,3 @@ pulseaudio_volume_new (PulseaudioConfig *config)
 
   return volume;
 }
-
-
diff --git a/panel-plugin/scalemenuitem.c b/panel-plugin/scalemenuitem.c
index 2755075..ee09ca8 100644
--- a/panel-plugin/scalemenuitem.c
+++ b/panel-plugin/scalemenuitem.c
@@ -75,7 +75,9 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
 G_DEFINE_TYPE (ScaleMenuItem, scale_menu_item, GTK_TYPE_IMAGE_MENU_ITEM)
+G_GNUC_END_IGNORE_DEPRECATIONS
 
 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_SCALE_MENU_ITEM, ScaleMenuItemPrivate))
 
@@ -111,7 +113,7 @@ scale_menu_item_class_init (ScaleMenuItemClass *item_class)
    * ScaleMenuItem::slider-grabbed:
    * @menuitem: The #ScaleMenuItem emitting the signal.
    *
-   * The ::slider-grabbed signal is emitted when the pointer selects the slider. 
+   * The ::slider-grabbed signal is emitted when the pointer selects the slider.
    */
   signals[SLIDER_GRABBED] = g_signal_new ("slider-grabbed",
                                           G_OBJECT_CLASS_TYPE (gobject_class),
@@ -284,7 +286,9 @@ scale_menu_item_button_release_event (GtkWidget      *menuitem,
                                       GdkEventButton *event)
 {
   ScaleMenuItemPrivate *priv;
+#if !(GTK_CHECK_VERSION (3, 14, 0))
   gint                  x, y;
+#endif
 
   TRACE("entering");
 
@@ -472,6 +476,22 @@ scale_menu_item_get_percentage_label (ScaleMenuItem *menuitem)
   return gtk_label_get_text (GTK_LABEL (priv->percentage_label));
 }
 
+static GtkWidget *
+scale_menu_item_label_new (const gchar *str)
+{
+  GtkWidget *label = gtk_label_new (str);
+
+  /* align left */
+#if GTK_CHECK_VERSION (3, 16, 0)
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+#else
+  gtk_misc_set_alignment (GTK_MISC(label), 0, 0);
+#endif
+
+  return label;
+}
+
 /**
  * scale_menu_item_set_description_label:
  * @menuitem: The #ScaleMenuItem
@@ -505,11 +525,8 @@ scale_menu_item_set_description_label (ScaleMenuItem *menuitem,
   else if(label)
     {
       /* create label */
-      priv->description_label = gtk_label_new (NULL);
+      priv->description_label = scale_menu_item_label_new (NULL);
       gtk_label_set_markup (GTK_LABEL (priv->description_label), label);
-
-      /* align left */
-      gtk_misc_set_alignment (GTK_MISC(priv->description_label), 0, 0);
     }
 
     update_packing (menuitem);
@@ -549,9 +566,7 @@ scale_menu_item_set_percentage_label (ScaleMenuItem *menuitem,
   else if(label)
     {
       /* create label */
-      priv->percentage_label = gtk_label_new (label);
-      /* align left */
-      gtk_misc_set_alignment (GTK_MISC(priv->percentage_label), 0, 0);
+      priv->percentage_label = scale_menu_item_label_new (label);
     }
 
     update_packing (menuitem);
@@ -582,3 +597,18 @@ scale_menu_item_set_value (ScaleMenuItem *item,
   gtk_range_set_value (GTK_RANGE (priv->scale), value);
   priv->ignore_value_changed = FALSE;
 }
+
+void
+scale_menu_item_set_image_from_icon_name (ScaleMenuItem *item,
+                                          const gchar   *icon_name)
+{
+  GtkWidget *img = NULL;
+
+  g_return_if_fail (IS_SCALE_MENU_ITEM (item));
+
+  img = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), img);
+G_GNUC_END_IGNORE_DEPRECATIONS
+  gtk_image_set_pixel_size (GTK_IMAGE (img), 24);
+}
diff --git a/panel-plugin/scalemenuitem.h b/panel-plugin/scalemenuitem.h
index 772419a..2d793e2 100644
--- a/panel-plugin/scalemenuitem.h
+++ b/panel-plugin/scalemenuitem.h
@@ -73,8 +73,11 @@ void         scale_menu_item_set_description_label (ScaleMenuItem *menuitem,
 void         scale_menu_item_set_percentage_label  (ScaleMenuItem *menuitem,
                                                     const gchar      *label);
 
-void        scale_menu_item_set_value (ScaleMenuItem *item,
-                                       gdouble        value);
+void         scale_menu_item_set_value             (ScaleMenuItem *item,
+                                                    gdouble        value);
+
+void         scale_menu_item_set_image_from_icon_name (ScaleMenuItem *menuitem,
+                                                       const gchar   *icon_name);
 
 
 G_END_DECLS
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 75a84e8..0cae865 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,3 +7,4 @@ panel-plugin/pulseaudio-button.c
 panel-plugin/pulseaudio-volume.c
 panel-plugin/pulseaudio-menu.c
 panel-plugin/pulseaudio-notify.c
+panel-plugin/mprismenuitem.c

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Xfce4-commits mailing list