From a82f9d88000fedc6b0aa9a325c3222b5cf3a8de8 Mon Sep 17 00:00:00 2001
From: Victor Kareh <vkareh@redhat.com>
Date: Sat, 9 Mar 2019 11:41:13 -0500
Subject: Re-enable old GtkStatusIcon

This allows us to have builds that use both the legacy status icon as well as the new applet.
---
 mate-volume-control/Makefile.am              |  28 +-
 mate-volume-control/applet-main.c            |   1 +
 mate-volume-control/gvc-applet.c             |  87 ++-
 mate-volume-control/gvc-applet.h             |   1 +
 mate-volume-control/gvc-status-icon.c        | 365 +++++++++++++
 mate-volume-control/gvc-status-icon.h        |  61 +++
 mate-volume-control/gvc-stream-applet-icon.c | 775 +++++++++++++++++++++++++++
 mate-volume-control/gvc-stream-applet-icon.h |  82 +++
 mate-volume-control/gvc-stream-status-icon.c | 238 ++++----
 mate-volume-control/gvc-stream-status-icon.h |  17 +-
 mate-volume-control/status-icon-main.c       | 100 ++++
 11 files changed, 1573 insertions(+), 182 deletions(-)
 create mode 100644 mate-volume-control/gvc-status-icon.c
 create mode 100644 mate-volume-control/gvc-status-icon.h
 create mode 100644 mate-volume-control/gvc-stream-applet-icon.c
 create mode 100644 mate-volume-control/gvc-stream-applet-icon.h
 create mode 100644 mate-volume-control/status-icon-main.c

diff --git a/mate-volume-control/Makefile.am b/mate-volume-control/Makefile.am
index f67d087..a2173bb 100644
--- a/mate-volume-control/Makefile.am
+++ b/mate-volume-control/Makefile.am
@@ -1,6 +1,10 @@
 NULL =
 
-bin_PROGRAMS = mate-volume-control
+bin_PROGRAMS = \
+	 mate-volume-control-status-icon \
+	 mate-volume-control \
+	$(NULL)
+
 libexec_PROGRAMS = mate-volume-control-applet
 
 AM_CPPFLAGS = \
@@ -45,6 +49,22 @@ libmatevolumecontrol_la_SOURCES = \
 	gvc-channel-bar.c \
 	$(NULL)
 
+mate_volume_control_status_icon_LDADD = \
+	-lm \
+	libmatevolumecontrol.la \
+	$(VOLUME_CONTROL_LIBS) \
+	$(NULL)
+
+mate_volume_control_status_icon_SOURCES = \
+	gvc-stream-status-icon.h \
+	gvc-stream-status-icon.c \
+	gvc-status-icon.h \
+	gvc-status-icon.c \
+	status-icon-main.c \
+	$(NULL)
+
+mate_volume_control_status_icon_CFLAGS = $(WARN_CFLAGS)
+
 mate_volume_control_applet_LDADD = \
 	-lm \
 	libmatevolumecontrol.la \
@@ -53,14 +73,14 @@ mate_volume_control_applet_LDADD = \
 	$(NULL)
 
 mate_volume_control_applet_SOURCES = \
-	gvc-stream-status-icon.h \
-	gvc-stream-status-icon.c \
+	gvc-stream-applet-icon.h \
+	gvc-stream-applet-icon.c \
 	gvc-applet.h \
 	gvc-applet.c \
 	applet-main.c \
 	$(NULL)
 
-mate_volume_contro_applet_CFLAGS = $(WARN_CFLAGS)
+mate_volume_control_applet_CFLAGS = $(WARN_CFLAGS)
 
 mate_volume_control_LDADD = \
 	-lm \
diff --git a/mate-volume-control/applet-main.c b/mate-volume-control/applet-main.c
index 776f823..58614a9 100644
--- a/mate-volume-control/applet-main.c
+++ b/mate-volume-control/applet-main.c
@@ -2,6 +2,7 @@
  *
  * Copyright (C) 2008 Red Hat, Inc.
  * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
+ * Copyright (C) 2019 Victor Kareh <vkareh@vkareh.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
diff --git a/mate-volume-control/gvc-applet.c b/mate-volume-control/gvc-applet.c
index 21baadb..cb51431 100644
--- a/mate-volume-control/gvc-applet.c
+++ b/mate-volume-control/gvc-applet.c
@@ -32,7 +32,7 @@
 #include <mate-panel-applet.h>
 
 #include "gvc-applet.h"
-#include "gvc-stream-status-icon.h"
+#include "gvc-stream-applet-icon.h"
 
 #define GVC_APPLET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_APPLET, GvcAppletPrivate))
 
@@ -55,8 +55,7 @@ static const gchar *icon_names_input[] = {
 static void menu_output_mute (GtkAction *action, GvcApplet *applet);
 static void menu_activate_open_volume_control (GtkAction *action, GvcApplet *applet);
 static const GtkActionEntry applet_menu_actions [] = {
-        { "Preferences", APPLET_ICON, N_("_Sound Preferences"), NULL, NULL,
-                                        G_CALLBACK(menu_activate_open_volume_control) },
+        { "Preferences", APPLET_ICON, N_("_Sound Preferences"), NULL, NULL, G_CALLBACK(menu_activate_open_volume_control) },
         { "MuteOutput", "audio-volume-muted", N_("Mute Output"), NULL, NULL, G_CALLBACK (menu_output_mute) }
 };
 
@@ -65,8 +64,8 @@ static char *ui = "<menuitem name='Preferences' action='Preferences' />"
 
 struct _GvcAppletPrivate
 {
-        GvcStreamStatusIcon *icon_input;
-        GvcStreamStatusIcon *icon_output;
+        GvcStreamAppletIcon *icon_input;
+        GvcStreamAppletIcon *icon_output;
         gboolean             running;
         MateMixerContext    *context;
         MateMixerStream     *input;
@@ -91,13 +90,11 @@ update_icon_input (GvcApplet *applet)
          * is a non-mixer application using the input */
         if (applet->priv->input != NULL) {
                 const gchar *app_id;
-                const GList *inputs =
-                        mate_mixer_stream_list_controls (applet->priv->input);
+                const GList *inputs = mate_mixer_stream_list_controls (applet->priv->input);
 
                 control = mate_mixer_stream_get_default_control (applet->priv->input);
 
-                const gchar *stream_name =
-                        mate_mixer_stream_get_name (applet->priv->input);
+                const gchar *stream_name = mate_mixer_stream_get_name (applet->priv->input);
                 g_debug ("Got stream name %s", stream_name);
                 if (g_str_has_suffix (stream_name, ".monitor")) {
                         inputs = NULL;
@@ -105,14 +102,11 @@ update_icon_input (GvcApplet *applet)
                 }
 
                 while (inputs != NULL) {
-                        MateMixerStreamControl    *input =
-                                MATE_MIXER_STREAM_CONTROL (inputs->data);
-                        MateMixerStreamControlRole role =
-                                mate_mixer_stream_control_get_role (input);
+                        MateMixerStreamControl *input = MATE_MIXER_STREAM_CONTROL (inputs->data);
+                        MateMixerStreamControlRole role = mate_mixer_stream_control_get_role (input);
 
                         if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) {
-                                MateMixerAppInfo *app_info =
-                                        mate_mixer_stream_control_get_app_info (input);
+                                MateMixerAppInfo *app_info = mate_mixer_stream_control_get_app_info (input);
 
                                 app_id = mate_mixer_app_info_get_id (app_info);
                                 if (app_id == NULL) {
@@ -152,7 +146,7 @@ update_icon_input (GvcApplet *applet)
                         g_debug ("There is no recording application, input icon disabled");
         }
 
-        gvc_stream_status_icon_set_control (applet->priv->icon_input, control);
+        gvc_stream_applet_icon_set_control (applet->priv->icon_input, control);
 
         gtk_widget_set_visible (GTK_WIDGET (applet->priv->icon_input), show);
 }
@@ -167,17 +161,15 @@ update_icon_output (GvcApplet *applet)
         if (stream != NULL)
                 control = mate_mixer_stream_get_default_control (stream);
 
-        gvc_stream_status_icon_set_control (applet->priv->icon_output, control);
+        gvc_stream_applet_icon_set_control (applet->priv->icon_output, control);
 
         if (control != NULL) {
                 g_debug ("Output icon enabled");
-                gtk_widget_set_visible (GTK_WIDGET (applet->priv->icon_output),
-                                             TRUE);
+                gtk_widget_set_visible (GTK_WIDGET (applet->priv->icon_output), TRUE);
         }
         else {
                 g_debug ("There is no output stream/control, output icon disabled");
-                gtk_widget_set_visible (GTK_WIDGET (applet->priv->icon_output),
-                                             FALSE);
+                gtk_widget_set_visible (GTK_WIDGET (applet->priv->icon_output), FALSE);
         }
 }
 
@@ -190,8 +182,7 @@ on_input_stream_control_added (MateMixerStream *stream,
 
         control = mate_mixer_stream_get_control (stream, name);
         if G_LIKELY (control != NULL) {
-                MateMixerStreamControlRole role =
-                        mate_mixer_stream_control_get_role (control);
+                MateMixerStreamControlRole role = mate_mixer_stream_control_get_role (control);
 
                 /* Non-application input control doesn't affect the icon */
                 if (role != MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION)
@@ -210,7 +201,7 @@ on_input_stream_control_removed (MateMixerStream *stream,
                                  GvcApplet       *applet)
 {
         /* The removed stream could be an application input, which may cause
-         * the input status icon to disappear */
+         * the input applet icon to disappear */
         update_icon_input (applet);
 }
 
@@ -225,8 +216,7 @@ update_default_input_stream (GvcApplet *applet)
 
         /* The input stream has changed */
         if (applet->priv->input != NULL) {
-                g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input),
-                                                      applet);
+                g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input), applet);
                 g_object_unref (applet->priv->input);
         }
 
@@ -261,7 +251,7 @@ on_context_state_notify (MateMixerContext *context,
         case MATE_MIXER_STATE_READY:
                 update_default_input_stream (applet);
 
-                /* Each status change may affect the visibility of the icons */
+                /* Each applet change may affect the visibility of the icons */
                 update_icon_output (applet);
                 update_icon_input (applet);
                 break;
@@ -314,8 +304,7 @@ gvc_applet_dispose (GObject *object)
         GvcApplet *applet = GVC_APPLET (object);
 
         if (applet->priv->input != NULL) {
-                g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input),
-                                                      applet);
+                g_signal_handlers_disconnect_by_data (G_OBJECT (applet->priv->input), applet);
                 g_clear_object (&applet->priv->input);
         }
 
@@ -341,16 +330,15 @@ gvc_applet_init (GvcApplet *applet)
 {
         applet->priv = GVC_APPLET_GET_PRIVATE (applet);
 
-        applet->priv->icon_input  = gvc_stream_status_icon_new (NULL, icon_names_input);
-        applet->priv->icon_output = gvc_stream_status_icon_new (NULL, icon_names_output);
+        applet->priv->icon_input  = gvc_stream_applet_icon_new (NULL, icon_names_input);
+        applet->priv->icon_output = gvc_stream_applet_icon_new (NULL, icon_names_output);
 
-        gvc_stream_status_icon_set_display_name (applet->priv->icon_input,  _("Input"));
-        gvc_stream_status_icon_set_display_name (applet->priv->icon_output, _("Output"));
+        gvc_stream_applet_icon_set_display_name (applet->priv->icon_input,  _("Input"));
+        gvc_stream_applet_icon_set_display_name (applet->priv->icon_output, _("Output"));
 
         applet->priv->context = mate_mixer_context_new ();
 
-        mate_mixer_context_set_app_name (applet->priv->context,
-                                         _("MATE Volume Control Applet"));
+        mate_mixer_context_set_app_name (applet->priv->context, _("MATE Volume Control Applet"));
 
         mate_mixer_context_set_app_id (applet->priv->context, GVC_APPLET_DBUS_NAME);
         mate_mixer_context_set_app_version (applet->priv->context, VERSION);
@@ -390,11 +378,9 @@ gvc_applet_set_size(GtkWidget* widget, int size, gpointer user_data)
                 size = 24;
         else if (size < 48)
                 size = 32;
-        else
-                size = 48;
 
-        gvc_stream_status_icon_set_size (applet->priv->icon_input, size);
-        gvc_stream_status_icon_set_size (applet->priv->icon_output, size);
+        gvc_stream_applet_icon_set_size (applet->priv->icon_input, size);
+        gvc_stream_applet_icon_set_size (applet->priv->icon_output, size);
 }
 
 static void
@@ -404,7 +390,7 @@ gvc_applet_set_mute (GtkWidget* widget, int size, gpointer user_data)
         gboolean is_muted;
         GtkAction *action;
 
-        is_muted = gvc_stream_status_icon_get_mute (applet->priv->icon_output);
+        is_muted = gvc_stream_applet_icon_get_mute (applet->priv->icon_output);
 
         action = gtk_action_group_get_action (applet->priv->action_group, "MuteOutput");
 
@@ -424,8 +410,8 @@ gvc_applet_set_orient(GtkWidget *widget, MatePanelAppletOrient orient, gpointer
 {
         GvcApplet *applet = user_data;
 
-        gvc_stream_status_icon_set_orient (applet->priv->icon_input, orient);
-        gvc_stream_status_icon_set_orient (applet->priv->icon_output, orient);
+        gvc_stream_applet_icon_set_orient (applet->priv->icon_input, orient);
+        gvc_stream_applet_icon_set_orient (applet->priv->icon_output, orient);
 }
 
 static void
@@ -433,15 +419,15 @@ menu_output_mute (GtkAction *action, GvcApplet *applet)
 {
         gboolean               is_muted;
 
-        is_muted = gvc_stream_status_icon_get_mute(applet->priv->icon_output);
+        is_muted = gvc_stream_applet_icon_get_mute(applet->priv->icon_output);
         if (!is_muted) {
-                gvc_stream_status_icon_set_mute (applet->priv->icon_output, TRUE);
+                gvc_stream_applet_icon_set_mute (applet->priv->icon_output, TRUE);
                 gtk_action_set_label (action, "Unmute Output");
                 gtk_action_set_icon_name( action, "audio-volume-medium");
 
         }
         else {
-                gvc_stream_status_icon_set_mute (applet->priv->icon_output, FALSE);
+                gvc_stream_applet_icon_set_mute (applet->priv->icon_output, FALSE);
                 gtk_action_set_label (action, "Mute Output");
                 gtk_action_set_icon_name (action, "audio-volume-muted");
        }
@@ -450,7 +436,7 @@ menu_output_mute (GtkAction *action, GvcApplet *applet)
 static void
 menu_activate_open_volume_control (GtkAction *action, GvcApplet *applet)
 {
-        gvc_stream_status_icon_volume_control (applet->priv->icon_output);
+        gvc_stream_applet_icon_volume_control (applet->priv->icon_output);
 }
 
 gboolean
@@ -469,11 +455,10 @@ gvc_applet_fill (GvcApplet *applet, MatePanelApplet* applet_widget)
         applet->priv->box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0));
 
         /* Define an initial size and orientation */
-        gvc_stream_status_icon_set_size (applet->priv->icon_input, mate_panel_applet_get_size (applet->priv->applet));
-        gvc_stream_status_icon_set_size (applet->priv->icon_output, mate_panel_applet_get_size (applet->priv->applet));
-        gvc_stream_status_icon_set_orient (applet->priv->icon_input, mate_panel_applet_get_orient (applet->priv->applet));
-        gvc_stream_status_icon_set_orient (applet->priv->icon_output, mate_panel_applet_get_orient (applet->priv->applet));
-
+        gvc_stream_applet_icon_set_size (applet->priv->icon_input, mate_panel_applet_get_size (applet->priv->applet));
+        gvc_stream_applet_icon_set_size (applet->priv->icon_output, mate_panel_applet_get_size (applet->priv->applet));
+        gvc_stream_applet_icon_set_orient (applet->priv->icon_input, mate_panel_applet_get_orient (applet->priv->applet));
+        gvc_stream_applet_icon_set_orient (applet->priv->icon_output, mate_panel_applet_get_orient (applet->priv->applet));
 
         /* we add the Gtk buttons into the applet */
         gtk_box_pack_start (applet->priv->box, GTK_WIDGET (applet->priv->icon_input), TRUE, TRUE, 2);
diff --git a/mate-volume-control/gvc-applet.h b/mate-volume-control/gvc-applet.h
index 42e9261..608e1a2 100644
--- a/mate-volume-control/gvc-applet.h
+++ b/mate-volume-control/gvc-applet.h
@@ -2,6 +2,7 @@
  *
  * Copyright (C) 2008 Red Hat, Inc.
  * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
+ * Copyright (C) 2019 Victor Kareh <vkareh@vkareh.net>
  *
  * 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
diff --git a/mate-volume-control/gvc-status-icon.c b/mate-volume-control/gvc-status-icon.c
new file mode 100644
index 0000000..0363888
--- /dev/null
+++ b/mate-volume-control/gvc-status-icon.c
@@ -0,0 +1,365 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <libmatemixer/matemixer.h>
+
+#include "gvc-status-icon.h"
+#include "gvc-stream-status-icon.h"
+
+#define GVC_STATUS_ICON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_STATUS_ICON, GvcStatusIconPrivate))
+
+static const gchar *icon_names_output[] = {
+        "audio-volume-muted",
+        "audio-volume-low",
+        "audio-volume-medium",
+        "audio-volume-high",
+        NULL
+};
+
+static const gchar *icon_names_input[] = {
+        "audio-input-microphone-muted",
+        "audio-input-microphone-low",
+        "audio-input-microphone-medium",
+        "audio-input-microphone-high",
+        NULL
+};
+
+struct _GvcStatusIconPrivate
+{
+        GvcStreamStatusIcon *icon_input;
+        GvcStreamStatusIcon *icon_output;
+        gboolean             running;
+        MateMixerContext    *context;
+        MateMixerStream     *input;
+};
+
+static void gvc_status_icon_class_init (GvcStatusIconClass *klass);
+static void gvc_status_icon_init       (GvcStatusIcon      *status_icon);
+
+G_DEFINE_TYPE (GvcStatusIcon, gvc_status_icon, G_TYPE_OBJECT)
+
+static void
+update_icon_input (GvcStatusIcon *status_icon)
+{
+        MateMixerStreamControl *control = NULL;
+        gboolean                show = FALSE;
+
+        /* Enable the input icon in case there is an input stream present and there
+         * is a non-mixer application using the input */
+        if (status_icon->priv->input != NULL) {
+                const gchar *app_id;
+                const GList *inputs =
+                        mate_mixer_stream_list_controls (status_icon->priv->input);
+
+                control = mate_mixer_stream_get_default_control (status_icon->priv->input);
+
+                const gchar *stream_name =
+                        mate_mixer_stream_get_name (status_icon->priv->input);
+                g_debug ("Got stream name %s", stream_name);
+                if (g_str_has_suffix (stream_name, ".monitor")) {
+                        inputs = NULL;
+                        g_debug ("Stream is a monitor, ignoring");
+                }
+
+                while (inputs != NULL) {
+                        MateMixerStreamControl    *input =
+                                MATE_MIXER_STREAM_CONTROL (inputs->data);
+                        MateMixerStreamControlRole role =
+                                mate_mixer_stream_control_get_role (input);
+
+                        if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) {
+                                MateMixerAppInfo *app_info =
+                                        mate_mixer_stream_control_get_app_info (input);
+
+                                app_id = mate_mixer_app_info_get_id (app_info);
+                                if (app_id == NULL) {
+                                        /* A recording application which has no
+                                         * identifier set */
+                                        g_debug ("Found a recording application control %s",
+                                                 mate_mixer_stream_control_get_label (input));
+
+                                        if G_UNLIKELY (control == NULL) {
+                                                /* In the unlikely case when there is no
+                                                 * default input control, use the application
+                                                 * control for the icon */
+                                                control = input;
+                                        }
+                                        show = TRUE;
+                                        break;
+                                }
+
+                                if (strcmp (app_id, "org.mate.VolumeControl") != 0 &&
+                                    strcmp (app_id, "org.gnome.VolumeControl") != 0 &&
+                                    strcmp (app_id, "org.PulseAudio.pavucontrol") != 0) {
+                                        g_debug ("Found a recording application %s", app_id);
+
+                                        if G_UNLIKELY (control == NULL)
+                                                control = input;
+
+                                        show = TRUE;
+                                        break;
+                                }
+                        }
+                        inputs = inputs->next;
+                }
+
+                if (show == TRUE)
+                        g_debug ("Input icon enabled");
+                else
+                        g_debug ("There is no recording application, input icon disabled");
+        }
+
+        gvc_stream_status_icon_set_control (status_icon->priv->icon_input, control);
+
+        gtk_status_icon_set_visible (GTK_STATUS_ICON (status_icon->priv->icon_input), show);
+}
+
+static void
+update_icon_output (GvcStatusIcon *status_icon)
+{
+        MateMixerStream        *stream;
+        MateMixerStreamControl *control = NULL;
+
+        stream = mate_mixer_context_get_default_output_stream (status_icon->priv->context);
+        if (stream != NULL)
+                control = mate_mixer_stream_get_default_control (stream);
+
+        gvc_stream_status_icon_set_control (status_icon->priv->icon_output, control);
+
+        if (control != NULL) {
+                g_debug ("Output icon enabled");
+                gtk_status_icon_set_visible (GTK_STATUS_ICON (status_icon->priv->icon_output),
+                                             TRUE);
+        }
+        else {
+                g_debug ("There is no output stream/control, output icon disabled");
+                gtk_status_icon_set_visible (GTK_STATUS_ICON (status_icon->priv->icon_output),
+                                             FALSE);
+        }
+}
+
+static void
+on_input_stream_control_added (MateMixerStream *stream,
+                               const gchar     *name,
+                               GvcStatusIcon       *status_icon)
+{
+        MateMixerStreamControl *control;
+
+        control = mate_mixer_stream_get_control (stream, name);
+        if G_LIKELY (control != NULL) {
+                MateMixerStreamControlRole role =
+                        mate_mixer_stream_control_get_role (control);
+
+                /* Non-application input control doesn't affect the icon */
+                if (role != MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION)
+                        return;
+        }
+
+        /* Either an application control has been added or we couldn't
+         * read the control, this shouldn't happen but let's revalidate the
+         * icon to be sure if it does */
+        update_icon_input (status_icon);
+}
+
+static void
+on_input_stream_control_removed (MateMixerStream *stream,
+                                 const gchar     *name,
+                                 GvcStatusIcon       *status_icon)
+{
+        /* The removed stream could be an application input, which may cause
+         * the input status icon to disappear */
+        update_icon_input (status_icon);
+}
+
+static gboolean
+update_default_input_stream (GvcStatusIcon *status_icon)
+{
+        MateMixerStream *stream;
+
+        stream = mate_mixer_context_get_default_input_stream (status_icon->priv->context);
+        if (stream == status_icon->priv->input)
+                return FALSE;
+
+        /* The input stream has changed */
+        if (status_icon->priv->input != NULL) {
+                g_signal_handlers_disconnect_by_data (G_OBJECT (status_icon->priv->input),
+                                                      status_icon);
+                g_object_unref (status_icon->priv->input);
+        }
+
+        status_icon->priv->input = (stream == NULL) ? NULL : g_object_ref (stream);
+        if (status_icon->priv->input != NULL) {
+                g_signal_connect (G_OBJECT (status_icon->priv->input),
+                                  "control-added",
+                                  G_CALLBACK (on_input_stream_control_added),
+                                  status_icon);
+                g_signal_connect (G_OBJECT (status_icon->priv->input),
+                                  "control-removed",
+                                  G_CALLBACK (on_input_stream_control_removed),
+                                  status_icon);
+        }
+
+        /* Return TRUE if the default input stream has changed */
+        return TRUE;
+}
+
+static void
+on_context_state_notify (MateMixerContext *context,
+                         GParamSpec       *pspec,
+                         GvcStatusIcon        *status_icon)
+{
+        MateMixerState state = mate_mixer_context_get_state (context);
+
+        switch (state) {
+        case MATE_MIXER_STATE_FAILED:
+                g_warning ("Failed to connect to a sound system");
+                break;
+
+        case MATE_MIXER_STATE_READY:
+                update_default_input_stream (status_icon);
+
+                /* Each status change may affect the visibility of the icons */
+                update_icon_output (status_icon);
+                update_icon_input (status_icon);
+                break;
+        default:
+                break;
+        }
+}
+
+static void
+on_context_default_input_stream_notify (MateMixerContext *context,
+                                        GParamSpec       *pspec,
+                                        GvcStatusIcon        *status_icon)
+{
+        if (update_default_input_stream (status_icon) == FALSE)
+                return;
+
+        update_icon_input (status_icon);
+}
+
+static void
+on_context_default_output_stream_notify (MateMixerContext *control,
+                                         GParamSpec       *pspec,
+                                         GvcStatusIcon        *status_icon)
+{
+        update_icon_output (status_icon);
+}
+
+void
+gvc_status_icon_start (GvcStatusIcon *status_icon)
+{
+        g_return_if_fail (GVC_IS_STATUS_ICON (status_icon));
+
+        if G_UNLIKELY (status_icon->priv->running == TRUE)
+                return;
+
+        if G_UNLIKELY (mate_mixer_context_open (status_icon->priv->context) == FALSE) {
+                /* Normally this should never happen, in the worst case we
+                 * should end up with the Null module */
+                g_warning ("Failed to connect to a sound system");
+        }
+
+        g_debug ("StatusIcon has been started");
+
+        status_icon->priv->running = TRUE;
+}
+
+static void
+gvc_status_icon_dispose (GObject *object)
+{
+        GvcStatusIcon *status_icon = GVC_STATUS_ICON (object);
+
+        if (status_icon->priv->input != NULL) {
+                g_signal_handlers_disconnect_by_data (G_OBJECT (status_icon->priv->input),
+                                                      status_icon);
+                g_clear_object (&status_icon->priv->input);
+        }
+
+        g_clear_object (&status_icon->priv->context);
+        g_clear_object (&status_icon->priv->icon_input);
+        g_clear_object (&status_icon->priv->icon_output);
+
+        G_OBJECT_CLASS (gvc_status_icon_parent_class)->dispose (object);
+}
+
+static void
+gvc_status_icon_class_init (GvcStatusIconClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->dispose = gvc_status_icon_dispose;
+
+        g_type_class_add_private (klass, sizeof (GvcStatusIconPrivate));
+}
+
+static void
+gvc_status_icon_init (GvcStatusIcon *status_icon)
+{
+        status_icon->priv = GVC_STATUS_ICON_GET_PRIVATE (status_icon);
+
+        status_icon->priv->icon_input  = gvc_stream_status_icon_new (NULL, icon_names_input);
+        status_icon->priv->icon_output = gvc_stream_status_icon_new (NULL, icon_names_output);
+
+        gvc_stream_status_icon_set_display_name (status_icon->priv->icon_input,  _("Input"));
+        gvc_stream_status_icon_set_display_name (status_icon->priv->icon_output, _("Output"));
+
+        gtk_status_icon_set_title (GTK_STATUS_ICON (status_icon->priv->icon_input),
+                                   _("Microphone Volume"));
+        gtk_status_icon_set_title (GTK_STATUS_ICON (status_icon->priv->icon_output),
+                                   _("Sound Output Volume"));
+
+        status_icon->priv->context = mate_mixer_context_new ();
+
+        mate_mixer_context_set_app_name (status_icon->priv->context,
+                                         _("MATE Volume Control StatusIcon"));
+
+        mate_mixer_context_set_app_id (status_icon->priv->context, GVC_STATUS_ICON_DBUS_NAME);
+        mate_mixer_context_set_app_version (status_icon->priv->context, VERSION);
+        mate_mixer_context_set_app_icon (status_icon->priv->context, "multimedia-volume-control");
+
+        g_signal_connect (G_OBJECT (status_icon->priv->context),
+                          "notify::state",
+                          G_CALLBACK (on_context_state_notify),
+                          status_icon);
+        g_signal_connect (G_OBJECT (status_icon->priv->context),
+                          "notify::default-input-stream",
+                          G_CALLBACK (on_context_default_input_stream_notify),
+                          status_icon);
+        g_signal_connect (G_OBJECT (status_icon->priv->context),
+                          "notify::default-output-stream",
+                          G_CALLBACK (on_context_default_output_stream_notify),
+                          status_icon);
+}
+
+GvcStatusIcon *
+gvc_status_icon_new (void)
+{
+        return g_object_new (GVC_TYPE_STATUS_ICON, NULL);
+}
diff --git a/mate-volume-control/gvc-status-icon.h b/mate-volume-control/gvc-status-icon.h
new file mode 100644
index 0000000..5914a1f
--- /dev/null
+++ b/mate-volume-control/gvc-status-icon.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __GVC_STATUS_ICON_H
+#define __GVC_STATUS_ICON_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GVC_STATUS_ICON_DBUS_NAME    "org.mate.VolumeControlStatusIcon"
+
+#define GVC_TYPE_STATUS_ICON         (gvc_status_icon_get_type ())
+#define GVC_STATUS_ICON(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_STATUS_ICON, GvcStatusIcon))
+#define GVC_STATUS_ICON_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GVC_TYPE_STATUS_ICON, GvcStatusIconClass))
+#define GVC_IS_STATUS_ICON(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_STATUS_ICON))
+#define GVC_IS_STATUS_ICON_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_STATUS_ICON))
+#define GVC_STATUS_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_STATUS_ICON, GvcStatusIconClass))
+
+typedef struct _GvcStatusIcon         GvcStatusIcon;
+typedef struct _GvcStatusIconClass    GvcStatusIconClass;
+typedef struct _GvcStatusIconPrivate  GvcStatusIconPrivate;
+
+struct _GvcStatusIcon
+{
+        GObject               parent;
+        GvcStatusIconPrivate *priv;
+};
+
+struct _GvcStatusIconClass
+{
+        GObjectClass          parent_class;
+};
+
+GType               gvc_status_icon_get_type            (void) G_GNUC_CONST;
+
+GvcStatusIcon *     gvc_status_icon_new                 (void);
+void                gvc_status_icon_start               (GvcStatusIcon *status_icon);
+
+G_END_DECLS
+
+#endif /* __GVC_STATUS_ICON_H */
diff --git a/mate-volume-control/gvc-stream-applet-icon.c b/mate-volume-control/gvc-stream-applet-icon.c
new file mode 100644
index 0000000..8a63799
--- /dev/null
+++ b/mate-volume-control/gvc-stream-applet-icon.c
@@ -0,0 +1,775 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
+ * Copyright (C) 2019 Victor Kareh <vkareh@vkareh.net>
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <libmatemixer/matemixer.h>
+#include <mate-panel-applet.h>
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+#include <libmate-desktop/mate-desktop-utils.h>
+
+#include "gvc-channel-bar.h"
+#include "gvc-stream-applet-icon.h"
+
+#define GVC_STREAM_APPLET_ICON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_STREAM_APPLET_ICON, GvcStreamAppletIconPrivate))
+
+struct _GvcStreamAppletIconPrivate
+{
+        gchar                 **icon_names;
+        GtkImage               *image;
+        GtkWidget              *dock;
+        GtkWidget              *bar;
+        guint                   current_icon;
+        gchar                  *display_name;
+        MateMixerStreamControl *control;
+        MatePanelAppletOrient   orient;
+        guint                   size;
+};
+
+enum
+{
+        PROP_0,
+        PROP_CONTROL,
+        PROP_DISPLAY_NAME,
+        PROP_ICON_NAMES,
+        N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void gvc_stream_applet_icon_class_init (GvcStreamAppletIconClass *klass);
+static void gvc_stream_applet_icon_init       (GvcStreamAppletIcon      *stream_applet_icon);
+static void gvc_stream_applet_icon_finalize   (GObject                  *object);
+
+G_DEFINE_TYPE (GvcStreamAppletIcon, gvc_stream_applet_icon, GTK_TYPE_EVENT_BOX)
+
+static gboolean
+popup_dock (GvcStreamAppletIcon *icon, guint time)
+{
+        GtkAllocation  allocation;
+        GdkDisplay    *display;
+        GdkScreen     *screen;
+        int            x, y;
+        GdkMonitor    *monitor_num;
+        GdkRectangle   monitor;
+        GtkRequisition dock_req;
+
+        screen = gtk_widget_get_screen (GTK_WIDGET (icon));
+        gtk_widget_get_allocation (GTK_WIDGET (icon), &allocation);
+        gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (icon)), &allocation.x, &allocation.y);
+
+        /* position roughly */
+        gtk_window_set_screen (GTK_WINDOW (icon->priv->dock), screen);
+        gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar), icon->priv->orient);
+
+        monitor_num = gdk_display_get_monitor_at_point (gdk_screen_get_display (screen), allocation.x, allocation.y);
+        gdk_monitor_get_geometry (monitor_num, &monitor);
+
+        gtk_container_foreach (GTK_CONTAINER (icon->priv->dock), (GtkCallback) gtk_widget_show_all, NULL);
+        gtk_widget_get_preferred_size (icon->priv->dock, &dock_req, NULL);
+
+        if (icon->priv->orient == MATE_PANEL_APPLET_ORIENT_LEFT || icon->priv->orient == MATE_PANEL_APPLET_ORIENT_RIGHT) {
+                if (allocation.x + allocation.width + dock_req.width <= monitor.x + monitor.width)
+                        x = allocation.x + allocation.width;
+                else
+                        x = allocation.x - dock_req.width;
+
+                if (allocation.y + dock_req.height <= monitor.y + monitor.height)
+                        y = allocation.y;
+                else
+                        y = monitor.y + monitor.height - dock_req.height;
+        } else {
+                if (allocation.y + allocation.height + dock_req.height <= monitor.y + monitor.height)
+                        y = allocation.y + allocation.height;
+                else
+                        y = allocation.y - dock_req.height;
+
+                if (allocation.x + dock_req.width <= monitor.x + monitor.width)
+                        x = allocation.x;
+                else
+                        x = monitor.x + monitor.width - dock_req.width;
+        }
+
+        gtk_window_move (GTK_WINDOW (icon->priv->dock), x, y);
+
+        /* Without this, the popup window appears as a square after changing
+         * the orientation */
+        gtk_window_resize (GTK_WINDOW (icon->priv->dock), 1, 1);
+
+        gtk_widget_show_all (icon->priv->dock);
+
+        /* Grab focus */
+        gtk_grab_add (icon->priv->dock);
+
+        display = gtk_widget_get_display (icon->priv->dock);
+
+        do {
+                GdkSeat *seat = gdk_display_get_default_seat (display);
+                GdkWindow *window = gtk_widget_get_window (icon->priv->dock);
+
+                if (gdk_seat_grab (seat,
+                                   window,
+                                   GDK_SEAT_CAPABILITY_ALL,
+                                   TRUE,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   NULL) != GDK_GRAB_SUCCESS) {
+                        gtk_grab_remove (icon->priv->dock);
+                        gtk_widget_hide (icon->priv->dock);
+                        break;
+                }
+        } while (0);
+
+        gtk_widget_grab_focus (icon->priv->dock);
+
+        return TRUE;
+}
+
+static gboolean
+on_applet_icon_button_press (GtkWidget           *applet_icon,
+                             GdkEventButton      *event,
+                             GvcStreamAppletIcon *icon)
+{
+        if (event->button == 1) {
+                popup_dock (icon, GDK_CURRENT_TIME);
+                return TRUE;
+        }
+
+        /* Middle click acts as mute/unmute */
+        if (event->button == 2) {
+                gboolean is_muted = mate_mixer_stream_control_get_mute (icon->priv->control);
+
+                mate_mixer_stream_control_set_mute (icon->priv->control, !is_muted);
+                return TRUE;
+        }
+        return FALSE;
+}
+
+void
+gvc_stream_applet_icon_set_mute (GvcStreamAppletIcon *icon, gboolean mute)
+{
+        mate_mixer_stream_control_set_mute (icon->priv->control, mute);
+}
+
+gboolean
+gvc_stream_applet_icon_get_mute (GvcStreamAppletIcon *icon)
+{
+        return mate_mixer_stream_control_get_mute (icon->priv->control);
+}
+
+void
+gvc_stream_applet_icon_volume_control (GvcStreamAppletIcon *icon)
+{
+        GError *error = NULL;
+
+        mate_gdk_spawn_command_line_on_screen (gtk_widget_get_screen (icon->priv->dock),
+                                               "mate-volume-control",
+                                               &error);
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+
+                dialog = gtk_message_dialog_new (NULL,
+                                                 0,
+                                                 GTK_MESSAGE_ERROR,
+                                                 GTK_BUTTONS_CLOSE,
+                                                 _("Failed to start Sound Preferences: %s"),
+                                                 error->message);
+                g_signal_connect (G_OBJECT (dialog),
+                                  "response",
+                                  G_CALLBACK (gtk_widget_destroy),
+                                  NULL);
+                gtk_widget_show (dialog);
+                g_error_free (error);
+        }
+}
+
+static gboolean
+on_applet_icon_scroll_event (GtkWidget           *event_box,
+                             GdkEventScroll      *event,
+                             GvcStreamAppletIcon *icon)
+{
+        return gvc_channel_bar_scroll (GVC_CHANNEL_BAR (icon->priv->bar), event->direction);
+}
+
+static void
+gvc_icon_release_grab (GvcStreamAppletIcon *icon, GdkEventButton *event)
+{
+        GdkDisplay *display = gtk_widget_get_display (icon->priv->dock);
+        GdkSeat *seat = gdk_display_get_default_seat (display);
+        gdk_seat_ungrab (seat);
+        gtk_grab_remove (icon->priv->dock);
+
+        /* Hide again */
+        gtk_widget_hide (icon->priv->dock);
+}
+
+static gboolean
+on_dock_button_press (GtkWidget           *widget,
+                      GdkEventButton      *event,
+                      GvcStreamAppletIcon *icon)
+{
+        if (event->type == GDK_BUTTON_PRESS) {
+                gvc_icon_release_grab (icon, event);
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static void
+popdown_dock (GvcStreamAppletIcon *icon)
+{
+        GdkDisplay *display;
+
+        display = gtk_widget_get_display (icon->priv->dock);
+
+        GdkSeat *seat = gdk_display_get_default_seat (display);
+        gdk_seat_ungrab (seat);
+
+        /* Hide again */
+        gtk_widget_hide (icon->priv->dock);
+}
+
+/* This is called when the grab is broken for either the dock, or the scale */
+static void
+gvc_icon_grab_notify (GvcStreamAppletIcon *icon, gboolean was_grabbed)
+{
+        if (was_grabbed != FALSE)
+                return;
+
+        if (gtk_widget_has_grab (icon->priv->dock) == FALSE)
+                return;
+
+        if (gtk_widget_is_ancestor (gtk_grab_get_current (), icon->priv->dock))
+                return;
+
+        popdown_dock (icon);
+}
+
+static void
+on_dock_grab_notify (GtkWidget           *widget,
+                     gboolean             was_grabbed,
+                     GvcStreamAppletIcon *icon)
+{
+        gvc_icon_grab_notify (icon, was_grabbed);
+}
+
+static gboolean
+on_dock_grab_broken_event (GtkWidget           *widget,
+                           gboolean             was_grabbed,
+                           GvcStreamAppletIcon *icon)
+{
+        gvc_icon_grab_notify (icon, FALSE);
+        return FALSE;
+}
+
+static gboolean
+on_dock_key_release (GtkWidget           *widget,
+                     GdkEventKey         *event,
+                     GvcStreamAppletIcon *icon)
+{
+        if (event->keyval == GDK_KEY_Escape) {
+                popdown_dock (icon);
+                return TRUE;
+        }
+        return TRUE;
+}
+
+static gboolean
+on_dock_scroll_event (GtkWidget           *widget,
+                      GdkEventScroll      *event,
+                      GvcStreamAppletIcon *icon)
+{
+        /* Forward event to the applet icon */
+        on_applet_icon_scroll_event (NULL, event, icon);
+        return TRUE;
+}
+
+static void
+gvc_stream_applet_icon_set_icon_from_name (GvcStreamAppletIcon *icon,
+                                           const gchar *icon_name)
+{
+        GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
+        gint icon_scale = gtk_widget_get_scale_factor (GTK_WIDGET (icon));
+
+        cairo_surface_t* surface = gtk_icon_theme_load_surface (icon_theme, icon_name,
+                                                                icon->priv->size,
+                                                                icon_scale, NULL,
+                                                                GTK_ICON_LOOKUP_FORCE_SIZE,
+                                                                NULL);
+
+        gtk_image_set_from_surface (GTK_IMAGE (icon->priv->image), surface);
+        cairo_surface_destroy (surface);
+}
+
+static void
+update_icon (GvcStreamAppletIcon *icon)
+{
+        guint                       volume = 0;
+        gdouble                     decibel = 0;
+        guint                       normal = 0;
+        gboolean                    muted = FALSE;
+        guint                       n = 0;
+        gchar                      *markup;
+        const gchar                *description;
+        MateMixerStreamControlFlags flags;
+
+        if (icon->priv->control == NULL) {
+                /* Do not bother creating a tooltip for an unusable icon as it
+                 * has no practical use */
+                gtk_widget_set_has_tooltip (GTK_WIDGET (icon), FALSE);
+                return;
+        } else
+                gtk_widget_set_has_tooltip (GTK_WIDGET (icon), TRUE);
+
+        flags = mate_mixer_stream_control_get_flags (icon->priv->control);
+
+        if (flags & MATE_MIXER_STREAM_CONTROL_MUTE_READABLE)
+                muted = mate_mixer_stream_control_get_mute (icon->priv->control);
+
+        if (flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) {
+                volume = mate_mixer_stream_control_get_volume (icon->priv->control);
+                normal = mate_mixer_stream_control_get_normal_volume (icon->priv->control);
+
+                /* Select an icon, they are expected to be sorted, the lowest index being
+                 * the mute icon and the rest being volume increments */
+                if (volume <= 0 || muted)
+                        n = 0;
+                else
+                        n = CLAMP (3 * volume / normal + 1, 1, 3);
+        }
+        if (flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL)
+                decibel = mate_mixer_stream_control_get_decibel (icon->priv->control);
+
+        /* Apparently applet icon will reset icon even if it doesn't change */
+        if (icon->priv->current_icon != n) {
+                gvc_stream_applet_icon_set_icon_from_name (icon, icon->priv->icon_names[n]);
+                icon->priv->current_icon = n;
+        }
+
+        description = mate_mixer_stream_control_get_label (icon->priv->control);
+
+        guint volume_percent = (guint) round (100.0 * volume / normal);
+        if (muted) {
+                markup = g_strdup_printf ("<b>%s: %s %u%%</b>\n<small>%s</small>",
+                                          icon->priv->display_name,
+                                          _("Muted at"),
+                                          volume_percent,
+                                          description);
+        } else if (flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) {
+                if (flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) {
+                        if (decibel > -MATE_MIXER_INFINITY) {
+                                markup = g_strdup_printf ("<b>%s: %u%%</b>\n"
+                                                          "<small>%0.2f dB\n%s</small>",
+                                                          icon->priv->display_name,
+                                                          volume_percent,
+                                                          decibel,
+                                                          description);
+                        } else {
+                                markup = g_strdup_printf ("<b>%s: %u%%</b>\n"
+                                                          "<small>-&#8734; dB\n%s</small>",
+                                                          icon->priv->display_name,
+                                                          volume_percent,
+                                                          description);
+                        }
+                } else {
+                        markup = g_strdup_printf ("<b>%s: %u%%</b>\n<small>%s</small>",
+                                                  icon->priv->display_name,
+                                                  volume_percent,
+                                                  description);
+                }
+        } else {
+                markup = g_strdup_printf ("<b>%s</b>\n<small>%s</small>",
+                                          icon->priv->display_name,
+                                          description);
+        }
+
+        gtk_widget_set_tooltip_markup (GTK_WIDGET (icon), markup);
+
+        g_free (markup);
+}
+
+void
+gvc_stream_applet_icon_set_size (GvcStreamAppletIcon *icon,
+                                 guint                size)
+{
+
+        /*Iterate through the icon sizes so they can be kept sharp*/
+        if (size < 22)
+                size = 16;
+        else if (size < 24)
+                size = 22;
+        else if (size < 32)
+                size = 24;
+        else if (size < 48)
+                size = 32;
+
+        icon->priv->size = size;
+        gvc_stream_applet_icon_set_icon_from_name (icon, icon->priv->icon_names[icon->priv->current_icon]);
+}
+
+void
+gvc_stream_applet_icon_set_orient (GvcStreamAppletIcon  *icon,
+                                   MatePanelAppletOrient orient)
+{
+        /* Sometimes orient does not get properly defined especially on a bottom panel.
+         * Use the applet orientation if it is valid, otherwise set a vertical slider,
+         * otherwise bottom panels get a horizontal slider.
+         */
+        if (orient)
+                icon->priv->orient = orient;
+        else
+                icon->priv->orient = MATE_PANEL_APPLET_ORIENT_DOWN;
+}
+
+void
+gvc_stream_applet_icon_set_icon_names (GvcStreamAppletIcon  *icon,
+                                       const gchar         **names)
+{
+        g_return_if_fail (GVC_IS_STREAM_APPLET_ICON (icon));
+        g_return_if_fail (names != NULL && *names != NULL);
+
+        if (G_UNLIKELY (g_strv_length ((gchar **) names) != 4)) {
+                g_warn_if_reached ();
+                return;
+        }
+
+        g_strfreev (icon->priv->icon_names);
+
+        icon->priv->icon_names = g_strdupv ((gchar **) names);
+
+        /* Set the first icon as the initial one, the icon may be immediately
+         * updated or not depending on whether a stream is available */
+        gvc_stream_applet_icon_set_icon_from_name (icon, names[0]);
+        update_icon (icon);
+
+        g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_ICON_NAMES]);
+}
+
+static void
+on_stream_control_volume_notify (MateMixerStreamControl *control,
+                                 GParamSpec             *pspec,
+                                 GvcStreamAppletIcon    *icon)
+{
+        update_icon (icon);
+}
+
+static void
+on_stream_control_mute_notify (MateMixerStreamControl *control,
+                               GParamSpec             *pspec,
+                               GvcStreamAppletIcon    *icon)
+{
+        update_icon (icon);
+}
+
+void
+gvc_stream_applet_icon_set_display_name (GvcStreamAppletIcon *icon,
+                                         const gchar         *name)
+{
+        g_return_if_fail (GVC_STREAM_APPLET_ICON (icon));
+
+        g_free (icon->priv->display_name);
+
+        icon->priv->display_name = g_strdup (name);
+        update_icon (icon);
+
+        g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_DISPLAY_NAME]);
+}
+
+void
+gvc_stream_applet_icon_set_control (GvcStreamAppletIcon    *icon,
+                                    MateMixerStreamControl *control)
+{
+        g_return_if_fail (GVC_STREAM_APPLET_ICON (icon));
+
+        if (icon->priv->control == control)
+                return;
+
+        if (control != NULL)
+                g_object_ref (control);
+
+        if (icon->priv->control != NULL) {
+                g_signal_handlers_disconnect_by_func (G_OBJECT (icon->priv->control),
+                                                      G_CALLBACK (on_stream_control_volume_notify),
+                                                      icon);
+                g_signal_handlers_disconnect_by_func (G_OBJECT (icon->priv->control),
+                                                      G_CALLBACK (on_stream_control_mute_notify),
+                                                      icon);
+
+                g_object_unref (icon->priv->control);
+        }
+
+        icon->priv->control = control;
+
+        if (icon->priv->control != NULL) {
+                g_signal_connect (G_OBJECT (icon->priv->control),
+                                  "notify::volume",
+                                  G_CALLBACK (on_stream_control_volume_notify),
+                                  icon);
+                g_signal_connect (G_OBJECT (icon->priv->control),
+                                  "notify::mute",
+                                  G_CALLBACK (on_stream_control_mute_notify),
+                                  icon);
+
+                // XXX when no stream set some default icon and "unset" dock
+                update_icon (icon);
+        }
+
+        gvc_channel_bar_set_control (GVC_CHANNEL_BAR (icon->priv->bar), icon->priv->control);
+
+        g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_CONTROL]);
+}
+
+static void
+gvc_stream_applet_icon_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+        GvcStreamAppletIcon *self = GVC_STREAM_APPLET_ICON (object);
+
+        switch (prop_id) {
+        case PROP_CONTROL:
+                gvc_stream_applet_icon_set_control (self, g_value_get_object (value));
+                break;
+        case PROP_DISPLAY_NAME:
+                gvc_stream_applet_icon_set_display_name (self, g_value_get_string (value));
+                break;
+        case PROP_ICON_NAMES:
+                gvc_stream_applet_icon_set_icon_names (self, g_value_get_boxed (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gvc_stream_applet_icon_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+        GvcStreamAppletIcon *self = GVC_STREAM_APPLET_ICON (object);
+
+        switch (prop_id) {
+        case PROP_CONTROL:
+                g_value_set_object (value, self->priv->control);
+                break;
+        case PROP_DISPLAY_NAME:
+                g_value_set_string (value, self->priv->display_name);
+                break;
+        case PROP_ICON_NAMES:
+                g_value_set_boxed (value, self->priv->icon_names);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gvc_stream_applet_icon_dispose (GObject *object)
+{
+        GvcStreamAppletIcon *icon = GVC_STREAM_APPLET_ICON (object);
+
+        if (icon->priv->dock != NULL) {
+                gtk_widget_destroy (icon->priv->dock);
+                icon->priv->dock = NULL;
+        }
+
+        g_clear_object (&icon->priv->control);
+
+        G_OBJECT_CLASS (gvc_stream_applet_icon_parent_class)->dispose (object);
+}
+
+static void
+gvc_stream_applet_icon_class_init (GvcStreamAppletIconClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize     = gvc_stream_applet_icon_finalize;
+        object_class->dispose      = gvc_stream_applet_icon_dispose;
+        object_class->set_property = gvc_stream_applet_icon_set_property;
+        object_class->get_property = gvc_stream_applet_icon_get_property;
+
+        properties[PROP_CONTROL] =
+                g_param_spec_object ("control",
+                                     "Control",
+                                     "MateMixer stream control",
+                                     MATE_MIXER_TYPE_STREAM_CONTROL,
+                                     G_PARAM_READWRITE |
+                                     G_PARAM_CONSTRUCT |
+                                     G_PARAM_STATIC_STRINGS);
+
+        properties[PROP_DISPLAY_NAME] =
+                g_param_spec_string ("display-name",
+                                     "Display name",
+                                     "Name to display for this stream",
+                                     NULL,
+                                     G_PARAM_READWRITE |
+                                     G_PARAM_CONSTRUCT |
+                                     G_PARAM_STATIC_STRINGS);
+
+        properties[PROP_ICON_NAMES] =
+                g_param_spec_boxed ("icon-names",
+                                    "Icon names",
+                                    "Name of icon to display for this stream",
+                                    G_TYPE_STRV,
+                                    G_PARAM_READWRITE |
+                                    G_PARAM_CONSTRUCT |
+                                    G_PARAM_STATIC_STRINGS);
+
+        g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+        g_type_class_add_private (klass, sizeof (GvcStreamAppletIconPrivate));
+}
+
+static void
+on_applet_icon_visible_notify (GvcStreamAppletIcon *icon)
+{
+        if (gtk_widget_get_visible (GTK_WIDGET (icon)) == FALSE)
+                gtk_widget_hide (icon->priv->dock);
+}
+
+static void
+on_icon_theme_change (GtkSettings         *settings,
+                      GParamSpec          *pspec,
+                      GvcStreamAppletIcon *icon)
+{
+        gvc_stream_applet_icon_set_icon_from_name (icon, icon->priv->icon_names[icon->priv->current_icon]);
+}
+
+static void
+gvc_stream_applet_icon_init (GvcStreamAppletIcon *icon)
+{
+        GtkWidget *frame;
+        GtkWidget *box;
+
+        icon->priv = GVC_STREAM_APPLET_ICON_GET_PRIVATE (icon);
+
+        icon->priv->image = GTK_IMAGE (gtk_image_new ());
+        gtk_container_add (GTK_CONTAINER (icon), GTK_WIDGET (icon->priv->image));
+
+        g_signal_connect (GTK_WIDGET (icon),
+                          "button-press-event",
+                          G_CALLBACK (on_applet_icon_button_press),
+                          icon);
+        g_signal_connect (GTK_WIDGET (icon),
+                          "scroll-event",
+                          G_CALLBACK (on_applet_icon_scroll_event),
+                          icon);
+        g_signal_connect (GTK_WIDGET (icon),
+                          "notify::visible",
+                          G_CALLBACK (on_applet_icon_visible_notify),
+                          NULL);
+
+        /* Create the dock window */
+        icon->priv->dock = gtk_window_new (GTK_WINDOW_POPUP);
+
+        gtk_window_set_decorated (GTK_WINDOW (icon->priv->dock), FALSE);
+
+        g_signal_connect (G_OBJECT (icon->priv->dock),
+                          "button-press-event",
+                          G_CALLBACK (on_dock_button_press),
+                          icon);
+        g_signal_connect (G_OBJECT (icon->priv->dock),
+                          "key-release-event",
+                          G_CALLBACK (on_dock_key_release),
+                          icon);
+        g_signal_connect (G_OBJECT (icon->priv->dock),
+                          "scroll-event",
+                          G_CALLBACK (on_dock_scroll_event),
+                          icon);
+        g_signal_connect (G_OBJECT (icon->priv->dock),
+                          "grab-notify",
+                          G_CALLBACK (on_dock_grab_notify),
+                          icon);
+        g_signal_connect (G_OBJECT (icon->priv->dock),
+                          "grab-broken-event",
+                          G_CALLBACK (on_dock_grab_broken_event),
+                          icon);
+
+        frame = gtk_frame_new (NULL);
+        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+        gtk_container_add (GTK_CONTAINER (icon->priv->dock), frame);
+
+        icon->priv->bar = gvc_channel_bar_new (NULL);
+
+        gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar),
+                                         GTK_ORIENTATION_VERTICAL);
+
+        /* Set volume control frame, slider and toplevel window to follow panel theme */
+        GtkWidget *toplevel = gtk_widget_get_toplevel (icon->priv->dock);
+        GtkStyleContext *context;
+        context = gtk_widget_get_style_context (GTK_WIDGET(toplevel));
+        gtk_style_context_add_class(context,"mate-panel-applet-slider");
+
+        /* Make transparency possible in gtk3 theme */
+        GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(toplevel));
+        GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
+        gtk_widget_set_visual(GTK_WIDGET(toplevel), visual);
+
+        box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+
+        gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+        gtk_container_add (GTK_CONTAINER (frame), box);
+
+        gtk_box_pack_start (GTK_BOX (box), icon->priv->bar, TRUE, FALSE, 0);
+
+        g_signal_connect (gtk_settings_get_default (),
+                          "notify::gtk-icon-theme-name",
+                          G_CALLBACK (on_icon_theme_change),
+                          icon);
+}
+
+static void
+gvc_stream_applet_icon_finalize (GObject *object)
+{
+        GvcStreamAppletIcon *icon;
+
+        icon = GVC_STREAM_APPLET_ICON (object);
+
+        g_strfreev (icon->priv->icon_names);
+
+        g_signal_handlers_disconnect_by_func (gtk_settings_get_default (),
+                                              on_icon_theme_change,
+                                              icon);
+
+        G_OBJECT_CLASS (gvc_stream_applet_icon_parent_class)->finalize (object);
+}
+
+GvcStreamAppletIcon *
+gvc_stream_applet_icon_new (MateMixerStreamControl *control,
+                            const gchar           **icon_names)
+{
+        return g_object_new (GVC_TYPE_STREAM_APPLET_ICON,
+                             "control", control,
+                             "icon-names", icon_names,
+                             NULL);
+}
diff --git a/mate-volume-control/gvc-stream-applet-icon.h b/mate-volume-control/gvc-stream-applet-icon.h
new file mode 100644
index 0000000..8ef4220
--- /dev/null
+++ b/mate-volume-control/gvc-stream-applet-icon.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
+ * Copyright (C) 2019 Victor Kareh <vkareh@vkareh.net>
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __GVC_STREAM_APPLET_ICON_H
+#define __GVC_STREAM_APPLET_ICON_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_STREAM_APPLET_ICON         (gvc_stream_applet_icon_get_type ())
+#define GVC_STREAM_APPLET_ICON(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_STREAM_APPLET_ICON, GvcStreamAppletIcon))
+#define GVC_STREAM_APPLET_ICON_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GVC_TYPE_STREAM_APPLET_ICON, GvcStreamAppletIconClass))
+#define GVC_IS_STREAM_APPLET_ICON(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_STREAM_APPLET_ICON))
+#define GVC_IS_STREAM_APPLET_ICON_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_STREAM_APPLET_ICON))
+#define GVC_STREAM_APPLET_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_STREAM_APPLET_ICON, GvcStreamAppletIconClass))
+
+typedef struct _GvcStreamAppletIcon         GvcStreamAppletIcon;
+typedef struct _GvcStreamAppletIconClass    GvcStreamAppletIconClass;
+typedef struct _GvcStreamAppletIconPrivate  GvcStreamAppletIconPrivate;
+
+struct _GvcStreamAppletIcon
+{
+        GtkEventBox                 parent;
+        GvcStreamAppletIconPrivate *priv;
+};
+
+struct _GvcStreamAppletIconClass
+{
+        GtkEventBoxClass            parent_class;
+};
+
+GType                 gvc_stream_applet_icon_get_type         (void) G_GNUC_CONST;
+
+GvcStreamAppletIcon * gvc_stream_applet_icon_new              (MateMixerStreamControl *control,
+                                                               const gchar           **icon_names);
+
+void                  gvc_stream_applet_icon_set_icon_names   (GvcStreamAppletIcon    *icon,
+                                                               const gchar           **icon_names);
+void                  gvc_stream_applet_icon_set_display_name (GvcStreamAppletIcon    *icon,
+                                                                  const gchar         *display_name);
+
+void                  gvc_stream_applet_icon_set_control      (GvcStreamAppletIcon    *icon,
+                                                               MateMixerStreamControl *control);
+
+void                  gvc_stream_applet_icon_set_size         (GvcStreamAppletIcon *icon,
+                                                               guint                size);
+
+void                  gvc_stream_applet_icon_set_orient       (GvcStreamAppletIcon  *icon,
+                                                               MatePanelAppletOrient orient);
+
+gboolean              gvc_stream_applet_icon_get_mute         (GvcStreamAppletIcon *icon);
+
+void                  gvc_stream_applet_icon_set_mute         (GvcStreamAppletIcon *icon,
+                                                               gboolean mute);
+
+void                  gvc_stream_applet_icon_volume_control   (GvcStreamAppletIcon *icon);
+
+G_END_DECLS
+
+#endif /* __GVC_STREAM_APPLET_ICON_H */
diff --git a/mate-volume-control/gvc-stream-status-icon.c b/mate-volume-control/gvc-stream-status-icon.c
index f48a370..558dd55 100644
--- a/mate-volume-control/gvc-stream-status-icon.c
+++ b/mate-volume-control/gvc-stream-status-icon.c
@@ -2,7 +2,6 @@
  *
  * Copyright (C) 2008 William Jon McCann
  * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
- * Copyright (C) 2019 Victor Kareh <vkareh@vkareh.net>
  *
  * 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
@@ -26,7 +25,6 @@
 #include <gdk/gdkkeysyms.h>
 
 #include <libmatemixer/matemixer.h>
-#include <mate-panel-applet.h>
 
 #define MATE_DESKTOP_USE_UNSTABLE_API
 #include <libmate-desktop/mate-desktop-utils.h>
@@ -38,15 +36,12 @@
 
 struct _GvcStreamStatusIconPrivate
 {
-        gchar                 **icon_names;
-        GtkImage               *image;
-        GtkWidget              *dock;
-        GtkWidget              *bar;
-        guint                   current_icon;
-        gchar                  *display_name;
+        gchar          **icon_names;
+        GtkWidget       *dock;
+        GtkWidget       *bar;
+        guint            current_icon;
+        gchar           *display_name;
         MateMixerStreamControl *control;
-        MatePanelAppletOrient   orient;
-        guint                   size;
 };
 
 enum
@@ -64,52 +59,61 @@ static void gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass);
 static void gvc_stream_status_icon_init       (GvcStreamStatusIcon      *stream_status_icon);
 static void gvc_stream_status_icon_finalize   (GObject                  *object);
 
-G_DEFINE_TYPE (GvcStreamStatusIcon, gvc_stream_status_icon, GTK_TYPE_EVENT_BOX)
+G_DEFINE_TYPE (GvcStreamStatusIcon, gvc_stream_status_icon, GTK_TYPE_STATUS_ICON)
 
 static gboolean
 popup_dock (GvcStreamStatusIcon *icon, guint time)
 {
-        GtkAllocation  allocation;
+        GdkRectangle   area;
+        GtkOrientation orientation;
         GdkDisplay    *display;
         GdkScreen     *screen;
-        int            x, y;
+        int            x;
+        int            y;
         GdkMonitor    *monitor_num;
         GdkRectangle   monitor;
         GtkRequisition dock_req;
 
-        screen = gtk_widget_get_screen (GTK_WIDGET (icon));
-        gtk_widget_get_allocation (GTK_WIDGET (icon), &allocation);
-        gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (icon)), &allocation.x, &allocation.y);
+        screen = gtk_status_icon_get_screen (GTK_STATUS_ICON (icon));
+
+        if (gtk_status_icon_get_geometry (GTK_STATUS_ICON (icon),
+                                          &screen,
+                                          &area,
+                                          &orientation) == FALSE) {
+                g_warning ("Unable to determine geometry of status icon");
+                return FALSE;
+        }
 
         /* position roughly */
         gtk_window_set_screen (GTK_WINDOW (icon->priv->dock), screen);
-        gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar), icon->priv->orient);
+        gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar),
+                                         1 - orientation);
 
-        monitor_num = gdk_display_get_monitor_at_point (gdk_screen_get_display (screen), allocation.x, allocation.y);
+        monitor_num = gdk_display_get_monitor_at_point (gdk_screen_get_display (screen), area.x, area.y);
         gdk_monitor_get_geometry (monitor_num, &monitor);
 
         gtk_container_foreach (GTK_CONTAINER (icon->priv->dock),
                                (GtkCallback) gtk_widget_show_all, NULL);
         gtk_widget_get_preferred_size (icon->priv->dock, &dock_req, NULL);
 
-        if (icon->priv->orient == MATE_PANEL_APPLET_ORIENT_LEFT || icon->priv->orient == MATE_PANEL_APPLET_ORIENT_RIGHT) {
-                if (allocation.x + allocation.width + dock_req.width <= monitor.x + monitor.width)
-                        x = allocation.x + allocation.width;
+        if (orientation == GTK_ORIENTATION_VERTICAL) {
+                if (area.x + area.width + dock_req.width <= monitor.x + monitor.width)
+                        x = area.x + area.width;
                 else
-                        x = allocation.x - dock_req.width;
+                        x = area.x - dock_req.width;
 
-                if (allocation.y + dock_req.height <= monitor.y + monitor.height)
-                        y = allocation.y;
+                if (area.y + dock_req.height <= monitor.y + monitor.height)
+                        y = area.y;
                 else
                         y = monitor.y + monitor.height - dock_req.height;
         } else {
-                if (allocation.y + allocation.height + dock_req.height <= monitor.y + monitor.height)
-                        y = allocation.y + allocation.height;
+                if (area.y + area.height + dock_req.height <= monitor.y + monitor.height)
+                        y = area.y + area.height;
                 else
-                        y = allocation.y - dock_req.height;
+                        y = area.y - dock_req.height;
 
-                if (allocation.x + dock_req.width <= monitor.x + monitor.width)
-                        x = allocation.x;
+                if (area.x + dock_req.width <= monitor.x + monitor.width)
+                        x = area.x;
                 else
                         x = monitor.x + monitor.width - dock_req.width;
         }
@@ -150,16 +154,17 @@ popup_dock (GvcStreamStatusIcon *icon, guint time)
         return TRUE;
 }
 
+static void
+on_status_icon_activate (GtkStatusIcon *status_icon, GvcStreamStatusIcon *icon)
+{
+        popup_dock (icon, GDK_CURRENT_TIME);
+}
+
 static gboolean
-on_status_icon_button_press (GtkWidget           *status_icon,
+on_status_icon_button_press (GtkStatusIcon       *status_icon,
                              GdkEventButton      *event,
                              GvcStreamStatusIcon *icon)
 {
-        if (event->button == 1) {
-                popup_dock (icon, GDK_CURRENT_TIME);
-                return TRUE;
-        }
-
         /* Middle click acts as mute/unmute */
         if (event->button == 2) {
                 gboolean is_muted = mate_mixer_stream_control_get_mute (icon->priv->control);
@@ -170,20 +175,19 @@ on_status_icon_button_press (GtkWidget           *status_icon,
         return FALSE;
 }
 
-void
-gvc_stream_status_icon_set_mute (GvcStreamStatusIcon *icon, gboolean mute)
+static void
+on_menu_mute_toggled (GtkMenuItem *item, GvcStreamStatusIcon *icon)
 {
-        mate_mixer_stream_control_set_mute (icon->priv->control, mute);
-}
+        gboolean is_muted;
 
-gboolean
-gvc_stream_status_icon_get_mute (GvcStreamStatusIcon *icon)
-{
-        return mate_mixer_stream_control_get_mute (icon->priv->control);
+        is_muted = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
+
+        mate_mixer_stream_control_set_mute (icon->priv->control, is_muted);
 }
 
-void
-gvc_stream_status_icon_volume_control (GvcStreamStatusIcon *icon)
+static void
+on_menu_activate_open_volume_control (GtkMenuItem         *item,
+                                      GvcStreamStatusIcon *icon)
 {
         GError *error = NULL;
 
@@ -209,8 +213,64 @@ gvc_stream_status_icon_volume_control (GvcStreamStatusIcon *icon)
         }
 }
 
+static void
+on_status_icon_popup_menu (GtkStatusIcon       *status_icon,
+                           guint                button,
+                           guint                activate_time,
+                           GvcStreamStatusIcon *icon)
+{
+        GtkWidget *menu;
+        GtkWidget *item;
+        GtkWidget *image;
+
+        menu = gtk_menu_new ();
+
+        /*Set up theme and transparency support*/
+        GtkWidget *toplevel = gtk_widget_get_toplevel (menu);
+        /* Fix any failures of compiz/other wm's to communicate with gtk for transparency */
+        GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(toplevel));
+        GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
+        gtk_widget_set_visual(GTK_WIDGET(toplevel), visual);
+        /* Set menu and it's toplevel window to follow panel theme */
+        GtkStyleContext *context;
+        context = gtk_widget_get_style_context (GTK_WIDGET(toplevel));
+        gtk_style_context_add_class(context,"gnome-panel-menu-bar");
+        gtk_style_context_add_class(context,"mate-panel-menu-bar");
+
+        item = gtk_check_menu_item_new_with_mnemonic (_("_Mute"));
+        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
+                                        mate_mixer_stream_control_get_mute (icon->priv->control));
+        g_signal_connect (G_OBJECT (item),
+                          "toggled",
+                          G_CALLBACK (on_menu_mute_toggled),
+                          icon);
+
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+        item = gtk_image_menu_item_new_with_mnemonic (_("_Sound Preferences"));
+        image = gtk_image_new_from_icon_name ("multimedia-volume-control",
+                                              GTK_ICON_SIZE_MENU);
+        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+
+        g_signal_connect (G_OBJECT (item),
+                          "activate",
+                          G_CALLBACK (on_menu_activate_open_volume_control),
+                          icon);
+
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+        gtk_widget_show_all (menu);
+        gtk_menu_popup (GTK_MENU (menu),
+                        NULL,
+                        NULL,
+                        gtk_status_icon_position_menu,
+                        status_icon,
+                        button,
+                        activate_time);
+}
+
 static gboolean
-on_status_icon_scroll_event (GtkWidget           *event_box,
+on_status_icon_scroll_event (GtkStatusIcon       *status_icon,
                              GdkEventScroll      *event,
                              GvcStreamStatusIcon *icon)
 {
@@ -311,23 +371,6 @@ on_dock_scroll_event (GtkWidget           *widget,
         return TRUE;
 }
 
-static void
-gvc_stream_status_icon_set_icon_from_name (GvcStreamStatusIcon *icon,
-                                           const gchar *icon_name)
-{
-        GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
-        gint icon_scale = gtk_widget_get_scale_factor (GTK_WIDGET (icon));
-
-        cairo_surface_t* surface = gtk_icon_theme_load_surface (icon_theme, icon_name,
-                                                                icon->priv->size,
-                                                                icon_scale, NULL,
-                                                                GTK_ICON_LOOKUP_FORCE_SIZE,
-                                                                NULL);
-
-        gtk_image_set_from_surface (GTK_IMAGE (icon->priv->image), surface);
-        cairo_surface_destroy (surface);
-}
-
 static void
 update_icon (GvcStreamStatusIcon *icon)
 {
@@ -343,10 +386,10 @@ update_icon (GvcStreamStatusIcon *icon)
         if (icon->priv->control == NULL) {
                 /* Do not bother creating a tooltip for an unusable icon as it
                  * has no practical use */
-                gtk_widget_set_has_tooltip (GTK_WIDGET (icon), FALSE);
+                gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), FALSE);
                 return;
         } else
-                gtk_widget_set_has_tooltip (GTK_WIDGET (icon), TRUE);
+                gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), TRUE);
 
         flags = mate_mixer_stream_control_get_flags (icon->priv->control);
 
@@ -369,7 +412,8 @@ update_icon (GvcStreamStatusIcon *icon)
 
         /* Apparently status icon will reset icon even if it doesn't change */
         if (icon->priv->current_icon != n) {
-                gvc_stream_status_icon_set_icon_from_name (icon, icon->priv->icon_names[n]);
+                gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon),
+                                                    icon->priv->icon_names[n]);
                 icon->priv->current_icon = n;
         }
 
@@ -410,46 +454,11 @@ update_icon (GvcStreamStatusIcon *icon)
                                           description);
         }
 
-        gtk_widget_set_tooltip_markup (GTK_WIDGET (icon), markup);
+        gtk_status_icon_set_tooltip_markup (GTK_STATUS_ICON (icon), markup);
 
         g_free (markup);
 }
 
-void
-gvc_stream_status_icon_set_size (GvcStreamStatusIcon *icon,
-                                 guint                size)
-{
-
-        /*Iterate through the icon sizes so they can be kept sharp*/
-        if (size < 22)
-                size = 16;
-        else if (size < 24)
-                size = 22;
-        else if (size < 32)
-                size = 24;
-        else if (size < 48)
-                size = 32;
-        else
-                size = 48;
-        icon->priv->size = size;
-        gvc_stream_status_icon_set_icon_from_name (icon,
-                                                   icon->priv->icon_names[icon->priv->current_icon]);
-}
-
-void
-gvc_stream_status_icon_set_orient (GvcStreamStatusIcon  *icon,
-                                   MatePanelAppletOrient orient)
-{
-        /*Sometimes orient does not get properly defined especially on a bottom panel
-         *Use the applet orientation if it is valid, otherwise set a vertical slider
-         *Otherwise bottom panels get a horizontal slider
-         */
-        if (orient)
-                icon->priv->orient = orient;
-        else
-        icon->priv->orient = MATE_PANEL_APPLET_ORIENT_DOWN;
-}
-
 void
 gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon  *icon,
                                        const gchar         **names)
@@ -468,7 +477,7 @@ gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon  *icon,
 
         /* Set the first icon as the initial one, the icon may be immediately
          * updated or not depending on whether a stream is available */
-        gvc_stream_status_icon_set_icon_from_name (icon, names[0]);
+        gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), names[0]);
         update_icon (icon);
 
         g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_ICON_NAMES]);
@@ -656,7 +665,7 @@ gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass)
 static void
 on_status_icon_visible_notify (GvcStreamStatusIcon *icon)
 {
-        if (gtk_widget_get_visible (GTK_WIDGET (icon)) == FALSE)
+        if (gtk_status_icon_get_visible (GTK_STATUS_ICON (icon)) == FALSE)
                 gtk_widget_hide (icon->priv->dock);
 }
 
@@ -665,8 +674,8 @@ on_icon_theme_change (GtkSettings         *settings,
                       GParamSpec          *pspec,
                       GvcStreamStatusIcon *icon)
 {
-        gvc_stream_status_icon_set_icon_from_name (icon,
-                                                   icon->priv->icon_names[icon->priv->current_icon]);
+        gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon),
+                                            icon->priv->icon_names[icon->priv->current_icon]);
 }
 
 static void
@@ -677,18 +686,23 @@ gvc_stream_status_icon_init (GvcStreamStatusIcon *icon)
 
         icon->priv = GVC_STREAM_STATUS_ICON_GET_PRIVATE (icon);
 
-        icon->priv->image = GTK_IMAGE (gtk_image_new ());
-        gtk_container_add (GTK_CONTAINER (icon), GTK_WIDGET (icon->priv->image));
-
-        g_signal_connect (GTK_WIDGET (icon),
+        g_signal_connect (G_OBJECT (icon),
+                          "activate",
+                          G_CALLBACK (on_status_icon_activate),
+                          icon);
+        g_signal_connect (G_OBJECT (icon),
                           "button-press-event",
                           G_CALLBACK (on_status_icon_button_press),
                           icon);
-        g_signal_connect (GTK_WIDGET (icon),
+        g_signal_connect (G_OBJECT (icon),
+                          "popup-menu",
+                          G_CALLBACK (on_status_icon_popup_menu),
+                          icon);
+        g_signal_connect (G_OBJECT (icon),
                           "scroll-event",
                           G_CALLBACK (on_status_icon_scroll_event),
                           icon);
-        g_signal_connect (GTK_WIDGET (icon),
+        g_signal_connect (G_OBJECT (icon),
                           "notify::visible",
                           G_CALLBACK (on_status_icon_visible_notify),
                           NULL);
diff --git a/mate-volume-control/gvc-stream-status-icon.h b/mate-volume-control/gvc-stream-status-icon.h
index 976616d..7b51801 100644
--- a/mate-volume-control/gvc-stream-status-icon.h
+++ b/mate-volume-control/gvc-stream-status-icon.h
@@ -41,13 +41,13 @@ typedef struct _GvcStreamStatusIconPrivate  GvcStreamStatusIconPrivate;
 
 struct _GvcStreamStatusIcon
 {
-        GtkEventBox                 parent;
+        GtkStatusIcon               parent;
         GvcStreamStatusIconPrivate *priv;
 };
 
 struct _GvcStreamStatusIconClass
 {
-        GtkEventBoxClass            parent_class;
+        GtkStatusIconClass          parent_class;
 };
 
 GType                 gvc_stream_status_icon_get_type         (void) G_GNUC_CONST;
@@ -63,19 +63,6 @@ void                  gvc_stream_status_icon_set_display_name (GvcStreamStatusIc
 void                  gvc_stream_status_icon_set_control      (GvcStreamStatusIcon    *icon,
                                                                MateMixerStreamControl *control);
 
-void                  gvc_stream_status_icon_set_size         (GvcStreamStatusIcon *icon,
-                                                               guint                size);
-
-void                  gvc_stream_status_icon_set_orient       (GvcStreamStatusIcon  *icon,
-                                                               MatePanelAppletOrient orient);
-
-gboolean              gvc_stream_status_icon_get_mute         (GvcStreamStatusIcon *icon);
-
-void                  gvc_stream_status_icon_set_mute         (GvcStreamStatusIcon *icon,
-                                                               gboolean mute);
-
-void                  gvc_stream_status_icon_volume_control   (GvcStreamStatusIcon *icon);
-
 G_END_DECLS
 
 #endif /* __GVC_STREAM_STATUS_ICON_H */
diff --git a/mate-volume-control/status-icon-main.c b/mate-volume-control/status-icon-main.c
new file mode 100644
index 0000000..0cf3910
--- /dev/null
+++ b/mate-volume-control/status-icon-main.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
+ *
+ * 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
+ * Lesser 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 St, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <libintl.h>
+#include <gio/gio.h>
+#include <libmatemixer/matemixer.h>
+
+#include "gvc-status-icon.h"
+
+static gboolean show_version = FALSE;
+static gboolean debug = FALSE;
+
+int
+main (int argc, char **argv)
+{
+        GError        *error = NULL;
+        GvcStatusIcon *status_icon;
+        GApplication  *app = NULL;
+        GOptionEntry   entries[] = {
+                { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+                { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, N_("Enable debug"), NULL },
+                { NULL }
+        };
+
+        bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+        textdomain (GETTEXT_PACKAGE);
+
+        gtk_init_with_args (&argc, &argv,
+                            _(" — MATE Volume Control Status Icon"),
+                            entries, GETTEXT_PACKAGE,
+                            &error);
+
+        if (error != NULL) {
+                g_warning ("%s", error->message);
+                g_error_free (error);
+                return 1;
+        }
+        if (show_version == TRUE) {
+                g_print ("%s %s\n", argv[0], VERSION);
+                return 0;
+        }
+        if (debug == TRUE) {
+                g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
+        }
+
+        app = g_application_new (GVC_STATUS_ICON_DBUS_NAME, G_APPLICATION_FLAGS_NONE);
+
+        if (!g_application_register (app, NULL, &error)) {
+                g_warning ("%s", error->message);
+                g_error_free (error);
+                return 1;
+        }
+        if (g_application_get_is_remote (app)) {
+                g_warning ("Status icon is already running, exiting");
+                return 0;
+        }
+        if (mate_mixer_init () == FALSE) {
+                g_warning ("libmatemixer initialization failed, exiting");
+                return 1;
+        }
+
+        gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
+                                           ICON_DATA_DIR);
+
+        status_icon = gvc_status_icon_new ();
+
+        gvc_status_icon_start (status_icon);
+        gtk_main ();
+
+        g_object_unref (status_icon);
+        g_object_unref (app);
+
+        return 0;
+}
-- 
cgit v1.2.1