diff options
Diffstat (limited to 'mate-volume-control')
21 files changed, 3571 insertions, 2754 deletions
diff --git a/mate-volume-control/src/Makefile.am b/mate-volume-control/src/Makefile.am index 8dd1b93..3ca81f5 100644 --- a/mate-volume-control/src/Makefile.am +++ b/mate-volume-control/src/Makefile.am @@ -9,7 +9,7 @@ AM_CPPFLAGS = \ $(WARN_CFLAGS) \ -I$(top_srcdir)/sound-theme \ $(VOLUME_CONTROL_CFLAGS) \ - $(DISABLE_DEPRECATED) \ + $(xxDISABLE_DEPRECATED) \ $(PULSEAUDIO_CFLAGS) \ -DLOCALE_DIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ diff --git a/mate-volume-control/src/applet-main.c b/mate-volume-control/src/applet-main.c index 34151ba..1c260fc 100644 --- a/mate-volume-control/src/applet-main.c +++ b/mate-volume-control/src/applet-main.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,8 +21,9 @@ #include "config.h" -#include <glib/gi18n.h> #include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> #include <gtk/gtk.h> #include <libintl.h> @@ -37,7 +39,7 @@ main (int argc, char **argv) { GError *error = NULL; GvcApplet *applet; - UniqueApp *app = NULL; + UniqueApp *app; GOptionEntry entries[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, { NULL } @@ -51,6 +53,7 @@ main (int argc, char **argv) (char *) _(" — MATE Volume Control Applet"), entries, GETTEXT_PACKAGE, &error); + if (error != NULL) { g_warning ("%s", error->message); return 1; diff --git a/mate-volume-control/src/dialog-main.c b/mate-volume-control/src/dialog-main.c index 2b139b1..24cae78 100644 --- a/mate-volume-control/src/dialog-main.c +++ b/mate-volume-control/src/dialog-main.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,11 +21,11 @@ #include "config.h" -#include <libintl.h> -#include <glib/gi18n.h> #include <glib.h> +#include <glib/gi18n.h> #include <gtk/gtk.h> +#include <libintl.h> #include <unique/uniqueapp.h> #include <libmatemixer/matemixer.h> @@ -42,10 +43,10 @@ static GtkWidget *warning_dialog = NULL; static void on_dialog_response (GtkDialog *dialog, guint response_id, gpointer data) { - gboolean destroy = GPOINTER_TO_INT (data); + gboolean destroy = GPOINTER_TO_INT (data); - if (destroy) - gtk_widget_destroy (GTK_WIDGET (dialog)); + if (destroy) + gtk_widget_destroy (GTK_WIDGET (dialog)); gtk_main_quit (); } @@ -53,10 +54,10 @@ on_dialog_response (GtkDialog *dialog, guint response_id, gpointer data) static void on_dialog_close (GtkDialog *dialog, gpointer data) { - gboolean destroy = GPOINTER_TO_INT (data); + gboolean destroy = GPOINTER_TO_INT (data); - if (destroy) - gtk_widget_destroy (GTK_WIDGET (dialog)); + if (destroy) + gtk_widget_destroy (GTK_WIDGET (dialog)); gtk_main_quit (); } @@ -76,12 +77,12 @@ on_app_message_received (UniqueApp *app, static void remove_warning_dialog (void) { - if (popup_id != 0) { - g_source_remove (popup_id); - popup_id = 0; - } + if (popup_id != 0) { + g_source_remove (popup_id); + popup_id = 0; + } - g_clear_pointer (&warning_dialog, gtk_widget_destroy); + g_clear_pointer (&warning_dialog, gtk_widget_destroy); } static void @@ -93,25 +94,30 @@ control_ready (MateMixerControl *control, UniqueApp *app) return; app_dialog = GTK_WIDGET (gvc_mixer_dialog_new (control)); - g_signal_connect (app_dialog, + + g_signal_connect (G_OBJECT (app_dialog), "response", G_CALLBACK (on_dialog_response), GINT_TO_POINTER (FALSE)); - g_signal_connect (app_dialog, + g_signal_connect (G_OBJECT (app_dialog), "close", G_CALLBACK (on_dialog_close), GINT_TO_POINTER (FALSE)); gvc_mixer_dialog_set_page (GVC_MIXER_DIALOG (app_dialog), page); - g_signal_connect (app, "message-received", - G_CALLBACK (on_app_message_received), app_dialog); + g_signal_connect (G_OBJECT (app), + "message-received", + G_CALLBACK (on_app_message_received), + app_dialog); gtk_widget_show (app_dialog); } static void -on_control_state_notify (MateMixerControl *control, UniqueApp *app) +on_control_state_notify (MateMixerControl *control, + GParamSpec *pspec, + UniqueApp *app) { MateMixerState state = mate_mixer_control_get_state (control); gboolean failed = FALSE; @@ -194,6 +200,7 @@ main (int argc, char **argv) (char *) _(" — MATE Volume Control"), entries, GETTEXT_PACKAGE, &error); + if (error != NULL) { g_warning ("%s", error->message); return 1; @@ -206,8 +213,8 @@ main (int argc, char **argv) app = unique_app_new (GVC_DIALOG_DBUS_NAME, NULL); if (unique_app_is_running (app)) { - unique_app_send_message (app, UNIQUE_ACTIVATE, NULL); - return 0; + unique_app_send_message (app, UNIQUE_ACTIVATE, NULL); + return 0; } if (!mate_mixer_init ()) { g_warning ("libmatemixer initialization failed, exiting"); @@ -216,19 +223,20 @@ main (int argc, char **argv) control = mate_mixer_control_new (); + mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_NULL); + mate_mixer_control_set_app_name (control, _("Volume Control")); mate_mixer_control_set_app_id (control, GVC_DIALOG_DBUS_NAME); mate_mixer_control_set_app_version (control, VERSION); mate_mixer_control_set_app_icon (control, "multimedia-volume-control"); - g_signal_connect (control, + g_signal_connect (G_OBJECT (control), "notify::state", G_CALLBACK (on_control_state_notify), app); mate_mixer_control_open (control); - /* */ if (mate_mixer_control_get_state (control) == MATE_MIXER_STATE_CONNECTING) popup_id = g_timeout_add_seconds (DIALOG_POPUP_TIMEOUT, dialog_popup_timeout, diff --git a/mate-volume-control/src/gvc-applet.c b/mate-volume-control/src/gvc-applet.c index 4522a8f..b2f40a1 100644 --- a/mate-volume-control/src/gvc-applet.c +++ b/mate-volume-control/src/gvc-applet.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -18,17 +19,14 @@ * */ -// XXX there are some bugs unrelated to the port, let's see if I can fix them: -// - clicking in the slider makes it disappear -// - drag all the way down to mute, then start dragging up and the change won't -// be reflected until i stop dragging -// - disable the slider if the volume is not changable - #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-applet.h" @@ -75,39 +73,69 @@ update_icon_input (GvcApplet *applet) * is a non-mixer application using the input */ stream = mate_mixer_control_get_default_input_stream (applet->priv->control); if (stream != NULL) { - const gchar *app; + const gchar *app_id; const GList *inputs = mate_mixer_control_list_streams (applet->priv->control); - while (inputs) { + while (inputs != NULL) { MateMixerStream *input = MATE_MIXER_STREAM (inputs->data); MateMixerStreamFlags flags = mate_mixer_stream_get_flags (input); - if (flags & MATE_MIXER_STREAM_INPUT && - flags & MATE_MIXER_STREAM_APPLICATION) { + if (flags & MATE_MIXER_STREAM_INPUT && flags & MATE_MIXER_STREAM_CLIENT) { MateMixerClientStream *client = MATE_MIXER_CLIENT_STREAM (input); - app = mate_mixer_client_stream_get_app_id (client); - if (app == NULL) { + MateMixerClientStreamRole client_role = + mate_mixer_client_stream_get_role (client); + MateMixerClientStreamFlags client_flags = + mate_mixer_client_stream_get_flags (client); + + if (!(client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) || + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT || + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_TEST || + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT || + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_FILTER) { + /* Skip roles we don't care about and non-application + * client streams */ + inputs = inputs->next; + continue; + } + + app_id = mate_mixer_client_stream_get_app_id (client); + if (app_id == NULL) { /* A recording application which has no * identifier set */ + g_debug ("Found a recording application %s (%s)", + mate_mixer_stream_get_name (input), + mate_mixer_stream_get_description (input)); show = TRUE; break; } - if (!g_str_equal (app, "org.mate.VolumeControl") && - !g_str_equal (app, "org.gnome.VolumeControl") && - !g_str_equal (app, "org.PulseAudio.pavucontrol")) { + 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); show = TRUE; break; } } inputs = inputs->next; } + + g_debug ("Current default input stream is %s (%s)", + mate_mixer_stream_get_name (stream), + mate_mixer_stream_get_description (stream)); + + if (show) + g_debug ("Input icon enabled"); + else + g_debug ("There is no recording application, input icon disabled"); + } else { + g_debug ("There is no default input stream, input icon disabled"); } - gvc_stream_status_icon_set_mixer_stream (applet->priv->icon_input, stream); + gvc_stream_status_icon_set_stream (applet->priv->icon_input, stream); gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_input), show); } @@ -119,33 +147,23 @@ update_icon_output (GvcApplet *applet) stream = mate_mixer_control_get_default_output_stream (applet->priv->control); - gvc_stream_status_icon_set_mixer_stream (applet->priv->icon_output, stream); - - /* Enable the output icon in case there is an output stream present */ - gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), - (stream != NULL) ? TRUE : FALSE); -} - -void -gvc_applet_start (GvcApplet *applet) -{ - g_return_if_fail (GVC_IS_APPLET (applet)); + gvc_stream_status_icon_set_stream (applet->priv->icon_output, stream); - if (G_UNLIKELY (applet->priv->running)) - return; + if (stream != NULL) { + g_debug ("Current default output stream is %s (%s)", + mate_mixer_stream_get_name (stream), + mate_mixer_stream_get_description (stream)); - if (mate_mixer_control_open (applet->priv->control) == 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"); + gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), + TRUE); + g_debug ("Output icon enabled"); + } else { gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), FALSE); - gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_input), - FALSE); - } - applet->priv->running = TRUE; + g_debug ("There is no default output stream, output icon disabled"); + } } static void @@ -155,12 +173,20 @@ on_control_state_notify (MateMixerControl *control, { MateMixerState state = mate_mixer_control_get_state (control); - if (state == MATE_MIXER_STATE_FAILED) + switch (state) { + case MATE_MIXER_STATE_FAILED: g_warning ("Failed to connect to a sound system"); + break; - /* Each status change may affect the visibility of the icons */ - update_icon_output (applet); - update_icon_input (applet); + case MATE_MIXER_STATE_READY: + case MATE_MIXER_STATE_CONNECTING: + /* Each status change may affect the visibility of the icons */ + update_icon_output (applet); + update_icon_input (applet); + break; + default: + break; + } } static void @@ -195,9 +221,21 @@ on_control_stream_added (MateMixerControl *control, /* Newly added input application stream may cause the input status * icon to change visibility */ - if (flags & MATE_MIXER_STREAM_INPUT && - flags & MATE_MIXER_STREAM_APPLICATION) - update_icon_input (applet); + if (flags & MATE_MIXER_STREAM_CLIENT && flags & MATE_MIXER_STREAM_INPUT) { + MateMixerClientStreamFlags client_flags = + mate_mixer_client_stream_get_flags (MATE_MIXER_CLIENT_STREAM (stream)); + + if (client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) { + g_debug ("Added input application stream %s (%s)", + mate_mixer_stream_get_name (stream), + mate_mixer_stream_get_description (stream)); + + update_icon_input (applet); + } + } else + g_debug ("Ignoring new stream %s (%s)", + mate_mixer_stream_get_name (stream), + mate_mixer_stream_get_description (stream)); } static void @@ -205,11 +243,45 @@ on_control_stream_removed (MateMixerControl *control, const gchar *name, GvcApplet *applet) { + g_debug ("Removed stream %s", name); + /* The removed stream could be an application input, which may cause * the input status icon to disappear */ update_icon_input (applet); } +void +gvc_applet_start (GvcApplet *applet) +{ + MateMixerState state; + + g_return_if_fail (GVC_IS_APPLET (applet)); + + if (G_UNLIKELY (applet->priv->running == TRUE)) + return; + + state = mate_mixer_control_get_state (applet->priv->control); + + if (G_UNLIKELY (state != MATE_MIXER_STATE_IDLE)) { + g_warn_if_reached (); + } else { + if (mate_mixer_control_open (applet->priv->control) == 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"); + } + + gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_output), + FALSE); + gtk_status_icon_set_visible (GTK_STATUS_ICON (applet->priv->icon_input), + FALSE); + } + + g_debug ("Applet has been started"); + + applet->priv->running = TRUE; +} + static void gvc_applet_dispose (GObject *object) { @@ -257,23 +329,23 @@ gvc_applet_init (GvcApplet *applet) mate_mixer_control_set_app_version (applet->priv->control, VERSION); mate_mixer_control_set_app_icon (applet->priv->control, "multimedia-volume-control"); - g_signal_connect (applet->priv->control, + g_signal_connect (G_OBJECT (applet->priv->control), "notify::state", G_CALLBACK (on_control_state_notify), applet); - g_signal_connect (applet->priv->control, + g_signal_connect (G_OBJECT (applet->priv->control), "notify::default-input", G_CALLBACK (on_control_default_input_notify), applet); - g_signal_connect (applet->priv->control, + g_signal_connect (G_OBJECT (applet->priv->control), "notify::default-output", G_CALLBACK (on_control_default_output_notify), applet); - g_signal_connect (applet->priv->control, + g_signal_connect (G_OBJECT (applet->priv->control), "stream-added", G_CALLBACK (on_control_stream_added), applet); - g_signal_connect (applet->priv->control, + g_signal_connect (G_OBJECT (applet->priv->control), "stream-removed", G_CALLBACK (on_control_stream_removed), applet); diff --git a/mate-volume-control/src/gvc-applet.h b/mate-volume-control/src/gvc-applet.h index 50b3a80..991ef6d 100644 --- a/mate-volume-control/src/gvc-applet.h +++ b/mate-volume-control/src/gvc-applet.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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/src/gvc-balance-bar.c b/mate-volume-control/src/gvc-balance-bar.c index 0730b67..f31d136 100644 --- a/mate-volume-control/src/gvc-balance-bar.c +++ b/mate-volume-control/src/gvc-balance-bar.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -18,10 +19,9 @@ * */ -#include "config.h" - #include <glib.h> #include <glib/gi18n.h> +#include <glib-object.h> #include <gtk/gtk.h> #include <canberra-gtk.h> @@ -29,14 +29,17 @@ #include "gvc-balance-bar.h" -#define SCALE_SIZE 128 -#define ADJUSTMENT_MAX_NORMAL 65536.0 /* PA_VOLUME_NORM */ // XXX +#define BALANCE_BAR_STYLE \ + "style \"balance-bar-scale-style\" {\n" \ + " GtkScale::trough-side-details = 0\n" \ + "}\n" \ + "widget \"*.balance-bar-scale\" style : rc \"balance-bar-scale-style\"\n" +#define SCALE_SIZE 128 #define GVC_BALANCE_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBarPrivate)) struct _GvcBalanceBarPrivate { - MateMixerStream *stream; GvcBalanceType btype; GtkWidget *scale_box; GtkWidget *start_box; @@ -46,7 +49,8 @@ struct _GvcBalanceBarPrivate GtkAdjustment *adjustment; GtkSizeGroup *size_group; gboolean symmetric; - gboolean click_lock; + MateMixerStream *stream; + gint lfe_channel; }; enum @@ -59,21 +63,16 @@ enum static GParamSpec *properties[N_PROPERTIES] = { NULL, }; -static void gvc_balance_bar_class_init (GvcBalanceBarClass *klass); -static void gvc_balance_bar_init (GvcBalanceBar *balance_bar); -static void gvc_balance_bar_dispose (GObject *object); - -static gboolean on_scale_button_press_event (GtkWidget *widget, - GdkEventButton *event, - GvcBalanceBar *bar); -static gboolean on_scale_button_release_event (GtkWidget *widget, - GdkEventButton *event, - GvcBalanceBar *bar); -static gboolean on_scale_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcBalanceBar *bar); -static void on_adjustment_value_changed (GtkAdjustment *adjustment, - GvcBalanceBar *bar); +static void gvc_balance_bar_class_init (GvcBalanceBarClass *klass); +static void gvc_balance_bar_init (GvcBalanceBar *balance_bar); +static void gvc_balance_bar_dispose (GObject *object); + +static gboolean on_scale_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcBalanceBar *bar); + +static void on_adjustment_value_changed (GtkAdjustment *adjustment, + GvcBalanceBar *bar); #if GTK_CHECK_VERSION (3, 0, 0) G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_BOX) @@ -81,33 +80,78 @@ G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_BOX) G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_HBOX) #endif -static GtkWidget * -_scale_box_new (GvcBalanceBar *bar) +static void +create_scale_box (GvcBalanceBar *bar) { - GvcBalanceBarPrivate *priv = bar->priv; - GtkWidget *box; - GtkWidget *sbox; - GtkWidget *ebox; - GtkAdjustment *adjustment = bar->priv->adjustment; - char *str_lower, *str_upper; - gdouble lower, upper; +#if GTK_CHECK_VERSION (3, 0, 0) + bar->priv->scale_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + bar->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + bar->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + bar->priv->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, + bar->priv->adjustment); +#if GTK_CHECK_VERSION (3, 4, 0) + /* Balance and fade scales do not have an origin */ + if (bar->priv->btype != BALANCE_TYPE_LFE) + gtk_scale_set_has_origin (GTK_SCALE (bar->priv->scale), FALSE); +#endif +#else + bar->priv->scale_box = gtk_hbox_new (FALSE, 6); + bar->priv->start_box = gtk_hbox_new (FALSE, 6); + bar->priv->end_box = gtk_hbox_new (FALSE, 6); + bar->priv->scale = gtk_hscale_new (bar->priv->adjustment); + + /* GTK2 way to remove the origin */ + if (bar->priv->btype != BALANCE_TYPE_LFE) { + gtk_rc_parse_string (BALANCE_BAR_STYLE); + gtk_widget_set_name (bar->priv->scale, "balance-bar-scale"); + } +#endif - bar->priv->scale_box = box = gtk_hbox_new (FALSE, 6); - priv->scale = gtk_hscale_new (priv->adjustment); - gtk_widget_set_size_request (priv->scale, SCALE_SIZE, -1); + gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->start_box, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->label, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->scale, + TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->end_box, + FALSE, FALSE, 0); - gtk_widget_set_name (priv->scale, "balance-bar-scale"); - gtk_rc_parse_string ("style \"balance-bar-scale-style\" {\n" - " GtkScale::trough-side-details = 0\n" - "}\n" - "widget \"*.balance-bar-scale\" style : rc \"balance-bar-scale-style\"\n"); + ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE); - bar->priv->start_box = sbox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0); + gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK); - gtk_box_pack_start (GTK_BOX (sbox), priv->label, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (bar->priv->scale), + "scroll-event", + G_CALLBACK (on_scale_scroll_event), + bar); - gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, TRUE, 0); + if (bar->priv->size_group != NULL) { + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->start_box); + + if (bar->priv->symmetric) + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->end_box); + } + + gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE); +} + +static void +update_scale_marks (GvcBalanceBar *bar) +{ + gchar *str_lower = NULL, + *str_upper = NULL; + gdouble lower, + upper; + + gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale)); switch (bar->priv->btype) { case BALANCE_TYPE_RL: @@ -122,49 +166,26 @@ _scale_box_new (GvcBalanceBar *bar) str_lower = g_strdup_printf ("<small>%s</small>", C_("balance", "Minimum")); str_upper = g_strdup_printf ("<small>%s</small>", C_("balance", "Maximum")); break; - default: - g_assert_not_reached (); } - lower = gtk_adjustment_get_lower (adjustment); - gtk_scale_add_mark (GTK_SCALE (priv->scale), lower, - GTK_POS_BOTTOM, str_lower); + lower = gtk_adjustment_get_lower (bar->priv->adjustment); + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + lower, + GTK_POS_BOTTOM, + str_lower); + upper = gtk_adjustment_get_upper (bar->priv->adjustment); + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + upper, + GTK_POS_BOTTOM, + str_upper); g_free (str_lower); - upper = gtk_adjustment_get_upper (adjustment); - gtk_scale_add_mark (GTK_SCALE (priv->scale), upper, - GTK_POS_BOTTOM, str_upper); g_free (str_upper); - if (bar->priv->btype != BALANCE_TYPE_LFE) { - gtk_scale_add_mark (GTK_SCALE (priv->scale), - (upper - lower)/2 + lower, - GTK_POS_BOTTOM, NULL); - } - - bar->priv->end_box = ebox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, FALSE, 0); - - ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE); - gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK); - - g_signal_connect (G_OBJECT (bar->priv->scale), "button-press-event", - G_CALLBACK (on_scale_button_press_event), bar); - g_signal_connect (G_OBJECT (bar->priv->scale), "button-release-event", - G_CALLBACK (on_scale_button_release_event), bar); - g_signal_connect (G_OBJECT (bar->priv->scale), "scroll-event", - G_CALLBACK (on_scale_scroll_event), bar); - - if (bar->priv->size_group != NULL) { - gtk_size_group_add_widget (bar->priv->size_group, sbox); - - if (bar->priv->symmetric) { - gtk_size_group_add_widget (bar->priv->size_group, ebox); - } - } - - gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE); - - return box; + if (bar->priv->btype != BALANCE_TYPE_LFE) + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + (upper - lower) / 2 + lower, + GTK_POS_BOTTOM, + NULL); } void @@ -173,6 +194,7 @@ gvc_balance_bar_set_size_group (GvcBalanceBar *bar, gboolean symmetric) { g_return_if_fail (GVC_IS_BALANCE_BAR (bar)); + g_return_if_fail (GTK_IS_SIZE_GROUP (group)); bar->priv->size_group = group; bar->priv->symmetric = symmetric; @@ -181,31 +203,13 @@ gvc_balance_bar_set_size_group (GvcBalanceBar *bar, gtk_size_group_add_widget (bar->priv->size_group, bar->priv->start_box); - if (bar->priv->symmetric) { + if (bar->priv->symmetric) gtk_size_group_add_widget (bar->priv->size_group, bar->priv->end_box); - } } gtk_widget_queue_draw (GTK_WIDGET (bar)); } -static const char * -btype_to_string (guint btype) -{ - switch (btype) { - case BALANCE_TYPE_RL: - return "Balance"; - case BALANCE_TYPE_FR: - return "Fade"; - break; - case BALANCE_TYPE_LFE: - return "LFE"; - default: - g_assert_not_reached (); - } - return NULL; -} - static void update_balance_value (GvcBalanceBar *bar) { @@ -214,20 +218,20 @@ update_balance_value (GvcBalanceBar *bar) switch (bar->priv->btype) { case BALANCE_TYPE_RL: value = mate_mixer_stream_get_balance (bar->priv->stream); + g_debug ("Balance value changed to %.2f", value); break; case BALANCE_TYPE_FR: value = mate_mixer_stream_get_fade (bar->priv->stream); + g_debug ("Fade value changed to %.2f", value); break; case BALANCE_TYPE_LFE: - value = mate_mixer_stream_get_position_volume (bar->priv->stream, - MATE_MIXER_CHANNEL_LFE); + value = mate_mixer_stream_get_channel_volume (bar->priv->stream, + bar->priv->lfe_channel); + + g_debug ("Subwoofer volume changed to %.0f", value); break; - default: - g_assert_not_reached (); } - g_debug ("%s value changed to %.1f", btype_to_string (bar->priv->btype), value); - gtk_adjustment_set_value (bar->priv->adjustment, value); } @@ -239,6 +243,22 @@ on_balance_value_changed (MateMixerStream *stream, update_balance_value (bar); } +static gint +find_stream_lfe_channel (MateMixerStream *stream) +{ + guint i; + + for (i = 0; i < mate_mixer_stream_get_num_channels (stream); i++) { + MateMixerChannelPosition position; + + position = mate_mixer_stream_get_channel_position (stream, i); + if (position == MATE_MIXER_CHANNEL_LFE) + return i; + } + + return -1; +} + static void gvc_balance_bar_set_stream (GvcBalanceBar *bar, MateMixerStream *stream) { @@ -254,6 +274,37 @@ gvc_balance_bar_set_stream (GvcBalanceBar *bar, MateMixerStream *stream) bar->priv->stream = g_object_ref (stream); + if (bar->priv->btype == BALANCE_TYPE_LFE) { + gdouble minimum = 0.0; + gdouble maximum = 0.0; + + if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_VOLUME) { + minimum = mate_mixer_stream_get_min_volume (stream); + maximum = mate_mixer_stream_get_normal_volume (stream); + } + + /* Configure the adjustment for the volume limits of the current + * stream. + * Only subwoofer scale uses volume, balance and fade use fixed + * limits which do not need to be updated as balance type is + * only set during construction. */ + gtk_adjustment_configure (GTK_ADJUSTMENT (bar->priv->adjustment), + gtk_adjustment_get_value (bar->priv->adjustment), + minimum, + maximum, + (maximum - minimum) / 100.0, + (maximum - minimum) / 10.0, + 0.0); + + bar->priv->lfe_channel = find_stream_lfe_channel (stream); + + if (G_LIKELY (bar->priv->lfe_channel > -1)) + g_debug ("Found LFE channel at position %d", bar->priv->lfe_channel); + else + g_warn_if_reached (); + } else + bar->priv->lfe_channel = -1; + switch (bar->priv->btype) { case BALANCE_TYPE_RL: g_signal_connect (G_OBJECT (stream), @@ -273,41 +324,30 @@ gvc_balance_bar_set_stream (GvcBalanceBar *bar, MateMixerStream *stream) G_CALLBACK (on_balance_value_changed), bar); break; - default: - g_assert_not_reached (); } update_balance_value (bar); + update_scale_marks (bar); - g_object_notify (G_OBJECT (bar), "stream"); + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_STREAM]); } static void gvc_balance_bar_set_balance_type (GvcBalanceBar *bar, GvcBalanceType btype) { - GtkWidget *frame; + GtkWidget *frame; + GtkAdjustment *adjustment; - g_return_if_fail (GVC_BALANCE_BAR (bar)); + /* Create adjustment with limits for balance and fade types because + * some limits must be provided. + * If subwoofer type is used instead, the limits will be changed when + * stream is set. */ + adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -1.0, 1.0, 0.05, 0.5, 0.0)); bar->priv->btype = btype; - if (bar->priv->btype != BALANCE_TYPE_LFE) { - bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, - -1.0, - 1.0, - 0.5, - 0.5, - 0.0)); - } else { - bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, - 0.0, - ADJUSTMENT_MAX_NORMAL, - ADJUSTMENT_MAX_NORMAL/100.0, - ADJUSTMENT_MAX_NORMAL/10.0, - 0.0)); - } + bar->priv->adjustment = GTK_ADJUSTMENT (g_object_ref_sink (adjustment)); - g_object_ref_sink (bar->priv->adjustment); - g_signal_connect (bar->priv->adjustment, + g_signal_connect (G_OBJECT (adjustment), "value-changed", G_CALLBACK (on_adjustment_value_changed), bar); @@ -322,27 +362,26 @@ gvc_balance_bar_set_balance_type (GvcBalanceBar *bar, GvcBalanceType btype) case BALANCE_TYPE_LFE: bar->priv->label = gtk_label_new_with_mnemonic (_("_Subwoofer:")); break; - default: - g_assert_not_reached (); } - gtk_misc_set_alignment (GTK_MISC (bar->priv->label), - 0.0, - 0.0); - /* frame */ + + gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.0); + + /* Frame */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_container_add (GTK_CONTAINER (bar), frame); + gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0); - /* box with scale */ - bar->priv->scale_box = _scale_box_new (bar); + /* Box with scale */ + create_scale_box (bar); gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); - gtk_widget_show_all (frame); - gtk_widget_set_direction (bar->priv->scale, GTK_TEXT_DIR_LTR); gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label), bar->priv->scale); - g_object_notify (G_OBJECT (bar), "balance-type"); + gtk_widget_set_direction (bar->priv->scale, GTK_TEXT_DIR_LTR); + gtk_widget_show_all (frame); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_BALANCE_TYPE]); } static void @@ -414,60 +453,37 @@ gvc_balance_bar_class_init (GvcBalanceBarClass *klass) g_type_class_add_private (klass, sizeof (GvcBalanceBarPrivate)); } - -static gboolean -on_scale_button_press_event (GtkWidget *widget, - GdkEventButton *event, - GvcBalanceBar *bar) -{ - // XXX this is unused - bar->priv->click_lock = TRUE; - return FALSE; -} - -static gboolean -on_scale_button_release_event (GtkWidget *widget, - GdkEventButton *event, - GvcBalanceBar *bar) -{ - // XXX this is unused - bar->priv->click_lock = FALSE; - return FALSE; -} - static gboolean on_scale_scroll_event (GtkWidget *widget, GdkEventScroll *event, GvcBalanceBar *bar) { gdouble value; - - value = gtk_adjustment_get_value (bar->priv->adjustment); - - if (bar->priv->btype == BALANCE_TYPE_LFE) { - if (event->direction == GDK_SCROLL_UP) { - if (value + ADJUSTMENT_MAX_NORMAL/100.0 > ADJUSTMENT_MAX_NORMAL) - value = ADJUSTMENT_MAX_NORMAL; - else - value = value + ADJUSTMENT_MAX_NORMAL/100.0; - } else if (event->direction == GDK_SCROLL_DOWN) { - if (value - ADJUSTMENT_MAX_NORMAL/100.0 < 0) - value = 0.0; - else - value = value - ADJUSTMENT_MAX_NORMAL/100.0; - } - } else { - if (event->direction == GDK_SCROLL_UP) { - if (value + 0.01 > 1.0) - value = 1.0; - else - value = value + 0.01; - } else if (event->direction == GDK_SCROLL_DOWN) { - if (value - 0.01 < 0) - value = 0.0; - else - value = value - 0.01; - } + gdouble minimum; + gdouble maximum; + gdouble step; + + value = gtk_adjustment_get_value (bar->priv->adjustment); + minimum = gtk_adjustment_get_lower (bar->priv->adjustment); + maximum = gtk_adjustment_get_upper (bar->priv->adjustment); + + // XXX fix this for GTK3 + + if (bar->priv->btype == BALANCE_TYPE_LFE) + step = (maximum - minimum) / 100.0; + else + step = 0.05; + + if (event->direction == GDK_SCROLL_UP) { + if (value + step > maximum) + value = maximum; + else + value = value + step; + } else if (event->direction == GDK_SCROLL_DOWN) { + if (value - step < minimum) + value = minimum; + else + value = value - step; } gtk_adjustment_set_value (bar->priv->adjustment, value); @@ -492,12 +508,10 @@ on_adjustment_value_changed (GtkAdjustment *adjustment, GvcBalanceBar *bar) mate_mixer_stream_set_fade (bar->priv->stream, value); break; case BALANCE_TYPE_LFE: - mate_mixer_stream_set_position_volume (bar->priv->stream, - MATE_MIXER_CHANNEL_LFE, - value); + mate_mixer_stream_set_channel_volume (bar->priv->stream, + bar->priv->lfe_channel, + value); break; - default: - g_assert_not_reached (); } } @@ -530,5 +544,8 @@ gvc_balance_bar_new (MateMixerStream *stream, GvcBalanceType btype) return g_object_new (GVC_TYPE_BALANCE_BAR, "balance-type", btype, "stream", stream, +#if GTK_CHECK_VERSION (3, 0, 0) + "orientation", GTK_ORIENTATION_HORIZONTAL, +#endif NULL); } diff --git a/mate-volume-control/src/gvc-balance-bar.h b/mate-volume-control/src/gvc-balance-bar.h index 02149d0..32602ae 100644 --- a/mate-volume-control/src/gvc-balance-bar.h +++ b/mate-volume-control/src/gvc-balance-bar.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -67,7 +68,7 @@ struct _GvcBalanceBarClass #endif }; -GType gvc_balance_bar_get_type (void); +GType gvc_balance_bar_get_type (void) G_GNUC_CONST; GtkWidget * gvc_balance_bar_new (MateMixerStream *stream, GvcBalanceType btype); diff --git a/mate-volume-control/src/gvc-channel-bar.c b/mate-volume-control/src/gvc-channel-bar.c index b1bf894..9a2c765 100644 --- a/mate-volume-control/src/gvc-channel-bar.c +++ b/mate-volume-control/src/gvc-channel-bar.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -22,75 +23,71 @@ #include <glib.h> #include <glib/gi18n.h> +#include <glib-object.h> #include <gtk/gtk.h> #include <canberra-gtk.h> +#include <libmatemixer/matemixer.h> #include "gvc-channel-bar.h" #define SCALE_SIZE 128 -#define ADJUSTMENT_MAX_NORMAL 65536.0 /* PA_VOLUME_NORM */ -#define ADJUSTMENT_MAX_AMPLIFIED 98304.0 /* 1.5 * ADJUSTMENT_MAX_NORMAL */ -#define ADJUSTMENT_MAX (bar->priv->is_amplified ? ADJUSTMENT_MAX_AMPLIFIED : ADJUSTMENT_MAX_NORMAL) -#define SCROLLSTEP (ADJUSTMENT_MAX / 100.0 * 5.0) - #define GVC_CHANNEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarPrivate)) -struct GvcChannelBarPrivate -{ - GtkOrientation orientation; - GtkWidget *scale_box; - GtkWidget *start_box; - GtkWidget *end_box; - GtkWidget *image; - GtkWidget *label; - GtkWidget *low_image; - GtkWidget *scale; - GtkWidget *high_image; - GtkWidget *mute_box; - GtkWidget *mute_button; - GtkAdjustment *adjustment; - GtkAdjustment *zero_adjustment; - gboolean show_mute; - gboolean is_muted; - char *name; - char *icon_name; - char *low_icon_name; - char *high_icon_name; - GtkSizeGroup *size_group; - gboolean symmetric; - gboolean click_lock; - gboolean is_amplified; - guint32 base_volume; +struct _GvcChannelBarPrivate +{ + GtkOrientation orientation; + GtkWidget *scale_box; + GtkWidget *start_box; + GtkWidget *end_box; + GtkWidget *image; + GtkWidget *label; + GtkWidget *low_image; + GtkWidget *scale; + GtkWidget *high_image; + GtkWidget *mute_box; + GtkWidget *mute_button; + GtkAdjustment *adjustment; + gboolean show_icons; + gboolean show_mute; + gboolean show_marks; + gboolean extended; + GtkSizeGroup *size_group; + gboolean symmetric; + gboolean click_lock; + MateMixerStream *stream; + MateMixerStreamFlags stream_flags; }; -enum -{ +enum { PROP_0, + PROP_STREAM, PROP_ORIENTATION, + PROP_SHOW_ICONS, PROP_SHOW_MUTE, - PROP_IS_MUTED, - PROP_ADJUSTMENT, + PROP_SHOW_MARKS, + PROP_EXTENDED, PROP_NAME, PROP_ICON_NAME, PROP_LOW_ICON_NAME, PROP_HIGH_ICON_NAME, - PROP_IS_AMPLIFIED, + N_PROPERTIES }; +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + static void gvc_channel_bar_class_init (GvcChannelBarClass *klass); -static void gvc_channel_bar_init (GvcChannelBar *channel_bar); -static void gvc_channel_bar_finalize (GObject *object); - -static gboolean on_scale_button_press_event (GtkWidget *widget, - GdkEventButton *event, - GvcChannelBar *bar); -static gboolean on_scale_button_release_event (GtkWidget *widget, - GdkEventButton *event, - GvcChannelBar *bar); -static gboolean on_scale_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcChannelBar *bar); +static void gvc_channel_bar_init (GvcChannelBar *bar); + +static gboolean on_scale_button_press_event (GtkWidget *widget, + GdkEventButton *event, + GvcChannelBar *bar); +static gboolean on_scale_button_release_event (GtkWidget *widget, + GdkEventButton *event, + GvcChannelBar *bar); +static gboolean on_scale_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcChannelBar *bar); #if GTK_CHECK_VERSION (3, 0, 0) G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_BOX) @@ -98,131 +95,159 @@ G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_BOX) G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_HBOX) #endif -static GtkWidget * -_scale_box_new (GvcChannelBar *bar) +static void +create_scale_box (GvcChannelBar *bar) { - GvcChannelBarPrivate *priv = bar->priv; - GtkWidget *box; - GtkWidget *sbox; - GtkWidget *ebox; - - if (priv->orientation == GTK_ORIENTATION_VERTICAL) { - bar->priv->scale_box = box = gtk_vbox_new (FALSE, 6); - - priv->scale = gtk_vscale_new (priv->adjustment); - - gtk_widget_set_size_request (priv->scale, -1, SCALE_SIZE); - gtk_range_set_inverted (GTK_RANGE (priv->scale), TRUE); - - bar->priv->start_box = sbox = gtk_vbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (sbox), priv->image, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (sbox), priv->label, FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (sbox), priv->high_image, FALSE, FALSE, 0); - gtk_widget_hide (priv->high_image); - gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, TRUE, 0); - - bar->priv->end_box = ebox = gtk_vbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (ebox), priv->low_image, FALSE, FALSE, 0); - gtk_widget_hide (priv->low_image); - - gtk_box_pack_start (GTK_BOX (ebox), priv->mute_box, FALSE, FALSE, 0); +#if GTK_CHECK_VERSION (3, 0, 0) + bar->priv->scale_box = gtk_box_new (bar->priv->orientation, 6); + bar->priv->start_box = gtk_box_new (bar->priv->orientation, 6); + bar->priv->end_box = gtk_box_new (bar->priv->orientation, 6); + bar->priv->scale = gtk_scale_new (bar->priv->orientation, + bar->priv->adjustment); +#else + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + bar->priv->scale_box = gtk_vbox_new (FALSE, 6); + bar->priv->start_box = gtk_vbox_new (FALSE, 6); + bar->priv->end_box = gtk_vbox_new (FALSE, 6); + bar->priv->scale = gtk_vscale_new (bar->priv->adjustment); } else { - bar->priv->scale_box = box = gtk_hbox_new (FALSE, 6); - - priv->scale = gtk_hscale_new (priv->adjustment); - - gtk_widget_set_size_request (priv->scale, SCALE_SIZE, -1); - - bar->priv->start_box = sbox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0); - - gtk_box_pack_end (GTK_BOX (sbox), priv->low_image, FALSE, FALSE, 0); - gtk_widget_show (priv->low_image); - - gtk_box_pack_start (GTK_BOX (sbox), priv->image, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (sbox), priv->label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, TRUE, 0); - - bar->priv->end_box = ebox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, FALSE, 0); + bar->priv->scale_box = gtk_hbox_new (FALSE, 6); + bar->priv->start_box = gtk_hbox_new (FALSE, 6); + bar->priv->end_box = gtk_hbox_new (FALSE, 6); + bar->priv->scale = gtk_hscale_new (bar->priv->adjustment); + } +#endif + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + gtk_widget_set_size_request (bar->priv->scale, -1, SCALE_SIZE); + + gtk_range_set_inverted (GTK_RANGE (bar->priv->scale), TRUE); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->start_box, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->label, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->high_image, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->scale, + TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->end_box, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->low_image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->mute_box, + FALSE, FALSE, 0); + } else { + gtk_widget_set_size_request (bar->priv->scale, SCALE_SIZE, -1); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->start_box, + FALSE, FALSE, 0); + + gtk_box_pack_end (GTK_BOX (bar->priv->start_box), + bar->priv->low_image, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->start_box), + bar->priv->label, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->scale, + TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->scale_box), + bar->priv->end_box, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->high_image, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (bar->priv->end_box), + bar->priv->mute_box, + FALSE, FALSE, 0); + } - gtk_box_pack_start (GTK_BOX (ebox), priv->high_image, FALSE, FALSE, 0); - gtk_widget_show (priv->high_image); - gtk_box_pack_start (GTK_BOX (ebox), priv->mute_box, FALSE, FALSE, 0); + if (bar->priv->show_icons) { + gtk_widget_show (bar->priv->low_image); + gtk_widget_show (bar->priv->high_image); + } else { + gtk_widget_hide (bar->priv->low_image); + gtk_widget_hide (bar->priv->high_image); } -#if !GTK_CHECK_VERSION (3, 0, 0) - gtk_range_set_update_policy (GTK_RANGE (priv->scale), GTK_UPDATE_CONTINUOUS); -#endif ca_gtk_widget_disable_sounds (bar->priv->scale, FALSE); + gtk_widget_add_events (bar->priv->scale, GDK_SCROLL_MASK); - g_signal_connect (G_OBJECT (bar->priv->scale), "button-press-event", - G_CALLBACK (on_scale_button_press_event), bar); - g_signal_connect (G_OBJECT (bar->priv->scale), "button-release-event", - G_CALLBACK (on_scale_button_release_event), bar); - g_signal_connect (G_OBJECT (bar->priv->scale), "scroll-event", - G_CALLBACK (on_scale_scroll_event), bar); + g_signal_connect (G_OBJECT (bar->priv->scale), + "button-press-event", + G_CALLBACK (on_scale_button_press_event), + bar); + g_signal_connect (G_OBJECT (bar->priv->scale), + "button-release-event", + G_CALLBACK (on_scale_button_release_event), + bar); + g_signal_connect (G_OBJECT (bar->priv->scale), + "scroll-event", + G_CALLBACK (on_scale_scroll_event), + bar); if (bar->priv->size_group != NULL) { - gtk_size_group_add_widget (bar->priv->size_group, sbox); + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->start_box); - if (bar->priv->symmetric) { - gtk_size_group_add_widget (bar->priv->size_group, ebox); - } + if (bar->priv->symmetric) + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->end_box); } - gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE); - - return box; + gtk_scale_set_draw_value (GTK_SCALE (bar->priv->scale), FALSE); } static void -update_image (GvcChannelBar *bar) +on_adjustment_value_changed (GtkAdjustment *adjustment, + GvcChannelBar *bar) { - gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->image), - bar->priv->icon_name, - GTK_ICON_SIZE_DIALOG); + gdouble value; + gdouble lower; - if (bar->priv->icon_name != NULL) { - gtk_widget_show (bar->priv->image); - } else { - gtk_widget_hide (bar->priv->image); - } -} + if (bar->priv->stream == NULL || bar->priv->click_lock == TRUE) + return; -static void -update_label (GvcChannelBar *bar) -{ - if (bar->priv->name != NULL) { - gtk_label_set_text_with_mnemonic (GTK_LABEL (bar->priv->label), - bar->priv->name); - gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label), - bar->priv->scale); - gtk_widget_show (bar->priv->label); - } else { - gtk_label_set_text (GTK_LABEL (bar->priv->label), NULL); - gtk_widget_hide (bar->priv->label); - } + value = gtk_adjustment_get_value (bar->priv->adjustment); + lower = gtk_adjustment_get_lower (bar->priv->adjustment); + + if (bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_MUTE) + mate_mixer_stream_set_mute (bar->priv->stream, (value <= lower)); + + if (bar->priv->stream_flags & MATE_MIXER_STREAM_CAN_SET_VOLUME) + mate_mixer_stream_set_volume (bar->priv->stream, (guint) value); } static void update_layout (GvcChannelBar *bar) { - GtkWidget *box; GtkWidget *frame; - if (bar->priv->scale == NULL) { + if (bar->priv->scale == NULL) return; - } - box = bar->priv->scale_box; - frame = gtk_widget_get_parent (box); + frame = gtk_widget_get_parent (bar->priv->scale_box); g_object_ref (bar->priv->image); g_object_ref (bar->priv->label); @@ -230,24 +255,36 @@ update_layout (GvcChannelBar *bar) g_object_ref (bar->priv->low_image); g_object_ref (bar->priv->high_image); - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), bar->priv->image); - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), bar->priv->label); - gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), bar->priv->mute_box); + // XXX this is not the opposite of what is done above + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->image); + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->label); + gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), + bar->priv->mute_box); if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), bar->priv->low_image); - gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), bar->priv->high_image); + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->low_image); + gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), + bar->priv->high_image); } else { - gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), bar->priv->low_image); - gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), bar->priv->high_image); + gtk_container_remove (GTK_CONTAINER (bar->priv->end_box), + bar->priv->low_image); + gtk_container_remove (GTK_CONTAINER (bar->priv->start_box), + bar->priv->high_image); } - gtk_container_remove (GTK_CONTAINER (box), bar->priv->start_box); - gtk_container_remove (GTK_CONTAINER (box), bar->priv->scale); - gtk_container_remove (GTK_CONTAINER (box), bar->priv->end_box); - gtk_container_remove (GTK_CONTAINER (frame), box); + gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), + bar->priv->start_box); + gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), + bar->priv->scale); + gtk_container_remove (GTK_CONTAINER (bar->priv->scale_box), + bar->priv->end_box); + gtk_container_remove (GTK_CONTAINER (frame), + bar->priv->scale_box); - bar->priv->scale_box = _scale_box_new (bar); + create_scale_box (bar); gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); g_object_unref (bar->priv->image); @@ -259,122 +296,144 @@ update_layout (GvcChannelBar *bar) gtk_widget_show_all (frame); } -void -gvc_channel_bar_set_size_group (GvcChannelBar *bar, - GtkSizeGroup *group, - gboolean symmetric) +static void +update_marks (GvcChannelBar *bar) { - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + gdouble base; + gdouble normal; + gboolean has_mark = FALSE; - bar->priv->size_group = group; - bar->priv->symmetric = symmetric; - - if (bar->priv->size_group != NULL) { - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->start_box); + gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale)); - if (bar->priv->symmetric) { - gtk_size_group_add_widget (bar->priv->size_group, - bar->priv->end_box); - } - } - gtk_widget_queue_draw (GTK_WIDGET (bar)); -} + if (bar->priv->stream == NULL || bar->priv->show_marks == FALSE) + return; + if (!(bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_VOLUME)) + return; -void -gvc_channel_bar_set_name (GvcChannelBar *bar, - const char *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + /* Base volume represents unamplified volume, normal volume is the 100% + * volume, in many cases they are the same as unamplified volume is unknown */ + base = mate_mixer_stream_get_base_volume (bar->priv->stream); + normal = mate_mixer_stream_get_normal_volume (bar->priv->stream); - g_free (bar->priv->name); - bar->priv->name = g_strdup (name); - update_label (bar); - g_object_notify (G_OBJECT (bar), "name"); -} + if (normal <= gtk_adjustment_get_lower (bar->priv->adjustment)) + return; -void -gvc_channel_bar_set_icon_name (GvcChannelBar *bar, - const char *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + if (base < normal) { + gchar *str = g_strdup_printf ("<small>%s</small>", C_("volume", "Unamplified")); - g_free (bar->priv->icon_name); - bar->priv->icon_name = g_strdup (name); - update_image (bar); - g_object_notify (G_OBJECT (bar), "icon-name"); -} + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + base, + GTK_POS_BOTTOM, + str); + has_mark = TRUE; + g_free (str); + } -void -gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, - const char *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + /* Only show 100% mark if the scale is extended beyond 100% and + * there is no unamplified mark or it is below the normal volume */ + if (bar->priv->extended && (base == normal || base < normal)) { + gchar *str = g_strdup_printf ("<small>%s</small>", C_("volume", "100%")); - if (name != NULL && strcmp (bar->priv->low_icon_name, name) != 0) { - g_free (bar->priv->low_icon_name); - bar->priv->low_icon_name = g_strdup (name); - gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->low_image), - bar->priv->low_icon_name, - GTK_ICON_SIZE_BUTTON); - g_object_notify (G_OBJECT (bar), "low-icon-name"); + gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), + normal, + GTK_POS_BOTTOM, + str); + has_mark = TRUE; + g_free (str); } -} -void -gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, - const char *name) -{ - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + if (has_mark) { + gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0, 0, 0); + + gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0); + gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0); + gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0); + } else { + gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0.5, 0, 0); - if (name != NULL && strcmp (bar->priv->high_icon_name, name) != 0) { - g_free (bar->priv->high_icon_name); - bar->priv->high_icon_name = g_strdup (name); - gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->high_image), - bar->priv->high_icon_name, - GTK_ICON_SIZE_BUTTON); - g_object_notify (G_OBJECT (bar), "high-icon-name"); + gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0.5); + gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0.5); + gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0.5); } } -void -gvc_channel_bar_set_orientation (GvcChannelBar *bar, - GtkOrientation orientation) +static void +update_adjustment_value (GvcChannelBar *bar) { - g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + gdouble value; + gboolean set_lower = FALSE; + + /* Move the slider to the minimal value if the stream is muted or + * volume is unavailable */ + if (!(bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_VOLUME)) + set_lower = TRUE; + else if (bar->priv->stream == NULL || + bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_MUTE) + set_lower = mate_mixer_stream_get_mute (bar->priv->stream); + + if (set_lower) + value = gtk_adjustment_get_lower (bar->priv->adjustment); + else + value = mate_mixer_stream_get_volume (bar->priv->stream); + + g_signal_handlers_block_by_func (G_OBJECT (bar->priv->adjustment), + on_adjustment_value_changed, + bar); - if (orientation != bar->priv->orientation) { - bar->priv->orientation = orientation; - update_layout (bar); - g_object_notify (G_OBJECT (bar), "orientation"); - } + gtk_adjustment_set_value (bar->priv->adjustment, value); + + g_signal_handlers_unblock_by_func (G_OBJECT (bar->priv->adjustment), + on_adjustment_value_changed, + bar); } static void -gvc_channel_bar_set_adjustment (GvcChannelBar *bar, - GtkAdjustment *adjustment) +update_adjustment_limits (GvcChannelBar *bar) { - g_return_if_fail (GVC_CHANNEL_BAR (bar)); - g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + gdouble minimum = 0.0; + gdouble maximum = 0.0; - if (bar->priv->adjustment != NULL) { - g_object_unref (bar->priv->adjustment); - } - bar->priv->adjustment = g_object_ref_sink (adjustment); - - if (bar->priv->scale != NULL) { - gtk_range_set_adjustment (GTK_RANGE (bar->priv->scale), adjustment); + if (bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_VOLUME) { + minimum = mate_mixer_stream_get_min_volume (bar->priv->stream); + if (bar->priv->extended) + maximum = mate_mixer_stream_get_max_volume (bar->priv->stream); + else + maximum = mate_mixer_stream_get_normal_volume (bar->priv->stream); } - g_object_notify (G_OBJECT (bar), "adjustment"); + gtk_adjustment_configure (bar->priv->adjustment, + gtk_adjustment_get_value (bar->priv->adjustment), + minimum, + maximum, + (maximum - minimum) / 100.0, + (maximum - minimum) / 15.0, + 0.0); } -GtkAdjustment * -gvc_channel_bar_get_adjustment (GvcChannelBar *bar) +static void +update_mute_button (GvcChannelBar *bar) { - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + if (bar->priv->show_mute == TRUE) { + gboolean enable = FALSE; + + if (bar->priv->stream != NULL && + bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_MUTE) + enable = TRUE; - return bar->priv->adjustment; + if (enable == TRUE) { + gboolean mute = mate_mixer_stream_get_mute (bar->priv->stream); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), + mute); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), + FALSE); + } + + gtk_widget_set_sensitive (bar->priv->mute_button, enable); + gtk_widget_show (bar->priv->mute_button); + } else + gtk_widget_hide (bar->priv->mute_button); } static gboolean @@ -382,16 +441,29 @@ on_scale_button_press_event (GtkWidget *widget, GdkEventButton *event, GvcChannelBar *bar) { - /* HACK: we want the behaviour you get with the middle button, so we - * mangle the event. clicking with other buttons moves the slider in - * step increments, clicking with the middle button moves the slider to - * the location of the click. - */ + +#if !GTK_CHECK_VERSION (3, 6, 0) + /* Up to GTK 3.4 the slider selection only moves in increments when + * clicking in the slider with the left button and it moves directly + * to the clicked position with the middle mouse button. + * Change this behaviour to also jump to the clicked position with the + * left mouse button. */ if (event->button == 1) event->button = 2; +#endif - bar->priv->click_lock = TRUE; + /* Muting the stream when volume is non-zero moves the slider to zero, + * but the volume remains the same. In this case delay unmuting and + * changing volume until user releases the mouse button. */ + if (bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_MUTE && + bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_VOLUME) { + if (mate_mixer_stream_get_mute (bar->priv->stream) == TRUE) { + guint minimum = (guint) gtk_adjustment_get_lower (bar->priv->adjustment); + if (mate_mixer_stream_get_volume (bar->priv->stream) > minimum) + bar->priv->click_lock = TRUE; + } + } return FALSE; } @@ -400,246 +472,422 @@ on_scale_button_release_event (GtkWidget *widget, GdkEventButton *event, GvcChannelBar *bar) { - GtkAdjustment *adj; - gdouble value; - - /* HACK: see on_scale_button_press_event() */ +#if !GTK_CHECK_VERSION (3, 6, 0) if (event->button == 1) event->button = 2; +#endif - bar->priv->click_lock = FALSE; - - adj = gtk_range_get_adjustment (GTK_RANGE (widget)); - - value = gtk_adjustment_get_value (adj); - - /* this means the adjustment moved away from zero and - * therefore we should unmute and set the volume. */ - gvc_channel_bar_set_is_muted (bar, (value == 0.0)); + if (bar->priv->click_lock == TRUE) { + /* The volume change is not reflected while the lock is + * held, propagate the change now that user has released + * the mouse button */ + bar->priv->click_lock = FALSE; + on_adjustment_value_changed (bar->priv->adjustment, bar); + } - /* Play a sound! */ + /* Play a sound */ ca_gtk_play_for_widget (GTK_WIDGET (bar), 0, CA_PROP_EVENT_ID, "audio-volume-change", - CA_PROP_EVENT_DESCRIPTION, "foobar event happened", + CA_PROP_EVENT_DESCRIPTION, "Volume change", CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", + CA_PROP_APPLICATION_NAME, _("Volume Control"), + CA_PROP_APPLICATION_VERSION, VERSION, + CA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control", NULL); - return FALSE; } -gboolean -gvc_channel_bar_scroll (GvcChannelBar *bar, GdkScrollDirection direction) +static gboolean +on_scale_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GvcChannelBar *bar) { - GtkAdjustment *adj; - gdouble value; + GdkScrollDirection direction = event->direction; - g_return_val_if_fail (bar != NULL, FALSE); - g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); +#if GTK_CHECK_VERSION (3, 4, 0) + if (direction == GDK_SCROLL_SMOOTH) { + gdouble dx = 0.0; + gdouble dy = 0.0; - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { - if (direction != GDK_SCROLL_UP && direction != GDK_SCROLL_DOWN) - return FALSE; - } else { - /* Switch direction for RTL */ - if (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_RTL) { - if (direction == GDK_SCROLL_RIGHT) - direction = GDK_SCROLL_LEFT; - else if (direction == GDK_SCROLL_LEFT) - direction = GDK_SCROLL_RIGHT; - } - /* Switch side scroll to vertical */ - if (direction == GDK_SCROLL_RIGHT) - direction = GDK_SCROLL_UP; - else if (direction == GDK_SCROLL_LEFT) + gdk_event_get_scroll_deltas ((const GdkEvent *) event, &dx, &dy); + if (dy > 0.0) direction = GDK_SCROLL_DOWN; + else if (dy < 0.0) + direction = GDK_SCROLL_UP; + else + return FALSE; } +#endif + return gvc_channel_bar_scroll (bar, direction); +} - adj = gtk_range_get_adjustment (GTK_RANGE (bar->priv->scale)); - if (adj == bar->priv->zero_adjustment) { - if (direction == GDK_SCROLL_UP) - gvc_channel_bar_set_is_muted (bar, FALSE); - return TRUE; - } +static void +on_stream_volume_notify (MateMixerStream *stream, + GParamSpec *pspec, + GvcChannelBar *bar) +{ + update_adjustment_value (bar); +} - value = gtk_adjustment_get_value (adj); +static void +on_stream_mute_notify (MateMixerStream *stream, + GParamSpec *pspec, + GvcChannelBar *bar) +{ + if (bar->priv->show_mute == TRUE) { + gboolean mute = mate_mixer_stream_get_mute (stream); - if (direction == GDK_SCROLL_UP) { - if (value + SCROLLSTEP > ADJUSTMENT_MAX) - value = ADJUSTMENT_MAX; - else - value = value + SCROLLSTEP; - } else if (direction == GDK_SCROLL_DOWN) { - if (value - SCROLLSTEP < 0) - value = 0.0; - else - value = value - SCROLLSTEP; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), mute); } + update_adjustment_value (bar); +} - gvc_channel_bar_set_is_muted (bar, (value == 0.0)); - adj = gtk_range_get_adjustment (GTK_RANGE (bar->priv->scale)); - gtk_adjustment_set_value (adj, value); +MateMixerStream * +gvc_channel_bar_get_stream (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); - return TRUE; + return bar->priv->stream; } -static gboolean -on_scale_scroll_event (GtkWidget *widget, - GdkEventScroll *event, - GvcChannelBar *bar) +void +gvc_channel_bar_set_stream (GvcChannelBar *bar, MateMixerStream *stream) { - return gvc_channel_bar_scroll (bar, event->direction); + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (bar->priv->stream == stream) + return; + + if (stream != NULL) + g_object_ref (stream); + + if (bar->priv->stream != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->stream), + G_CALLBACK (on_stream_volume_notify), + bar); + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->stream), + G_CALLBACK (on_stream_mute_notify), + bar); + g_object_unref (bar->priv->stream); + } + + bar->priv->stream = stream; + + if (stream != NULL) + bar->priv->stream_flags = mate_mixer_stream_get_flags (stream); + else + bar->priv->stream_flags = MATE_MIXER_STREAM_NO_FLAGS; + + if (bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_VOLUME) + g_signal_connect (G_OBJECT (stream), + "notify::volume", + G_CALLBACK (on_stream_volume_notify), + bar); + if (bar->priv->stream_flags & MATE_MIXER_STREAM_HAS_MUTE) + g_signal_connect (G_OBJECT (stream), + "notify::mute", + G_CALLBACK (on_stream_mute_notify), + bar); + + update_marks (bar); + update_mute_button (bar); + update_adjustment_limits (bar); + update_adjustment_value (bar); } -static void -on_zero_adjustment_value_changed (GtkAdjustment *adjustment, - GvcChannelBar *bar) +GtkOrientation +gvc_channel_bar_get_orientation (GvcChannelBar *bar) { - gdouble value; + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), 0); - if (bar->priv->click_lock != FALSE) { + return bar->priv->orientation; +} + +void +gvc_channel_bar_set_orientation (GvcChannelBar *bar, + GtkOrientation orientation) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (orientation == bar->priv->orientation) return; - } - value = gtk_adjustment_get_value (bar->priv->zero_adjustment); - gtk_adjustment_set_value (bar->priv->adjustment, value); + bar->priv->orientation = orientation; + update_layout (bar); + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]); +} - if (bar->priv->show_mute == FALSE) { - /* this means the adjustment moved away from zero and - * therefore we should unmute and set the volume. */ - gvc_channel_bar_set_is_muted (bar, value > 0.0); - } +gboolean +gvc_channel_bar_get_show_icons (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + return bar->priv->show_icons; } -static void -update_mute_button (GvcChannelBar *bar) +void +gvc_channel_bar_set_show_icons (GvcChannelBar *bar, gboolean show_icons) { - if (bar->priv->show_mute) { - gtk_widget_show (bar->priv->mute_button); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button), - bar->priv->is_muted); - } else { - gtk_widget_hide (bar->priv->mute_button); + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - if (bar->priv->is_muted) { - /* If we aren't showing the mute button then - * move slider to the zero. But we don't want to - * change the adjustment. */ - g_signal_handlers_block_by_func (bar->priv->zero_adjustment, - on_zero_adjustment_value_changed, - bar); - gtk_adjustment_set_value (bar->priv->zero_adjustment, 0); - g_signal_handlers_unblock_by_func (bar->priv->zero_adjustment, - on_zero_adjustment_value_changed, - bar); - gtk_range_set_adjustment (GTK_RANGE (bar->priv->scale), - bar->priv->zero_adjustment); - } else { - /* no longer muted so restore the original adjustment - * and tell the front-end that the value changed */ - gtk_range_set_adjustment (GTK_RANGE (bar->priv->scale), - bar->priv->adjustment); - gtk_adjustment_value_changed (bar->priv->adjustment); - } + if (show_icons == bar->priv->show_icons) + return; + + bar->priv->show_icons = show_icons; + + if (bar->priv->show_icons == TRUE) { + gtk_widget_show (bar->priv->low_image); + gtk_widget_show (bar->priv->high_image); + } else { + gtk_widget_hide (bar->priv->low_image); + gtk_widget_hide (bar->priv->high_image); } + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_ICONS]); +} + +gboolean +gvc_channel_bar_get_show_mute (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + return bar->priv->show_mute; } void -gvc_channel_bar_set_is_muted (GvcChannelBar *bar, - gboolean is_muted) +gvc_channel_bar_set_show_mute (GvcChannelBar *bar, gboolean show_mute) { g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - if (is_muted != bar->priv->is_muted) { - /* Update our internal state before telling the - * front-end about our changes */ - bar->priv->is_muted = is_muted; - update_mute_button (bar); - g_object_notify (G_OBJECT (bar), "is-muted"); - } + if (show_mute == bar->priv->show_mute) + return; + + bar->priv->show_mute = show_mute; + update_mute_button (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MUTE]); } gboolean -gvc_channel_bar_get_is_muted (GvcChannelBar *bar) +gvc_channel_bar_get_show_marks (GvcChannelBar *bar) { g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); - return bar->priv->is_muted; + + return bar->priv->show_marks; } void -gvc_channel_bar_set_show_mute (GvcChannelBar *bar, - gboolean show_mute) +gvc_channel_bar_set_show_marks (GvcChannelBar *bar, gboolean show_marks) { g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - if (show_mute != bar->priv->show_mute) { - bar->priv->show_mute = show_mute; - g_object_notify (G_OBJECT (bar), "show-mute"); - update_mute_button (bar); - } + if (show_marks == bar->priv->show_marks) + return; + + bar->priv->show_marks = show_marks; + update_marks (bar); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SHOW_MARKS]); } gboolean -gvc_channel_bar_get_show_mute (GvcChannelBar *bar) +gvc_channel_bar_get_extended (GvcChannelBar *bar) { g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); - return bar->priv->show_mute; + + return bar->priv->extended; } void -gvc_channel_bar_set_is_amplified (GvcChannelBar *bar, gboolean amplified) +gvc_channel_bar_set_extended (GvcChannelBar *bar, gboolean extended) { g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - bar->priv->is_amplified = amplified; - gtk_adjustment_set_upper (bar->priv->adjustment, ADJUSTMENT_MAX); - gtk_adjustment_set_upper (bar->priv->zero_adjustment, ADJUSTMENT_MAX); - gtk_scale_clear_marks (GTK_SCALE (bar->priv->scale)); + if (extended == bar->priv->extended) + return; - if (amplified) { - char *str; + bar->priv->extended = extended; - if (bar->priv->base_volume == ADJUSTMENT_MAX_NORMAL) { - str = g_strdup_printf ("<small>%s</small>", C_("volume", "100%")); - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), ADJUSTMENT_MAX_NORMAL, - GTK_POS_BOTTOM, str); - } else { - str = g_strdup_printf ("<small>%s</small>", C_("volume", "Unamplified")); - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), bar->priv->base_volume, - GTK_POS_BOTTOM, str); - /* Only show 100% if it's higher than the base volume */ - if (bar->priv->base_volume < ADJUSTMENT_MAX_NORMAL) { - str = g_strdup_printf ("<small>%s</small>", C_("volume", "100%")); - gtk_scale_add_mark (GTK_SCALE (bar->priv->scale), ADJUSTMENT_MAX_NORMAL, - GTK_POS_BOTTOM, str); - } - } + /* Update displayed marks as non-extended scales do not show the 100% + * limit at the end of the scale */ + update_marks (bar); + update_adjustment_limits (bar); - g_free (str); - gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0, 0, 0); - gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0); - gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0); - gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0); + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_EXTENDED]); +} + +const gchar * +gvc_channel_bar_get_name (GvcChannelBar *bar) +{ + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + return gtk_label_get_text (GTK_LABEL (bar->priv->label)); +} + +void +gvc_channel_bar_set_name (GvcChannelBar *bar, const gchar *name) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + if (name != NULL) { + gtk_label_set_text_with_mnemonic (GTK_LABEL (bar->priv->label), name); + gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label), + bar->priv->scale); + + gtk_widget_show (bar->priv->label); } else { - gtk_alignment_set (GTK_ALIGNMENT (bar->priv->mute_box), 0.5, 0.5, 0, 0); - gtk_misc_set_alignment (GTK_MISC (bar->priv->low_image), 0.5, 0.5); - gtk_misc_set_alignment (GTK_MISC (bar->priv->high_image), 0.5, 0.5); - gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0, 0.5); + gtk_label_set_text (GTK_LABEL (bar->priv->label), NULL); + gtk_widget_hide (bar->priv->label); } + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_NAME]); +} + +const gchar * +gvc_channel_bar_get_icon_name (GvcChannelBar *bar) +{ + const gchar *name = NULL; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + gtk_image_get_icon_name (GTK_IMAGE (bar->priv->image), &name, NULL); + return name; } void -gvc_channel_bar_set_base_volume (GvcChannelBar *bar, guint base_volume) +gvc_channel_bar_set_icon_name (GvcChannelBar *bar, const gchar *name) { g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); - if (base_volume == 0) { - bar->priv->base_volume = ADJUSTMENT_MAX_NORMAL; - return; + gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->image), + name, + GTK_ICON_SIZE_DIALOG); + if (name != NULL) + gtk_widget_show (bar->priv->image); + else + gtk_widget_hide (bar->priv->image); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ICON_NAME]); +} + +const gchar * +gvc_channel_bar_get_low_icon_name (GvcChannelBar *bar) +{ + const gchar *name = NULL; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + gtk_image_get_icon_name (GTK_IMAGE (bar->priv->low_image), &name, NULL); + return name; +} + +void +gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, const gchar *name) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->low_image), + name, + GTK_ICON_SIZE_BUTTON); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_LOW_ICON_NAME]); +} + +const gchar * +gvc_channel_bar_get_high_icon_name (GvcChannelBar *bar) +{ + const gchar *name = NULL; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL); + + gtk_image_get_icon_name (GTK_IMAGE (bar->priv->high_image), &name, NULL); + return name; +} + +void +gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, const gchar *name) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + + gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->high_image), + name, + GTK_ICON_SIZE_BUTTON); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_HIGH_ICON_NAME]); +} + +gboolean +gvc_channel_bar_scroll (GvcChannelBar *bar, GdkScrollDirection direction) +{ + gdouble value; + gdouble minimum; + gdouble maximum; + gdouble scrollstep; + + g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE); + + if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { + if (direction != GDK_SCROLL_UP && direction != GDK_SCROLL_DOWN) + return FALSE; + } else { + /* Switch direction for RTL */ + if (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_RTL) { + if (direction == GDK_SCROLL_RIGHT) + direction = GDK_SCROLL_LEFT; + else if (direction == GDK_SCROLL_LEFT) + direction = GDK_SCROLL_RIGHT; + } + + /* Switch side scroll to vertical */ + if (direction == GDK_SCROLL_RIGHT) + direction = GDK_SCROLL_UP; + else if (direction == GDK_SCROLL_LEFT) + direction = GDK_SCROLL_DOWN; + } + + value = gtk_adjustment_get_value (bar->priv->adjustment); + minimum = gtk_adjustment_get_lower (bar->priv->adjustment); + maximum = gtk_adjustment_get_upper (bar->priv->adjustment); + + scrollstep = maximum / 100.0 * 5.0; + + if (direction == GDK_SCROLL_UP) { + if (value + scrollstep > maximum) + value = maximum; + else + value = value + scrollstep; + } else if (direction == GDK_SCROLL_DOWN) { + if (value - scrollstep < minimum) + value = minimum; + else + value = value - scrollstep; } - /* Note that you need to call _is_amplified() afterwards to update the marks */ - bar->priv->base_volume = base_volume; + gtk_adjustment_set_value (bar->priv->adjustment, value); + return TRUE; +} + +void +gvc_channel_bar_set_size_group (GvcChannelBar *bar, + GtkSizeGroup *group, + gboolean symmetric) +{ + g_return_if_fail (GVC_IS_CHANNEL_BAR (bar)); + g_return_if_fail (GTK_IS_SIZE_GROUP (group)); + + bar->priv->size_group = group; + bar->priv->symmetric = symmetric; + + if (bar->priv->size_group != NULL) { + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->start_box); + + if (bar->priv->symmetric) + gtk_size_group_add_widget (bar->priv->size_group, + bar->priv->end_box); + } + gtk_widget_queue_draw (GTK_WIDGET (bar)); } static void @@ -651,15 +899,24 @@ gvc_channel_bar_set_property (GObject *object, GvcChannelBar *self = GVC_CHANNEL_BAR (object); switch (prop_id) { + case PROP_STREAM: + gvc_channel_bar_set_stream (self, g_value_get_object (value)); + break; case PROP_ORIENTATION: gvc_channel_bar_set_orientation (self, g_value_get_enum (value)); break; - case PROP_IS_MUTED: - gvc_channel_bar_set_is_muted (self, g_value_get_boolean (value)); - break; case PROP_SHOW_MUTE: gvc_channel_bar_set_show_mute (self, g_value_get_boolean (value)); break; + case PROP_SHOW_ICONS: + gvc_channel_bar_set_show_icons (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_MARKS: + gvc_channel_bar_set_show_marks (self, g_value_get_boolean (value)); + break; + case PROP_EXTENDED: + gvc_channel_bar_set_extended (self, g_value_get_boolean (value)); + break; case PROP_NAME: gvc_channel_bar_set_name (self, g_value_get_string (value)); break; @@ -672,12 +929,6 @@ gvc_channel_bar_set_property (GObject *object, case PROP_HIGH_ICON_NAME: gvc_channel_bar_set_high_icon_name (self, g_value_get_string (value)); break; - case PROP_ADJUSTMENT: - gvc_channel_bar_set_adjustment (self, g_value_get_object (value)); - break; - case PROP_IS_AMPLIFIED: - gvc_channel_bar_set_is_amplified (self, g_value_get_boolean (value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -691,35 +942,28 @@ gvc_channel_bar_get_property (GObject *object, GParamSpec *pspec) { GvcChannelBar *self = GVC_CHANNEL_BAR (object); - GvcChannelBarPrivate *priv = self->priv; switch (prop_id) { - case PROP_ORIENTATION: - g_value_set_enum (value, priv->orientation); + case PROP_STREAM: + g_value_set_object (value, self->priv->stream); break; - case PROP_IS_MUTED: - g_value_set_boolean (value, priv->is_muted); + case PROP_ORIENTATION: + g_value_set_enum (value, self->priv->orientation); break; case PROP_SHOW_MUTE: - g_value_set_boolean (value, priv->show_mute); - break; - case PROP_NAME: - g_value_set_string (value, priv->name); + g_value_set_boolean (value, self->priv->show_mute); break; - case PROP_ICON_NAME: - g_value_set_string (value, priv->icon_name); - break; - case PROP_LOW_ICON_NAME: - g_value_set_string (value, priv->low_icon_name); + case PROP_SHOW_ICONS: + g_value_set_boolean (value, self->priv->show_icons); break; - case PROP_HIGH_ICON_NAME: - g_value_set_string (value, priv->high_icon_name); + case PROP_SHOW_MARKS: + g_value_set_boolean (value, self->priv->show_marks); break; - case PROP_ADJUSTMENT: - g_value_set_object (value, gvc_channel_bar_get_adjustment (self)); + case PROP_EXTENDED: + g_value_set_boolean (value, self->priv->extended); break; - case PROP_IS_AMPLIFIED: - g_value_set_boolean (value, priv->is_amplified); + case PROP_NAME: + g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -727,109 +971,119 @@ gvc_channel_bar_get_property (GObject *object, } } -static GObject * -gvc_channel_bar_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_params) -{ - GObject *object; - GvcChannelBar *self; - - object = G_OBJECT_CLASS (gvc_channel_bar_parent_class)->constructor (type, n_construct_properties, construct_params); - - self = GVC_CHANNEL_BAR (object); - - update_mute_button (self); - - return object; -} - static void gvc_channel_bar_class_init (GvcChannelBarClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->constructor = gvc_channel_bar_constructor; - object_class->finalize = gvc_channel_bar_finalize; object_class->set_property = gvc_channel_bar_set_property; object_class->get_property = gvc_channel_bar_get_property; - g_object_class_install_property (object_class, - PROP_ORIENTATION, - g_param_spec_enum ("orientation", - "Orientation", - "The orientation of the scale", - GTK_TYPE_ORIENTATION, - GTK_ORIENTATION_VERTICAL, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_IS_MUTED, - g_param_spec_boolean ("is-muted", - "is muted", - "Whether stream is muted", - FALSE, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_SHOW_MUTE, - g_param_spec_boolean ("show-mute", - "show mute", - "Whether stream is muted", - FALSE, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_ADJUSTMENT, - g_param_spec_object ("adjustment", - "Adjustment", - "The GtkAdjustment that contains the current value of this scale button object", - GTK_TYPE_ADJUSTMENT, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_NAME, - g_param_spec_string ("name", - "Name", - "Name to display for this stream", - NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_ICON_NAME, - g_param_spec_string ("icon-name", - "Icon Name", - "Name of icon to display for this stream", - NULL, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_LOW_ICON_NAME, - g_param_spec_string ("low-icon-name", - "Icon Name", - "Name of icon to display for this stream", - "audio-volume-low", - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_HIGH_ICON_NAME, - g_param_spec_string ("high-icon-name", - "Icon Name", - "Name of icon to display for this stream", - "audio-volume-high", - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_IS_AMPLIFIED, - g_param_spec_boolean ("is-amplified", - "is amplified", - "Whether the stream is digitally amplified", - FALSE, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + properties[PROP_STREAM] = + g_param_spec_object ("stream", + "Stream", + "MateMixer stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ORIENTATION] = + g_param_spec_enum ("orientation", + "Orientation", + "The orientation of the scale", + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_VERTICAL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_MUTE] = + g_param_spec_boolean ("show-mute", + "show mute", + "Whether stream is muted", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_ICONS] = + g_param_spec_boolean ("show-icons", + "show mute", + "Whether to show low and high icons", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SHOW_MARKS] = + g_param_spec_boolean ("show-marks", + "Show marks", + "Whether to show scale marks", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_EXTENDED] = + g_param_spec_boolean ("extended", + "Extended", + "Allow the scale to be extended above normal volume", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name to display for this stream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ICON_NAME] = + g_param_spec_string ("icon-name", + "Icon name", + "Name of icon to display for this stream", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_LOW_ICON_NAME] = + g_param_spec_string ("low-icon-name", + "Low icon name", + "Name of low volume icon to display for this stream", + "audio-volume-low", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_HIGH_ICON_NAME] = + g_param_spec_string ("high-icon-name", + "High icon name", + "Name of high volume icon to display for this stream", + "audio-volume-high", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); g_type_class_add_private (klass, sizeof (GvcChannelBarPrivate)); } static void -on_mute_button_toggled (GtkToggleButton *button, - GvcChannelBar *bar) +on_mute_button_toggled (GtkToggleButton *button, GvcChannelBar *bar) { - gboolean is_muted; - is_muted = gtk_toggle_button_get_active (button); - gvc_channel_bar_set_is_muted (bar, is_muted); + gboolean mute; + + if (G_UNLIKELY (bar->priv->stream == NULL)) + g_warn_if_reached (); + + mute = gtk_toggle_button_get_active (button); + + mate_mixer_stream_set_mute (bar->priv->stream, mute); } static void @@ -839,89 +1093,64 @@ gvc_channel_bar_init (GvcChannelBar *bar) bar->priv = GVC_CHANNEL_BAR_GET_PRIVATE (bar); - bar->priv->base_volume = ADJUSTMENT_MAX_NORMAL; - bar->priv->low_icon_name = g_strdup ("audio-volume-low"); - bar->priv->high_icon_name = g_strdup ("audio-volume-high"); - - bar->priv->orientation = GTK_ORIENTATION_VERTICAL; - bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, - 0.0, - ADJUSTMENT_MAX_NORMAL, - ADJUSTMENT_MAX_NORMAL/100.0, - ADJUSTMENT_MAX_NORMAL/10.0, - 0.0)); - g_object_ref_sink (bar->priv->adjustment); - - bar->priv->zero_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, - 0.0, - ADJUSTMENT_MAX_NORMAL, - ADJUSTMENT_MAX_NORMAL/100.0, - ADJUSTMENT_MAX_NORMAL/10.0, - 0.0)); - g_object_ref_sink (bar->priv->zero_adjustment); - - g_signal_connect (bar->priv->zero_adjustment, - "value-changed", - G_CALLBACK (on_zero_adjustment_value_changed), - bar); - + /* Mute button */ bar->priv->mute_button = gtk_check_button_new_with_label (_("Mute")); gtk_widget_set_no_show_all (bar->priv->mute_button, TRUE); + g_signal_connect (bar->priv->mute_button, "toggled", G_CALLBACK (on_mute_button_toggled), bar); + bar->priv->mute_box = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_container_add (GTK_CONTAINER (bar->priv->mute_box), bar->priv->mute_button); - bar->priv->low_image = gtk_image_new_from_icon_name ("audio-volume-low", - GTK_ICON_SIZE_BUTTON); - gtk_widget_set_no_show_all (bar->priv->low_image, TRUE); - bar->priv->high_image = gtk_image_new_from_icon_name ("audio-volume-high", - GTK_ICON_SIZE_BUTTON); - gtk_widget_set_no_show_all (bar->priv->high_image, TRUE); - bar->priv->image = gtk_image_new (); gtk_widget_set_no_show_all (bar->priv->image, TRUE); + /* Low/high icons */ + bar->priv->low_image = gtk_image_new (); + gtk_widget_set_no_show_all (bar->priv->low_image, TRUE); + + bar->priv->high_image = gtk_image_new (); + gtk_widget_set_no_show_all (bar->priv->high_image, TRUE); + bar->priv->label = gtk_label_new (NULL); gtk_misc_set_alignment (GTK_MISC (bar->priv->label), 0.0, 0.5); gtk_widget_set_no_show_all (bar->priv->label, TRUE); - /* frame */ + /* Frame */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_container_add (GTK_CONTAINER (bar), frame); - gtk_widget_show_all (frame); + gtk_box_pack_start (GTK_BOX (bar), frame, TRUE, TRUE, 0); - /* box with scale */ - bar->priv->scale_box = _scale_box_new (bar); + gtk_widget_show_all (frame); - gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); -} + /* Create a default adjustment */ + bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 0, 0, 0, 0)); -static void -gvc_channel_bar_finalize (GObject *object) -{ - GvcChannelBar *channel_bar; - - g_return_if_fail (object != NULL); - g_return_if_fail (GVC_IS_CHANNEL_BAR (object)); + g_object_ref_sink (bar->priv->adjustment); - channel_bar = GVC_CHANNEL_BAR (object); + g_signal_connect (bar->priv->adjustment, + "value-changed", + G_CALLBACK (on_adjustment_value_changed), + bar); - g_return_if_fail (channel_bar->priv != NULL); + /* Initially create a vertical scale box */ + bar->priv->orientation = GTK_ORIENTATION_VERTICAL; - g_free (channel_bar->priv->name); - g_free (channel_bar->priv->icon_name); - g_free (channel_bar->priv->low_icon_name); - g_free (channel_bar->priv->high_icon_name); + create_scale_box (bar); - G_OBJECT_CLASS (gvc_channel_bar_parent_class)->finalize (object); + gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box); } GtkWidget * -gvc_channel_bar_new (void) +gvc_channel_bar_new (MateMixerStream *stream) { - return g_object_new (GVC_TYPE_CHANNEL_BAR, NULL); + return g_object_new (GVC_TYPE_CHANNEL_BAR, + "stream", stream, +#if GTK_CHECK_VERSION (3, 0, 0) + "orientation", GTK_ORIENTATION_HORIZONTAL, +#endif + NULL); } diff --git a/mate-volume-control/src/gvc-channel-bar.h b/mate-volume-control/src/gvc-channel-bar.h index d31da50..16d6257 100644 --- a/mate-volume-control/src/gvc-channel-bar.h +++ b/mate-volume-control/src/gvc-channel-bar.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -23,6 +24,9 @@ #include <glib.h> #include <glib-object.h> +#include <gtk/gtk.h> + +#include <libmatemixer/matemixer.h> G_BEGIN_DECLS @@ -33,9 +37,11 @@ G_BEGIN_DECLS #define GVC_IS_CHANNEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_BAR)) #define GVC_CHANNEL_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarClass)) -typedef struct GvcChannelBarPrivate GvcChannelBarPrivate; +typedef struct _GvcChannelBar GvcChannelBar; +typedef struct _GvcChannelBarClass GvcChannelBarClass; +typedef struct _GvcChannelBarPrivate GvcChannelBarPrivate; -typedef struct +struct _GvcChannelBar { #if GTK_CHECK_VERSION (3, 0, 0) GtkBox parent; @@ -43,52 +49,69 @@ typedef struct GtkHBox parent; #endif GvcChannelBarPrivate *priv; -} GvcChannelBar; +}; -typedef struct +struct _GvcChannelBarClass { #if GTK_CHECK_VERSION (3, 0, 0) GtkBoxClass parent_class; #else GtkHBoxClass parent_class; #endif -} GvcChannelBarClass; + + void (* changed) (GvcChannelBar *bar); +}; GType gvc_channel_bar_get_type (void); -GtkWidget * gvc_channel_bar_new (void); - -void gvc_channel_bar_set_name (GvcChannelBar *bar, - const char *name); -void gvc_channel_bar_set_icon_name (GvcChannelBar *bar, - const char *icon_name); -void gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, - const char *icon_name); -void gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, - const char *icon_name); - -void gvc_channel_bar_set_orientation (GvcChannelBar *bar, - GtkOrientation orientation); -GtkOrientation gvc_channel_bar_get_orientation (GvcChannelBar *bar); - -GtkAdjustment * gvc_channel_bar_get_adjustment (GvcChannelBar *bar); - -gboolean gvc_channel_bar_get_is_muted (GvcChannelBar *bar); -void gvc_channel_bar_set_is_muted (GvcChannelBar *bar, - gboolean is_muted); -gboolean gvc_channel_bar_get_show_mute (GvcChannelBar *bar); -void gvc_channel_bar_set_show_mute (GvcChannelBar *bar, - gboolean show_mute); -void gvc_channel_bar_set_size_group (GvcChannelBar *bar, - GtkSizeGroup *group, - gboolean symmetric); -void gvc_channel_bar_set_is_amplified (GvcChannelBar *bar, - gboolean amplified); -void gvc_channel_bar_set_base_volume (GvcChannelBar *bar, - guint32 base_volume); - -gboolean gvc_channel_bar_scroll (GvcChannelBar *bar, - GdkScrollDirection direction); +GtkWidget * gvc_channel_bar_new (MateMixerStream *stream); + +MateMixerStream * gvc_channel_bar_get_stream (GvcChannelBar *bar); +void gvc_channel_bar_set_stream (GvcChannelBar *bar, + MateMixerStream *stream); + +const gchar * gvc_channel_bar_get_name (GvcChannelBar *bar); +void gvc_channel_bar_set_name (GvcChannelBar *bar, + const gchar *name); + +const gchar * gvc_channel_bar_get_icon_name (GvcChannelBar *bar); +void gvc_channel_bar_set_icon_name (GvcChannelBar *bar, + const gchar *icon_name); + +const gchar * gvc_channel_bar_get_low_icon_name (GvcChannelBar *bar); +void gvc_channel_bar_set_low_icon_name (GvcChannelBar *bar, + const gchar *icon_name); + +const gchar * gvc_channel_bar_get_high_icon_name (GvcChannelBar *bar); +void gvc_channel_bar_set_high_icon_name (GvcChannelBar *bar, + const gchar *icon_name); + +GtkOrientation gvc_channel_bar_get_orientation (GvcChannelBar *bar); +void gvc_channel_bar_set_orientation (GvcChannelBar *bar, + GtkOrientation orientation); + +gboolean gvc_channel_bar_get_show_icons (GvcChannelBar *bar); +void gvc_channel_bar_set_show_icons (GvcChannelBar *bar, + gboolean show_mute); + +gboolean gvc_channel_bar_get_show_mute (GvcChannelBar *bar); +void gvc_channel_bar_set_show_mute (GvcChannelBar *bar, + gboolean show_mute); + +gboolean gvc_channel_bar_get_show_marks (GvcChannelBar *bar); +void gvc_channel_bar_set_show_marks (GvcChannelBar *bar, + gboolean show_marks); + +gboolean gvc_channel_bar_get_extended (GvcChannelBar *bar); +void gvc_channel_bar_set_extended (GvcChannelBar *bar, + gboolean extended); + +void gvc_channel_bar_set_size_group (GvcChannelBar *bar, + GtkSizeGroup *group, + gboolean symmetric); + +gboolean gvc_channel_bar_scroll (GvcChannelBar *bar, + GdkScrollDirection direction); G_END_DECLS diff --git a/mate-volume-control/src/gvc-combo-box.c b/mate-volume-control/src/gvc-combo-box.c index 6004632..524e1b0 100644 --- a/mate-volume-control/src/gvc-combo-box.c +++ b/mate-volume-control/src/gvc-combo-box.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2009 Bastien Nocera + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -18,20 +19,18 @@ * */ -#include "config.h" - #include <glib.h> #include <glib/gi18n.h> +#include <glib-object.h> #include <gtk/gtk.h> -#include <canberra-gtk.h> #include <libmatemixer/matemixer.h> #include "gvc-combo-box.h" #define GVC_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_COMBO_BOX, GvcComboBoxPrivate)) -struct GvcComboBoxPrivate +struct _GvcComboBoxPrivate { GtkWidget *drop_box; GtkWidget *start_box; @@ -57,18 +56,21 @@ enum { LAST_SIGNAL }; +static guint signals[LAST_SIGNAL] = { 0, }; + enum { PROP_0, PROP_LABEL, PROP_SHOW_BUTTON, - PROP_BUTTON_LABEL + PROP_BUTTON_LABEL, + N_PROPERTIES }; -static guint signals [LAST_SIGNAL] = { 0, }; +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; -static void gvc_combo_box_class_init (GvcComboBoxClass *klass); -static void gvc_combo_box_init (GvcComboBox *combo_box); -static void gvc_combo_box_finalize (GObject *object); +static void gvc_combo_box_class_init (GvcComboBoxClass *klass); +static void gvc_combo_box_init (GvcComboBox *combo); +static void gvc_combo_box_dispose (GObject *object); #if GTK_CHECK_VERSION (3, 0, 0) G_DEFINE_TYPE (GvcComboBox, gvc_combo_box, GTK_TYPE_BOX) @@ -77,25 +79,25 @@ G_DEFINE_TYPE (GvcComboBox, gvc_combo_box, GTK_TYPE_HBOX) #endif void -gvc_combo_box_set_size_group (GvcComboBox *combo_box, - GtkSizeGroup *group, - gboolean symmetric) +gvc_combo_box_set_size_group (GvcComboBox *combobox, + GtkSizeGroup *group, + gboolean symmetric) { - g_return_if_fail (GVC_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); + g_return_if_fail (GTK_IS_SIZE_GROUP (group)); - combo_box->priv->size_group = group; - combo_box->priv->symmetric = symmetric; + combobox->priv->size_group = group; + combobox->priv->symmetric = symmetric; - if (combo_box->priv->size_group != NULL) { - gtk_size_group_add_widget (combo_box->priv->size_group, - combo_box->priv->start_box); + if (combobox->priv->size_group != NULL) { + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->start_box); - if (combo_box->priv->symmetric) { - gtk_size_group_add_widget (combo_box->priv->size_group, - combo_box->priv->end_box); - } + if (combobox->priv->symmetric) + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->end_box); } - gtk_widget_queue_draw (GTK_WIDGET (combo_box)); + gtk_widget_queue_draw (GTK_WIDGET (combobox)); } static void @@ -132,16 +134,13 @@ gvc_combo_box_get_property (GObject *object, switch (prop_id) { case PROP_LABEL: - g_value_set_string (value, - gtk_label_get_text (GTK_LABEL (self->priv->label))); + g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label))); break; case PROP_BUTTON_LABEL: - g_value_set_string (value, - gtk_button_get_label (GTK_BUTTON (self->priv->button))); + g_value_set_string (value, gtk_button_get_label (GTK_BUTTON (self->priv->button))); break; case PROP_SHOW_BUTTON: - g_value_set_boolean (value, - gtk_widget_get_visible (self->priv->button)); + g_value_set_boolean (value, gtk_widget_get_visible (self->priv->button)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -154,32 +153,34 @@ gvc_combo_box_class_init (GvcComboBoxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = gvc_combo_box_finalize; + object_class->dispose = gvc_combo_box_dispose; object_class->set_property = gvc_combo_box_set_property; object_class->get_property = gvc_combo_box_get_property; - g_object_class_install_property (object_class, - PROP_LABEL, - g_param_spec_string ("label", - "label", - "The combo box label", - _("_Profile:"), - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_SHOW_BUTTON, - g_param_spec_boolean ("show-button", - "show-button", - "Whether to show the button", - FALSE, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_BUTTON_LABEL, - g_param_spec_string ("button-label", - "button-label", - "The button's label", - "APPLICATION BUG", - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); - signals [CHANGED] = + properties[PROP_LABEL] = + g_param_spec_string ("label", + "label", + "The combo box label", + _("_Profile:"), + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + properties[PROP_SHOW_BUTTON] = + g_param_spec_boolean ("show-button", + "show-button", + "Whether to show the button", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + properties[PROP_BUTTON_LABEL] = + g_param_spec_string ("button-label", + "button-label", + "The button's label", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -187,7 +188,8 @@ gvc_combo_box_class_init (GvcComboBoxClass *klass) NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); - signals [BUTTON_CLICKED] = + + signals[BUTTON_CLICKED] = g_signal_new ("button-clicked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -200,190 +202,219 @@ gvc_combo_box_class_init (GvcComboBoxClass *klass) } void -gvc_combo_box_set_profiles (GvcComboBox *combo_box, - const GList *profiles) +gvc_combo_box_set_profiles (GvcComboBox *combobox, const GList *profiles) { const GList *l; - g_return_if_fail (GVC_IS_COMBO_BOX (combo_box)); - g_return_if_fail (combo_box->priv->set_called == FALSE); + g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); + g_return_if_fail (combobox->priv->set_called == FALSE); for (l = profiles; l != NULL; l = l->next) { MateMixerDeviceProfile *p = MATE_MIXER_DEVICE_PROFILE (l->data); - gtk_list_store_insert_with_values (GTK_LIST_STORE (combo_box->priv->model), + gtk_list_store_insert_with_values (GTK_LIST_STORE (combobox->priv->model), NULL, G_MAXINT, - COL_NAME, mate_mixer_device_profile_get_name (p), - COL_HUMAN_NAME, mate_mixer_device_profile_get_description (p), + COL_NAME, + mate_mixer_device_profile_get_name (p), + COL_HUMAN_NAME, + mate_mixer_device_profile_get_description (p), -1); } - combo_box->priv->set_called = TRUE; + combobox->priv->set_called = TRUE; } void -gvc_combo_box_set_ports (GvcComboBox *combo_box, const GList *ports) +gvc_combo_box_set_ports (GvcComboBox *combobox, const GList *ports) { const GList *l; - g_return_if_fail (GVC_IS_COMBO_BOX (combo_box)); - g_return_if_fail (combo_box->priv->set_called == FALSE); + g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); + g_return_if_fail (combobox->priv->set_called == FALSE); for (l = ports; l != NULL; l = l->next) { MateMixerPort *p = MATE_MIXER_PORT (l->data); - gtk_list_store_insert_with_values (GTK_LIST_STORE (combo_box->priv->model), + gtk_list_store_insert_with_values (GTK_LIST_STORE (combobox->priv->model), NULL, G_MAXINT, - COL_NAME, mate_mixer_port_get_name (p), - COL_HUMAN_NAME, mate_mixer_port_get_description (p), + COL_NAME, + mate_mixer_port_get_name (p), + COL_HUMAN_NAME, + mate_mixer_port_get_description (p), -1); } - combo_box->priv->set_called = TRUE; + combobox->priv->set_called = TRUE; } void -gvc_combo_box_set_active (GvcComboBox *combo_box, - const char *id) +gvc_combo_box_set_active (GvcComboBox *combobox, const gchar *id) { GtkTreeIter iter; - gboolean cont; + gboolean cont; - cont = gtk_tree_model_get_iter_first (combo_box->priv->model, &iter); + g_return_if_fail (GVC_IS_COMBO_BOX (combobox)); + g_return_if_fail (id != NULL); + + cont = gtk_tree_model_get_iter_first (combobox->priv->model, &iter); while (cont != FALSE) { - char *name; + gchar *name; - gtk_tree_model_get (combo_box->priv->model, &iter, + gtk_tree_model_get (combobox->priv->model, &iter, COL_NAME, &name, -1); if (g_strcmp0 (name, id) == 0) { - gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box->priv->combobox), &iter); + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox->priv->combobox), &iter); + g_free (name); return; } - gtk_tree_model_iter_next (combo_box->priv->model, &iter); + g_free (name); + + gtk_tree_model_iter_next (combobox->priv->model, &iter); } g_warning ("Could not find id '%s' in combo box", id); } static void -on_combo_box_changed (GtkComboBox *widget, - GvcComboBox *combo_box) +on_combo_box_changed (GtkComboBox *widget, GvcComboBox *combobox) { - GtkTreeIter iter; - char *profile; + GtkTreeIter iter; + gchar *profile; if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter) == FALSE) { g_warning ("Could not find an active profile or port"); return; } - gtk_tree_model_get (combo_box->priv->model, &iter, + gtk_tree_model_get (combobox->priv->model, &iter, COL_NAME, &profile, -1); - g_signal_emit (combo_box, signals[CHANGED], 0, profile); + + g_signal_emit (combobox, signals[CHANGED], 0, profile); g_free (profile); } static void -on_combo_box_button_clicked (GtkButton *button, - GvcComboBox *combo_box) +on_combo_box_button_clicked (GtkButton *button, GvcComboBox *combobox) { - g_signal_emit (combo_box, signals[BUTTON_CLICKED], 0); + g_signal_emit (combobox, signals[BUTTON_CLICKED], 0); } static void -gvc_combo_box_init (GvcComboBox *combo_box) +gvc_combo_box_init (GvcComboBox *combobox) { - GtkWidget *frame; - GtkWidget *box; - GtkWidget *sbox; - GtkWidget *ebox; - GtkCellRenderer *renderer; + GtkWidget *frame; + GtkCellRenderer *renderer; - combo_box->priv = GVC_COMBO_BOX_GET_PRIVATE (combo_box); + combobox->priv = GVC_COMBO_BOX_GET_PRIVATE (combobox); - combo_box->priv->model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS, - G_TYPE_STRING, - G_TYPE_STRING)); + combobox->priv->model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS, + G_TYPE_STRING, + G_TYPE_STRING)); + combobox->priv->label = gtk_label_new (NULL); - combo_box->priv->label = gtk_label_new (NULL); - gtk_misc_set_alignment (GTK_MISC (combo_box->priv->label), - 0.0, - 0.5); + gtk_misc_set_alignment (GTK_MISC (combobox->priv->label), 0.0, 0.5); - /* frame */ + /* Frame */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - gtk_container_add (GTK_CONTAINER (combo_box), frame); + gtk_box_pack_start (GTK_BOX (combobox), frame, TRUE, TRUE, 0); + +#if GTK_CHECK_VERSION (3, 0, 0) + combobox->priv->drop_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + combobox->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + combobox->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); +#else + combobox->priv->drop_box = gtk_hbox_new (FALSE, 6); + combobox->priv->start_box = gtk_hbox_new (FALSE, 6); + combobox->priv->end_box = gtk_hbox_new (FALSE, 6); +#endif + combobox->priv->combobox = gtk_combo_box_new_with_model (combobox->priv->model); - combo_box->priv->drop_box = box = gtk_hbox_new (FALSE, 6); - combo_box->priv->combobox = gtk_combo_box_new_with_model (combo_box->priv->model); renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->combobox), - renderer, FALSE); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->combobox), + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox->priv->combobox), + renderer, + FALSE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox->priv->combobox), renderer, - "text", COL_HUMAN_NAME); - -/* gtk_widget_set_size_request (combo_box->priv->combobox, 128, -1); */ - - combo_box->priv->start_box = sbox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0); + "text", + COL_HUMAN_NAME); - gtk_box_pack_start (GTK_BOX (sbox), combo_box->priv->label, FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (box), combo_box->priv->combobox, TRUE, TRUE, 0); - - combo_box->priv->button = gtk_button_new_with_label ("APPLICATION BUG"); - gtk_widget_set_no_show_all (combo_box->priv->button, TRUE); - gtk_box_pack_start (GTK_BOX (box), combo_box->priv->button, FALSE, FALSE, 0); - - - combo_box->priv->end_box = ebox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, FALSE, 0); +#if GTK_CHECK_VERSION (3, 0, 0) + /* Make sure the combo box does not get too long on long profile names */ + g_object_set (G_OBJECT (renderer), + "ellipsize", + PANGO_ELLIPSIZE_END, + NULL); - if (combo_box->priv->size_group != NULL) { - gtk_size_group_add_widget (combo_box->priv->size_group, sbox); + gtk_combo_box_set_popup_fixed_width (GTK_COMBO_BOX (combobox->priv->combobox), FALSE); +#endif - if (combo_box->priv->symmetric) { - gtk_size_group_add_widget (combo_box->priv->size_group, ebox); - } + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->start_box, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (combobox->priv->start_box), + combobox->priv->label, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->combobox, + TRUE, TRUE, 0); + + combobox->priv->button = gtk_button_new_with_label (""); + + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->button, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box), + combobox->priv->end_box, + FALSE, FALSE, 0); + + gtk_widget_set_no_show_all (combobox->priv->button, TRUE); + + if (combobox->priv->size_group != NULL) { + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->start_box); + + if (combobox->priv->symmetric) + gtk_size_group_add_widget (combobox->priv->size_group, + combobox->priv->end_box); } - gtk_container_add (GTK_CONTAINER (frame), combo_box->priv->drop_box); - gtk_widget_show_all (frame); + gtk_label_set_mnemonic_widget (GTK_LABEL (combobox->priv->label), + combobox->priv->combobox); - gtk_label_set_mnemonic_widget (GTK_LABEL (combo_box->priv->label), - combo_box->priv->combobox); + gtk_container_add (GTK_CONTAINER (frame), combobox->priv->drop_box); + gtk_widget_show_all (frame); - g_signal_connect (G_OBJECT (combo_box->priv->combobox), "changed", - G_CALLBACK (on_combo_box_changed), combo_box); - g_signal_connect (G_OBJECT (combo_box->priv->button), "clicked", - G_CALLBACK (on_combo_box_button_clicked), combo_box); + g_signal_connect (G_OBJECT (combobox->priv->combobox), + "changed", + G_CALLBACK (on_combo_box_changed), + combobox); + g_signal_connect (G_OBJECT (combobox->priv->button), + "clicked", + G_CALLBACK (on_combo_box_button_clicked), + combobox); } static void -gvc_combo_box_finalize (GObject *object) +gvc_combo_box_dispose (GObject *object) { - GvcComboBox *combo_box; - - g_return_if_fail (object != NULL); - g_return_if_fail (GVC_IS_COMBO_BOX (object)); + GvcComboBox *combobox; - combo_box = GVC_COMBO_BOX (object); + combobox = GVC_COMBO_BOX (object); - g_return_if_fail (combo_box->priv != NULL); + g_clear_object (&combobox->priv->model); - g_object_unref (combo_box->priv->model); - combo_box->priv->model = NULL; - - G_OBJECT_CLASS (gvc_combo_box_parent_class)->finalize (object); + G_OBJECT_CLASS (gvc_combo_box_parent_class)->dispose (object); } GtkWidget * -gvc_combo_box_new (const char *label) +gvc_combo_box_new (const gchar *label) { return g_object_new (GVC_TYPE_COMBO_BOX, "label", label, +#if GTK_CHECK_VERSION (3, 0, 0) + "orientation", GTK_ORIENTATION_HORIZONTAL, +#endif NULL); } diff --git a/mate-volume-control/src/gvc-combo-box.h b/mate-volume-control/src/gvc-combo-box.h index ee18ff8..4cd8511 100644 --- a/mate-volume-control/src/gvc-combo-box.h +++ b/mate-volume-control/src/gvc-combo-box.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -21,7 +22,9 @@ #ifndef __GVC_COMBO_BOX_H #define __GVC_COMBO_BOX_H +#include <glib.h> #include <glib-object.h> +#include <gtk/gtk.h> G_BEGIN_DECLS @@ -32,43 +35,46 @@ G_BEGIN_DECLS #define GVC_IS_COMBO_BOX_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_COMBO_BOX)) #define GVC_COMBO_BOX_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_COMBO_BOX, GvcComboBoxClass)) -typedef struct GvcComboBoxPrivate GvcComboBoxPrivate; +typedef struct _GvcComboBox GvcComboBox; +typedef struct _GvcComboBoxClass GvcComboBoxClass; +typedef struct _GvcComboBoxPrivate GvcComboBoxPrivate; -typedef struct +struct _GvcComboBox { #if GTK_CHECK_VERSION (3, 0, 0) - GtkBox parent; + GtkBox parent; #else - GtkHBox parent; + GtkHBox parent; #endif - GvcComboBoxPrivate *priv; -} GvcComboBox; + GvcComboBoxPrivate *priv; +}; -typedef struct +struct _GvcComboBoxClass { #if GTK_CHECK_VERSION (3, 0, 0) GtkBoxClass parent_class; #else GtkHBoxClass parent_class; #endif - void (* changed) (GvcComboBox *combobox, const char *name); + void (* changed) (GvcComboBox *combobox, + const gchar *name); void (* button_clicked) (GvcComboBox *combobox); -} GvcComboBoxClass; +}; -GType gvc_combo_box_get_type (void); +GType gvc_combo_box_get_type (void) G_GNUC_CONST; -GtkWidget * gvc_combo_box_new (const char *label); +GtkWidget * gvc_combo_box_new (const gchar *label); -void gvc_combo_box_set_size_group (GvcComboBox *combo_box, +void gvc_combo_box_set_size_group (GvcComboBox *combobox, GtkSizeGroup *group, gboolean symmetric); -void gvc_combo_box_set_profiles (GvcComboBox *combo_box, +void gvc_combo_box_set_profiles (GvcComboBox *combobox, const GList *profiles); -void gvc_combo_box_set_ports (GvcComboBox *combo_box, +void gvc_combo_box_set_ports (GvcComboBox *combobox, const GList *ports); -void gvc_combo_box_set_active (GvcComboBox *combo_box, - const char *id); +void gvc_combo_box_set_active (GvcComboBox *combobox, + const gchar *id); G_END_DECLS diff --git a/mate-volume-control/src/gvc-level-bar.c b/mate-volume-control/src/gvc-level-bar.c index fbc0538..0f3dc1b 100644 --- a/mate-volume-control/src/gvc-level-bar.c +++ b/mate-volume-control/src/gvc-level-bar.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann <[email protected]> + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -18,54 +19,50 @@ * */ -#include "config.h" +// XXX on gtk3 the last two squares don't get filled -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> #include <math.h> - #include <glib.h> #include <glib/gi18n.h> +#include <glib-object.h> #include <gtk/gtk.h> #include "gvc-level-bar.h" - -#define NUM_BOXES 15 +#include "mvc-helpers.h" #define GVC_LEVEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBarPrivate)) +#define NUM_BOXES 15 #define MIN_HORIZONTAL_BAR_WIDTH 150 #define HORIZONTAL_BAR_HEIGHT 6 #define VERTICAL_BAR_WIDTH 6 #define MIN_VERTICAL_BAR_HEIGHT 400 typedef struct { - int peak_num; - int max_peak_num; - - GdkRectangle area; - int delta; - int box_width; - int box_height; - int box_radius; - double bg_r; - double bg_g; - double bg_b; - double bdr_r; - double bdr_g; - double bdr_b; - double fl_r; - double fl_g; - double fl_b; + int peak_num; + int max_peak_num; + GdkRectangle area; + int delta; + int box_width; + int box_height; + int box_radius; +#if GTK_CHECK_VERSION (3, 0, 0) + GdkRGBA color_bg; + GdkRGBA color_fg; + GdkRGBA color_dark; +#else + GdkColor color_bg; + GdkColor color_fg; + GdkColor color_dark; +#endif } LevelBarLayout; -struct GvcLevelBarPrivate +struct _GvcLevelBarPrivate { GtkOrientation orientation; GtkAdjustment *peak_adjustment; GtkAdjustment *rms_adjustment; - int scale; + GvcLevelScale scale; gdouble peak_fraction; gdouble rms_fraction; gdouble max_peak; @@ -80,47 +77,51 @@ enum PROP_RMS_ADJUSTMENT, PROP_SCALE, PROP_ORIENTATION, + N_PROPERTIES }; -static void gvc_level_bar_class_init (GvcLevelBarClass *klass); -static void gvc_level_bar_init (GvcLevelBar *level_bar); -static void gvc_level_bar_finalize (GObject *object); +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; -#if GTK_CHECK_VERSION (3, 0, 0) -G_DEFINE_TYPE (GvcLevelBar, gvc_level_bar, GTK_TYPE_BOX) -#else -G_DEFINE_TYPE (GvcLevelBar, gvc_level_bar, GTK_TYPE_HBOX) -#endif +static void gvc_level_bar_class_init (GvcLevelBarClass *klass); +static void gvc_level_bar_init (GvcLevelBar *bar); +static void gvc_level_bar_finalize (GObject *object); -#define check_rectangle(rectangle1, rectangle2) \ - { \ - if (rectangle1.x != rectangle2.x) return TRUE; \ - if (rectangle1.y != rectangle2.y) return TRUE; \ - if (rectangle1.width != rectangle2.width) return TRUE; \ - if (rectangle1.height != rectangle2.height) return TRUE; \ - } +G_DEFINE_TYPE (GvcLevelBar, gvc_level_bar, GTK_TYPE_WIDGET) static gboolean -layout_changed (LevelBarLayout *layout1, - LevelBarLayout *layout2) +layout_changed (LevelBarLayout *layout1, LevelBarLayout *layout2) { - check_rectangle (layout1->area, layout2->area); - if (layout1->delta != layout2->delta) return TRUE; - if (layout1->peak_num != layout2->peak_num) return TRUE; - if (layout1->max_peak_num != layout2->max_peak_num) return TRUE; - if (layout1->bg_r != layout2->bg_r - || layout1->bg_g != layout2->bg_g - || layout1->bg_b != layout2->bg_b) + if (layout1->area.x != layout2->area.x) + return TRUE; + if (layout1->area.y != layout2->area.y) return TRUE; - if (layout1->bdr_r != layout2->bdr_r - || layout1->bdr_g != layout2->bdr_g - || layout1->bdr_b != layout2->bdr_b) + if (layout1->area.width != layout2->area.width) return TRUE; - if (layout1->fl_r != layout2->fl_r - || layout1->fl_g != layout2->fl_g - || layout1->fl_b != layout2->fl_b) + if (layout1->area.height != layout2->area.height) return TRUE; + if (layout1->delta != layout2->delta) + return TRUE; + if (layout1->peak_num != layout2->peak_num) + return TRUE; + if (layout1->max_peak_num != layout2->max_peak_num) + return TRUE; + +#if GTK_CHECK_VERSION (3, 0, 0) + if (!gdk_rgba_equal (&layout1->color_fg, &layout2->color_fg)) + return TRUE; + if (!gdk_rgba_equal (&layout1->color_bg, &layout2->color_bg)) + return TRUE; + if (!gdk_rgba_equal (&layout1->color_dark, &layout2->color_dark)) + return TRUE; +#else + if (!gdk_color_equal (&layout1->color_fg, &layout2->color_fg)) + return TRUE; + if (!gdk_color_equal (&layout1->color_bg, &layout2->color_bg)) + return TRUE; + if (!gdk_color_equal (&layout1->color_dark, &layout2->color_dark)) + return TRUE; +#endif return FALSE; } @@ -129,14 +130,13 @@ fraction_from_adjustment (GvcLevelBar *bar, GtkAdjustment *adjustment) { gdouble level; - gdouble fraction; + gdouble fraction = 0.0; gdouble min; gdouble max; level = gtk_adjustment_get_value (adjustment); - - min = gtk_adjustment_get_lower (adjustment); - max = gtk_adjustment_get_upper (adjustment); + min = gtk_adjustment_get_lower (adjustment); + max = gtk_adjustment_get_upper (adjustment); switch (bar->priv->scale) { case GVC_LEVEL_SCALE_LINEAR: @@ -145,8 +145,6 @@ fraction_from_adjustment (GvcLevelBar *bar, case GVC_LEVEL_SCALE_LOG: fraction = log10 ((level - min + 1) / (max - min + 1)); break; - default: - g_assert_not_reached (); } return fraction; @@ -155,12 +153,12 @@ fraction_from_adjustment (GvcLevelBar *bar, static gboolean reset_max_peak (GvcLevelBar *bar) { - gdouble min; + bar->priv->max_peak = gtk_adjustment_get_lower (bar->priv->peak_adjustment); - min = gtk_adjustment_get_lower (bar->priv->peak_adjustment); - bar->priv->max_peak = min; bar->priv->layout.max_peak_num = 0; + gtk_widget_queue_draw (GTK_WIDGET (bar)); + bar->priv->max_peak_id = 0; return FALSE; } @@ -168,30 +166,43 @@ reset_max_peak (GvcLevelBar *bar) static void bar_calc_layout (GvcLevelBar *bar) { - GdkColor color; - int peak_level; - int max_peak_level; + int peak_level; + int max_peak_level; GtkAllocation allocation; + +#if GTK_CHECK_VERSION (3, 0, 0) + GtkStyleContext *context; + + context = gtk_widget_get_style_context (GTK_WIDGET (bar)); + + gtk_style_context_get_background_color (context, + GTK_STATE_FLAG_NORMAL, + &bar->priv->layout.color_bg); + gtk_style_context_get_background_color (context, + GTK_STATE_FLAG_SELECTED, + &bar->priv->layout.color_fg); + gtk_style_context_get_color (context, + GTK_STATE_FLAG_NORMAL, + &bar->priv->layout.color_dark); + + mvc_color_shade (&bar->priv->layout.color_dark, + &bar->priv->layout.color_dark, + 0.7); +#else GtkStyle *style; + style = gtk_widget_get_style (GTK_WIDGET (bar)); + + bar->priv->layout.color_bg = style->bg[GTK_STATE_NORMAL]; + bar->priv->layout.color_fg = style->bg[GTK_STATE_SELECTED]; + bar->priv->layout.color_dark = style->dark[GTK_STATE_NORMAL]; +#endif + gtk_widget_get_allocation (GTK_WIDGET (bar), &allocation); + bar->priv->layout.area.width = allocation.width - 2; bar->priv->layout.area.height = allocation.height - 2; - style = gtk_widget_get_style (GTK_WIDGET (bar)); - color = style->bg [GTK_STATE_NORMAL]; - bar->priv->layout.bg_r = (float)color.red / 65535.0; - bar->priv->layout.bg_g = (float)color.green / 65535.0; - bar->priv->layout.bg_b = (float)color.blue / 65535.0; - color = style->dark [GTK_STATE_NORMAL]; - bar->priv->layout.bdr_r = (float)color.red / 65535.0; - bar->priv->layout.bdr_g = (float)color.green / 65535.0; - bar->priv->layout.bdr_b = (float)color.blue / 65535.0; - color = style->bg [GTK_STATE_SELECTED]; - bar->priv->layout.fl_r = (float)color.red / 65535.0; - bar->priv->layout.fl_g = (float)color.green / 65535.0; - bar->priv->layout.fl_b = (float)color.blue / 65535.0; - if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { peak_level = bar->priv->peak_fraction * bar->priv->layout.area.height; max_peak_level = bar->priv->max_peak * bar->priv->layout.area.height; @@ -200,7 +211,7 @@ bar_calc_layout (GvcLevelBar *bar) bar->priv->layout.area.x = 0; bar->priv->layout.area.y = 0; bar->priv->layout.box_height = bar->priv->layout.delta / 2; - bar->priv->layout.box_width = bar->priv->layout.area.width; + bar->priv->layout.box_width = bar->priv->layout.area.width; bar->priv->layout.box_radius = bar->priv->layout.box_width / 2; } else { peak_level = bar->priv->peak_fraction * bar->priv->layout.area.width; @@ -209,7 +220,7 @@ bar_calc_layout (GvcLevelBar *bar) bar->priv->layout.delta = bar->priv->layout.area.width / NUM_BOXES; bar->priv->layout.area.x = 0; bar->priv->layout.area.y = 0; - bar->priv->layout.box_width = bar->priv->layout.delta / 2; + bar->priv->layout.box_width = bar->priv->layout.delta / 2; bar->priv->layout.box_height = bar->priv->layout.area.height; bar->priv->layout.box_radius = bar->priv->layout.box_height / 2; } @@ -221,42 +232,41 @@ bar_calc_layout (GvcLevelBar *bar) static void update_peak_value (GvcLevelBar *bar) { - gdouble val; + gdouble value; LevelBarLayout layout; - layout = bar->priv->layout; + value = fraction_from_adjustment (bar, bar->priv->peak_adjustment); - val = fraction_from_adjustment (bar, bar->priv->peak_adjustment); - bar->priv->peak_fraction = val; + bar->priv->peak_fraction = value; - if (val > bar->priv->max_peak) { - if (bar->priv->max_peak_id > 0) { + if (value > bar->priv->max_peak) { + if (bar->priv->max_peak_id > 0) g_source_remove (bar->priv->max_peak_id); - } - bar->priv->max_peak_id = g_timeout_add_seconds (1, (GSourceFunc)reset_max_peak, bar); - bar->priv->max_peak = val; + + bar->priv->max_peak_id = + g_timeout_add_seconds (1, (GSourceFunc) reset_max_peak, bar); + bar->priv->max_peak = value; } + layout = bar->priv->layout; + bar_calc_layout (bar); - if (layout_changed (&bar->priv->layout, &layout)) { + if (layout_changed (&bar->priv->layout, &layout)) gtk_widget_queue_draw (GTK_WIDGET (bar)); - } } static void update_rms_value (GvcLevelBar *bar) { - gdouble val; - - val = fraction_from_adjustment (bar, bar->priv->rms_adjustment); - bar->priv->rms_fraction = val; + bar->priv->rms_fraction = fraction_from_adjustment (bar, bar->priv->rms_adjustment); } GtkOrientation gvc_level_bar_get_orientation (GvcLevelBar *bar) { g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), 0); + return bar->priv->orientation; } @@ -267,9 +277,17 @@ gvc_level_bar_set_orientation (GvcLevelBar *bar, g_return_if_fail (GVC_IS_LEVEL_BAR (bar)); if (orientation != bar->priv->orientation) { + if (G_UNLIKELY (orientation != GTK_ORIENTATION_VERTICAL && + orientation != GTK_ORIENTATION_HORIZONTAL)) { + g_warn_if_reached (); + return; + } + bar->priv->orientation = orientation; + gtk_widget_queue_draw (GTK_WIDGET (bar)); - g_object_notify (G_OBJECT (bar), "orientation"); + + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]); } } @@ -295,7 +313,7 @@ gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar, g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); if (bar->priv->peak_adjustment != NULL) { - g_signal_handlers_disconnect_by_func (bar->priv->peak_adjustment, + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->peak_adjustment), G_CALLBACK (on_peak_adjustment_value_changed), bar); g_object_unref (bar->priv->peak_adjustment); @@ -303,14 +321,14 @@ gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar, bar->priv->peak_adjustment = g_object_ref_sink (adjustment); - g_signal_connect (bar->priv->peak_adjustment, + g_signal_connect (G_OBJECT (bar->priv->peak_adjustment), "value-changed", G_CALLBACK (on_peak_adjustment_value_changed), bar); update_peak_value (bar); - g_object_notify (G_OBJECT (bar), "peak-adjustment"); + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_PEAK_ADJUSTMENT]); } void @@ -321,7 +339,7 @@ gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar, g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); if (bar->priv->rms_adjustment != NULL) { - g_signal_handlers_disconnect_by_func (bar->priv->peak_adjustment, + g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->rms_adjustment), G_CALLBACK (on_rms_adjustment_value_changed), bar); g_object_unref (bar->priv->rms_adjustment); @@ -329,15 +347,14 @@ gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar, bar->priv->rms_adjustment = g_object_ref_sink (adjustment); - - g_signal_connect (bar->priv->peak_adjustment, + g_signal_connect (G_OBJECT (bar->priv->rms_adjustment), "value-changed", - G_CALLBACK (on_peak_adjustment_value_changed), + G_CALLBACK (on_rms_adjustment_value_changed), bar); update_rms_value (bar); - g_object_notify (G_OBJECT (bar), "rms-adjustment"); + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_RMS_ADJUSTMENT]); } GtkAdjustment * @@ -357,26 +374,30 @@ gvc_level_bar_get_rms_adjustment (GvcLevelBar *bar) } void -gvc_level_bar_set_scale (GvcLevelBar *bar, - GvcLevelScale scale) +gvc_level_bar_set_scale (GvcLevelBar *bar, GvcLevelScale scale) { g_return_if_fail (GVC_IS_LEVEL_BAR (bar)); if (scale != bar->priv->scale) { + if (G_UNLIKELY (scale != GVC_LEVEL_SCALE_LINEAR && + scale != GVC_LEVEL_SCALE_LOG)) { + g_warn_if_reached (); + return; + } bar->priv->scale = scale; update_peak_value (bar); update_rms_value (bar); - g_object_notify (G_OBJECT (bar), "scale"); + g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SCALE]); } } static void gvc_level_bar_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) + guint prop_id, + const GValue *value, + GParamSpec *pspec) { GvcLevelBar *self = GVC_LEVEL_BAR (object); @@ -401,9 +422,9 @@ gvc_level_bar_set_property (GObject *object, static void gvc_level_bar_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { GvcLevelBar *self = GVC_LEVEL_BAR (object); @@ -426,14 +447,6 @@ gvc_level_bar_get_property (GObject *object, } } -static GObject * -gvc_level_bar_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_params) -{ - return G_OBJECT_CLASS (gvc_level_bar_parent_class)->constructor (type, n_construct_properties, construct_params); -} - static void gvc_level_bar_size_request (GtkWidget *widget, GtkRequisition *requisition) @@ -447,19 +460,17 @@ gvc_level_bar_size_request (GtkWidget *widget, switch (bar->priv->orientation) { case GTK_ORIENTATION_VERTICAL: - requisition->width = VERTICAL_BAR_WIDTH; + requisition->width = VERTICAL_BAR_WIDTH; requisition->height = MIN_VERTICAL_BAR_HEIGHT; break; case GTK_ORIENTATION_HORIZONTAL: - requisition->width = MIN_HORIZONTAL_BAR_WIDTH; + requisition->width = MIN_HORIZONTAL_BAR_WIDTH; requisition->height = HORIZONTAL_BAR_HEIGHT; break; - default: - g_assert_not_reached (); - break; } } +#if GTK_CHECK_VERSION (3, 0, 0) static void gvc_level_bar_get_preferred_width (GtkWidget *widget, gint *minimum, @@ -469,7 +480,10 @@ gvc_level_bar_get_preferred_width (GtkWidget *widget, gvc_level_bar_size_request (widget, &requisition); - *minimum = *natural = requisition.width; + if (minimum != NULL) + *minimum = requisition.width; + if (natural != NULL) + *natural = requisition.width; } static void @@ -481,18 +495,18 @@ gvc_level_bar_get_preferred_height (GtkWidget *widget, gvc_level_bar_size_request (widget, &requisition); - *minimum = *natural = requisition.height; + if (minimum != NULL) + *minimum = requisition.height; + if (natural != NULL) + *natural = requisition.height; } +#endif static void -gvc_level_bar_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) +gvc_level_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GvcLevelBar *bar; - g_return_if_fail (GVC_IS_LEVEL_BAR (widget)); - g_return_if_fail (allocation != NULL); - bar = GVC_LEVEL_BAR (widget); /* FIXME: add height property, labels, etc */ @@ -503,9 +517,9 @@ gvc_level_bar_size_allocate (GtkWidget *widget, if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { allocation->height = MIN (allocation->height, MIN_VERTICAL_BAR_HEIGHT); - allocation->width = MAX (allocation->width, VERTICAL_BAR_WIDTH); + allocation->width = MAX (allocation->width, VERTICAL_BAR_WIDTH); } else { - allocation->width = MIN (allocation->width, MIN_HORIZONTAL_BAR_WIDTH); + allocation->width = MIN (allocation->width, MIN_HORIZONTAL_BAR_WIDTH); allocation->height = MAX (allocation->height, HORIZONTAL_BAR_HEIGHT); } @@ -526,9 +540,8 @@ curved_rectangle (cairo_t *cr, x1 = x0 + width; y1 = y0 + height; - if (!width || !height) { + if (!width || !height) return; - } if (width / 2 < radius) { if (height / 2 < radius) { @@ -541,7 +554,7 @@ curved_rectangle (cairo_t *cr, cairo_move_to (cr, x0, y0 + radius); cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0); cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); - cairo_line_to (cr, x1, y1 - radius); + cairo_line_to (cr, x1, y1 - radius); cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1); cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); } @@ -549,19 +562,19 @@ curved_rectangle (cairo_t *cr, if (height / 2 < radius) { cairo_move_to (cr, x0, (y0 + y1) / 2); cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0); - cairo_line_to (cr, x1 - radius, y0); + cairo_line_to (cr, x1 - radius, y0); cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2); cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); - cairo_line_to (cr, x0 + radius, y1); + cairo_line_to (cr, x0 + radius, y1); cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2); } else { cairo_move_to (cr, x0, y0 + radius); cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0); - cairo_line_to (cr, x1 - radius, y0); + cairo_line_to (cr, x1 - radius, y0); cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius); - cairo_line_to (cr, x1, y1 - radius); + cairo_line_to (cr, x1, y1 - radius); cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1); - cairo_line_to (cr, x0 + radius, y1); + cairo_line_to (cr, x0 + radius, y1); cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius); } } @@ -570,40 +583,13 @@ curved_rectangle (cairo_t *cr, } static int -#if GTK_CHECK_VERSION (3, 0, 0) -gvc_level_bar_draw (GtkWidget *widget, - cairo_t *cr) -#else -gvc_level_bar_expose (GtkWidget *widget, - GdkEventExpose *event) -#endif +gvc_level_bar_draw (GtkWidget *widget, cairo_t *cr) { - GvcLevelBar *bar; -#if !GTK_CHECK_VERSION (3, 0, 0) - cairo_t *cr; - GtkAllocation allocation; -#endif - - g_return_val_if_fail (GVC_IS_LEVEL_BAR (widget), FALSE); -#if !GTK_CHECK_VERSION (3, 0, 0) - g_return_val_if_fail (event != NULL, FALSE); - - /* event queue compression */ - if (event->count > 0) { - return FALSE; - } -#endif + GvcLevelBar *bar; bar = GVC_LEVEL_BAR (widget); -#if !GTK_CHECK_VERSION (3, 0, 0) - cr = gdk_cairo_create (gtk_widget_get_window (widget)); - - gtk_widget_get_allocation (widget, &allocation); - cairo_translate (cr, - allocation.x, - allocation.y); -#endif + cairo_save (cr); if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) { int i; @@ -619,31 +605,68 @@ gvc_level_bar_expose (GtkWidget *widget, bar->priv->layout.box_radius); if ((bar->priv->layout.max_peak_num - 1) == i) { /* fill peak foreground */ - cairo_set_source_rgb (cr, bar->priv->layout.fl_r, bar->priv->layout.fl_g, bar->priv->layout.fl_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg); +#endif cairo_fill_preserve (cr); } else if ((bar->priv->layout.peak_num - 1) >= i) { /* fill background */ - cairo_set_source_rgb (cr, bar->priv->layout.bg_r, bar->priv->layout.bg_g, bar->priv->layout.bg_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif cairo_fill_preserve (cr); + /* fill foreground */ - cairo_set_source_rgba (cr, bar->priv->layout.fl_r, bar->priv->layout.fl_g, bar->priv->layout.fl_b, 0.5); +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red, + bar->priv->layout.color_fg.green, + bar->priv->layout.color_fg.blue, + 0.5); +#else + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red / 65535.0, + bar->priv->layout.color_fg.green / 65535.0, + bar->priv->layout.color_fg.blue / 65535.0, + 0.5); +#endif cairo_fill_preserve (cr); } else { /* fill background */ - cairo_set_source_rgb (cr, bar->priv->layout.bg_r, bar->priv->layout.bg_g, bar->priv->layout.bg_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif cairo_fill_preserve (cr); } /* stroke border */ - cairo_set_source_rgb (cr, bar->priv->layout.bdr_r, bar->priv->layout.bdr_g, bar->priv->layout.bdr_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark); +#endif cairo_set_line_width (cr, 1); cairo_stroke (cr); } - } else { int i; int bx; + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) { + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + + cairo_scale (cr, -1, 1); + cairo_translate (cr, -allocation.width, 0); + } + for (i = 0; i < NUM_BOXES; i++) { bx = i * bar->priv->layout.delta; curved_rectangle (cr, @@ -655,33 +678,88 @@ gvc_level_bar_expose (GtkWidget *widget, if ((bar->priv->layout.max_peak_num - 1) == i) { /* fill peak foreground */ - cairo_set_source_rgb (cr, bar->priv->layout.fl_r, bar->priv->layout.fl_g, bar->priv->layout.fl_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg); +#endif cairo_fill_preserve (cr); } else if ((bar->priv->layout.peak_num - 1) >= i) { /* fill background */ - cairo_set_source_rgb (cr, bar->priv->layout.bg_r, bar->priv->layout.bg_g, bar->priv->layout.bg_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif cairo_fill_preserve (cr); + /* fill foreground */ - cairo_set_source_rgba (cr, bar->priv->layout.fl_r, bar->priv->layout.fl_g, bar->priv->layout.fl_b, 0.5); +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red, + bar->priv->layout.color_fg.green, + bar->priv->layout.color_fg.blue, + 0.5); +#else + cairo_set_source_rgba (cr, + bar->priv->layout.color_fg.red / 65535.0, + bar->priv->layout.color_fg.green / 65535.0, + bar->priv->layout.color_fg.blue / 65535.0, + 0.5); +#endif cairo_fill_preserve (cr); } else { /* fill background */ - cairo_set_source_rgb (cr, bar->priv->layout.bg_r, bar->priv->layout.bg_g, bar->priv->layout.bg_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg); +#endif cairo_fill_preserve (cr); } /* stroke border */ - cairo_set_source_rgb (cr, bar->priv->layout.bdr_r, bar->priv->layout.bdr_g, bar->priv->layout.bdr_b); +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark); +#else + gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark); +#endif cairo_set_line_width (cr, 1); cairo_stroke (cr); } } + + cairo_restore (cr); + + return FALSE; +} + #if !GTK_CHECK_VERSION (3, 0, 0) - cairo_destroy (cr); -#endif +static int +gvc_level_bar_expose (GtkWidget *widget, GdkEventExpose *event) +{ + cairo_t *cr; + GtkAllocation allocation; + g_return_val_if_fail (event != NULL, FALSE); + + /* Event queue compression */ + if (event->count > 0) + return FALSE; + + gtk_widget_get_allocation (widget, &allocation); + + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + cairo_translate (cr, + allocation.x, + allocation.y); + + gvc_level_bar_draw (widget, cr); + + cairo_destroy (cr); return FALSE; } +#endif static void gvc_level_bar_class_init (GvcLevelBarClass *klass) @@ -689,7 +767,6 @@ gvc_level_bar_class_init (GvcLevelBarClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - object_class->constructor = gvc_level_bar_constructor; object_class->finalize = gvc_level_bar_finalize; object_class->set_property = gvc_level_bar_set_property; object_class->get_property = gvc_level_bar_get_property; @@ -704,37 +781,43 @@ gvc_level_bar_class_init (GvcLevelBarClass *klass) #endif widget_class->size_allocate = gvc_level_bar_size_allocate; - g_object_class_install_property (object_class, - PROP_ORIENTATION, - g_param_spec_enum ("orientation", - "Orientation", - "The orientation of the bar", - GTK_TYPE_ORIENTATION, - GTK_ORIENTATION_HORIZONTAL, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_PEAK_ADJUSTMENT, - g_param_spec_object ("peak-adjustment", - "Peak Adjustment", - "The GtkAdjustment that contains the current peak value", - GTK_TYPE_ADJUSTMENT, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_RMS_ADJUSTMENT, - g_param_spec_object ("rms-adjustment", - "RMS Adjustment", - "The GtkAdjustment that contains the current rms value", - GTK_TYPE_ADJUSTMENT, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_SCALE, - g_param_spec_int ("scale", - "Scale", - "Scale", - 0, - G_MAXINT, - GVC_LEVEL_SCALE_LINEAR, - G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); + properties[PROP_ORIENTATION] = + g_param_spec_enum ("orientation", + "Orientation", + "The orientation of the bar", + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_PEAK_ADJUSTMENT] = + g_param_spec_object ("peak-adjustment", + "Peak Adjustment", + "The GtkAdjustment that contains the current peak value", + GTK_TYPE_ADJUSTMENT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_RMS_ADJUSTMENT] = + g_param_spec_object ("rms-adjustment", + "RMS Adjustment", + "The GtkAdjustment that contains the current rms value", + GTK_TYPE_ADJUSTMENT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SCALE] = + g_param_spec_int ("scale", + "Scale", + "Scale", + 0, + G_MAXINT, + GVC_LEVEL_SCALE_LINEAR, + 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 (GvcLevelBarPrivate)); } @@ -751,6 +834,7 @@ gvc_level_bar_init (GvcLevelBar *bar) 0.1, 0.1)); g_object_ref_sink (bar->priv->peak_adjustment); + g_signal_connect (bar->priv->peak_adjustment, "value-changed", G_CALLBACK (on_peak_adjustment_value_changed), @@ -763,6 +847,7 @@ gvc_level_bar_init (GvcLevelBar *bar) 0.1, 0.1)); g_object_ref_sink (bar->priv->rms_adjustment); + g_signal_connect (bar->priv->rms_adjustment, "value-changed", G_CALLBACK (on_rms_adjustment_value_changed), @@ -776,16 +861,10 @@ gvc_level_bar_finalize (GObject *object) { GvcLevelBar *bar; - g_return_if_fail (object != NULL); - g_return_if_fail (GVC_IS_LEVEL_BAR (object)); - bar = GVC_LEVEL_BAR (object); - if (bar->priv->max_peak_id > 0) { + if (bar->priv->max_peak_id > 0) g_source_remove (bar->priv->max_peak_id); - } - - g_return_if_fail (bar->priv != NULL); G_OBJECT_CLASS (gvc_level_bar_parent_class)->finalize (object); } @@ -793,8 +872,5 @@ gvc_level_bar_finalize (GObject *object) GtkWidget * gvc_level_bar_new (void) { - GObject *bar; - bar = g_object_new (GVC_TYPE_LEVEL_BAR, - NULL); - return GTK_WIDGET (bar); + return g_object_new (GVC_TYPE_LEVEL_BAR, NULL); } diff --git a/mate-volume-control/src/gvc-level-bar.h b/mate-volume-control/src/gvc-level-bar.h index 844d0f9..ef9ae7e 100644 --- a/mate-volume-control/src/gvc-level-bar.h +++ b/mate-volume-control/src/gvc-level-bar.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann <[email protected]> + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -21,6 +22,7 @@ #ifndef __GVC_LEVEL_BAR_H #define __GVC_LEVEL_BAR_H +#include <glib.h> #include <glib-object.h> #include <gtk/gtk.h> @@ -33,35 +35,28 @@ G_BEGIN_DECLS #define GVC_IS_LEVEL_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_LEVEL_BAR)) #define GVC_LEVEL_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBarClass)) -typedef struct GvcLevelBarPrivate GvcLevelBarPrivate; +typedef struct _GvcLevelBar GvcLevelBar; +typedef struct _GvcLevelBarClass GvcLevelBarClass; +typedef struct _GvcLevelBarPrivate GvcLevelBarPrivate; -typedef struct +struct _GvcLevelBar { -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBox parent; -#else - GtkHBox parent; -#endif - GvcLevelBarPrivate *priv; -} GvcLevelBar; + GtkWidget parent; + GvcLevelBarPrivate *priv; +}; -typedef struct +struct _GvcLevelBarClass { -#if GTK_CHECK_VERSION (3, 0, 0) - GtkBoxClass parent_class; -#else - GtkHBoxClass parent_class; -#endif -} GvcLevelBarClass; + GtkWidgetClass parent_class; +}; typedef enum { GVC_LEVEL_SCALE_LINEAR, - GVC_LEVEL_SCALE_LOG, - GVC_LEVEL_SCALE_LAST + GVC_LEVEL_SCALE_LOG } GvcLevelScale; -GType gvc_level_bar_get_type (void); +GType gvc_level_bar_get_type (void) G_GNUC_CONST; GtkWidget * gvc_level_bar_new (void); void gvc_level_bar_set_orientation (GvcLevelBar *bar, @@ -71,13 +66,14 @@ GtkOrientation gvc_level_bar_get_orientation (GvcLevelBar *bar); void gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar, GtkAdjustment *adjustment); GtkAdjustment * gvc_level_bar_get_peak_adjustment (GvcLevelBar *bar); + void gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar, GtkAdjustment *adjustment); GtkAdjustment * gvc_level_bar_get_rms_adjustment (GvcLevelBar *bar); + void gvc_level_bar_set_scale (GvcLevelBar *bar, GvcLevelScale scale); - G_END_DECLS #endif /* __GVC_LEVEL_BAR_H */ diff --git a/mate-volume-control/src/gvc-mixer-dialog.c b/mate-volume-control/src/gvc-mixer-dialog.c index e46ca1a..9b524fe 100644 --- a/mate-volume-control/src/gvc-mixer-dialog.c +++ b/mate-volume-control/src/gvc-mixer-dialog.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -22,11 +23,11 @@ #include <glib.h> #include <glib/gi18n.h> +#include <glib-object.h> +#include <gdk/gdk.h> #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> -#if GTK_CHECK_VERSION (3, 0, 0) -#include <gdk/gdkkeysyms-compat.h> -#endif +#include <libmatemixer/matemixer.h> #include "gvc-channel-bar.h" #include "gvc-balance-bar.h" @@ -57,9 +58,10 @@ struct _GvcMixerDialogPrivate GtkWidget *input_box; GtkWidget *output_box; GtkWidget *applications_box; - GtkWidget *applications_scrolled_window; + GtkWidget *applications_window; GtkWidget *no_apps_label; GtkWidget *output_treeview; + GtkWidget *output_settings_frame; GtkWidget *output_settings_box; GtkWidget *output_balance_bar; GtkWidget *output_fade_bar; @@ -70,7 +72,6 @@ struct _GvcMixerDialogPrivate GtkWidget *input_settings_box; GtkWidget *sound_theme_chooser; GtkSizeGroup *size_group; - GtkSizeGroup *apps_size_group; gdouble last_input_peak; guint num_apps; }; @@ -79,7 +80,6 @@ enum { ICON_COLUMN, NAME_COLUMN, DESCRIPTION_COLUMN, - DEVICE_COLUMN, ACTIVE_COLUMN, SPEAKERS_COLUMN, NUM_COLUMNS @@ -91,212 +91,292 @@ enum { HW_DESCRIPTION_COLUMN, HW_STATUS_COLUMN, HW_PROFILE_COLUMN, - HW_PROFILE_HUMAN_COLUMN, - HW_SENSITIVE_COLUMN, HW_NUM_COLUMNS }; enum { - PAGE_EVENTS, + PAGE_EFFECTS, PAGE_HARDWARE, PAGE_INPUT, PAGE_OUTPUT, PAGE_APPLICATIONS }; -enum -{ +enum { PROP_0, - PROP_MIXER_CONTROL + PROP_CONTROL }; -static void gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass); -static void gvc_mixer_dialog_init (GvcMixerDialog *mixer_dialog); -static void gvc_mixer_dialog_finalize (GObject *object); +static const guint tab_accel_keys[] = { + GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5 +}; -static void bar_set_stream (GvcMixerDialog *dialog, - GtkWidget *bar, - MateMixerStream *stream); +static void gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass); +static void gvc_mixer_dialog_init (GvcMixerDialog *dialog); +static void gvc_mixer_dialog_finalize (GObject *object); -static void on_adjustment_value_changed (GtkAdjustment *adjustment, - GvcMixerDialog *dialog); +static void bar_set_stream (GvcMixerDialog *dialog, + GtkWidget *bar, + MateMixerStream *stream); G_DEFINE_TYPE (GvcMixerDialog, gvc_mixer_dialog, GTK_TYPE_DIALOG) -static void -update_default_input (GvcMixerDialog *dialog) +static gboolean +find_tree_item_by_name (GtkTreeModel *model, + const gchar *name, + guint column, + GtkTreeIter *iter) { - GtkTreeModel *model; - GtkTreeIter iter; - gboolean ret; + gboolean found = FALSE; - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - ret = gtk_tree_model_get_iter_first (model, &iter); - if (ret == FALSE) { - g_debug ("No default input selected or available"); - return; - } - do { - gboolean toggled; - gboolean is_default = FALSE; - MateMixerStream *stream; - gchar *name; + if (!gtk_tree_model_get_iter_first (model, iter)) + return FALSE; - gtk_tree_model_get (model, &iter, - NAME_COLUMN, &name, - ACTIVE_COLUMN, &toggled, - -1); + do { + gchar *n; - stream = mate_mixer_control_get_stream (dialog->priv->control, name); - if (stream == NULL) { - g_warning ("Unable to find stream for id: %s", name); - g_free (name); - continue; - } + gtk_tree_model_get (model, iter, column, &n, -1); - if (stream == mate_mixer_control_get_default_input_stream (dialog->priv->control)) - is_default = TRUE; + if (!g_strcmp0 (name, n)) + found = TRUE; - gtk_list_store_set (GTK_LIST_STORE (model), - &iter, - ACTIVE_COLUMN, is_default, - -1); + g_free (n); + } while (!found && gtk_tree_model_iter_next (model, iter)); - g_free (name); - } while (gtk_tree_model_iter_next (model, &iter)); + return found; } static void -update_description (GvcMixerDialog *dialog, - guint column, - const gchar *value, - MateMixerStream *stream) +update_default_item (GvcMixerDialog *dialog, + GtkTreeModel *model, + MateMixerStream *stream) { - GtkTreeModel *model; - GtkTreeIter iter; - MateMixerStreamFlags flags; - const gchar *name; - - flags = mate_mixer_stream_get_flags (stream); + GtkTreeIter iter; + const gchar *name = NULL; - if (flags & MATE_MIXER_STREAM_INPUT) - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - else if (flags & MATE_MIXER_STREAM_OUTPUT) - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - else - g_assert_not_reached (); + if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) + return; - gtk_tree_model_get_iter_first (model, &iter); + /* The supplied stream is the default, or the selected item. Traverse + * the item list and mark each item as being selected or not. Also do not + * presume some known stream is selected and allow NULL here. */ + if (stream != NULL) + name = mate_mixer_stream_get_name (stream); - name = mate_mixer_stream_get_name (stream); do { - const gchar *current_name; - - gtk_tree_model_get (model, &iter, NAME_COLUMN, ¤t_name, -1); - if (!g_strcmp0 (name, current_name)) - continue; + gchar *n; + gtk_tree_model_get (model, &iter, + NAME_COLUMN, &n, + -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, - column, value, + ACTIVE_COLUMN, !g_strcmp0 (name, n), -1); - break; + g_free (n); } while (gtk_tree_model_iter_next (model, &iter)); } static void -port_selection_changed (GvcComboBox *combo, - const gchar *port, - GvcMixerDialog *dialog) +on_combo_box_port_changed (GvcComboBox *combo, + const gchar *name, + GvcMixerDialog *dialog) { MateMixerStream *stream; + MateMixerPort *port = NULL; + GList *ports; stream = g_object_get_data (G_OBJECT (combo), "stream"); - if (stream == NULL) { - g_warning ("Could not find stream for port combo box"); + if (G_UNLIKELY (stream == NULL)) { + g_warn_if_reached (); + return; + } + + ports = (GList *) mate_mixer_stream_list_ports (stream); + while (ports) { + port = MATE_MIXER_PORT (ports->data); + + if (!g_strcmp0 (mate_mixer_port_get_name (port), name)) + break; + + port = NULL; + ports = ports->next; + } + + if (G_UNLIKELY (port == NULL)) { + g_warn_if_reached (); return; } + g_debug ("Stream port changed to %s for stream %s", + name, + mate_mixer_stream_get_name (stream)); + mate_mixer_stream_set_active_port (stream, port); } static void +on_combo_box_profile_changed (GvcComboBox *combo, + const gchar *name, + GvcMixerDialog *dialog) +{ + MateMixerDevice *device; + MateMixerDeviceProfile *profile = NULL; + GList *profiles; + + device = g_object_get_data (G_OBJECT (combo), "device"); + if (G_UNLIKELY (device == NULL)) { + g_warn_if_reached (); + return; + } + + profiles = (GList *) mate_mixer_device_list_profiles (device); + while (profiles) { + profile = MATE_MIXER_DEVICE_PROFILE (profiles->data); + + if (!g_strcmp0 (mate_mixer_device_profile_get_name (profile), name)) + break; + + profile = NULL; + profiles = profiles->next; + } + + if (G_UNLIKELY (profile == NULL)) { + g_warn_if_reached (); + return; + } + + g_debug ("Device profile changed to %s for device %s", + name, + mate_mixer_device_get_name (device)); + + mate_mixer_device_set_active_profile (device, profile); +} + +static GtkWidget * +create_port_combo_box (GvcMixerDialog *dialog, + MateMixerStream *stream, + const gchar *name, + const GList *items, + const gchar *active) +{ + GtkWidget *combobox; + + combobox = gvc_combo_box_new (name); + + gvc_combo_box_set_ports (GVC_COMBO_BOX (combobox), items); + gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), active); + + gvc_combo_box_set_size_group (GVC_COMBO_BOX (combobox), + dialog->priv->size_group, + FALSE); + + g_object_set_data_full (G_OBJECT (combobox), + "stream", + g_object_ref (stream), + g_object_unref); + + g_signal_connect (G_OBJECT (combobox), + "changed", + G_CALLBACK (on_combo_box_port_changed), + dialog); + + return combobox; +} + +static void update_output_settings (GvcMixerDialog *dialog) { MateMixerStream *stream; MateMixerStreamFlags flags; const GList *ports; + gboolean has_settings = FALSE; g_debug ("Updating output settings"); + if (dialog->priv->output_balance_bar != NULL) { gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), dialog->priv->output_balance_bar); + dialog->priv->output_balance_bar = NULL; } if (dialog->priv->output_fade_bar != NULL) { gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), dialog->priv->output_fade_bar); + dialog->priv->output_fade_bar = NULL; } if (dialog->priv->output_lfe_bar != NULL) { gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), dialog->priv->output_lfe_bar); + dialog->priv->output_lfe_bar = NULL; } if (dialog->priv->output_port_combo != NULL) { gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box), dialog->priv->output_port_combo); + dialog->priv->output_port_combo = NULL; } stream = mate_mixer_control_get_default_output_stream (dialog->priv->control); if (stream == NULL) { - g_warning ("Default sink stream not found"); + g_debug ("There is no default output stream - output settings disabled"); + gtk_widget_hide (dialog->priv->output_settings_frame); return; } flags = mate_mixer_stream_get_flags (stream); - gvc_channel_bar_set_base_volume (GVC_CHANNEL_BAR (dialog->priv->output_bar), - mate_mixer_stream_get_base_volume (stream)); - gvc_channel_bar_set_is_amplified (GVC_CHANNEL_BAR (dialog->priv->output_bar), - flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME); + /* Enable balance bar if stream feature is available */ + if (flags & MATE_MIXER_STREAM_CAN_BALANCE) { + dialog->priv->output_balance_bar = + gvc_balance_bar_new (stream, BALANCE_TYPE_RL); - dialog->priv->output_balance_bar = gvc_balance_bar_new (stream, BALANCE_TYPE_RL); - if (dialog->priv->size_group != NULL) { gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_balance_bar), dialog->priv->size_group, TRUE); + + gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), + dialog->priv->output_balance_bar, + FALSE, FALSE, 6); + + gtk_widget_show (dialog->priv->output_balance_bar); + has_settings = TRUE; } - gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), - dialog->priv->output_balance_bar, - FALSE, FALSE, 6); - gtk_widget_show (dialog->priv->output_balance_bar); + /* Enable fade bar if stream feature is available */ if (flags & MATE_MIXER_STREAM_CAN_FADE) { - dialog->priv->output_fade_bar = gvc_balance_bar_new (stream, BALANCE_TYPE_FR); - if (dialog->priv->size_group != NULL) { - gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_fade_bar), - dialog->priv->size_group, - TRUE); - } + dialog->priv->output_fade_bar = + gvc_balance_bar_new (stream, BALANCE_TYPE_FR); + + gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_fade_bar), + dialog->priv->size_group, + TRUE); + gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), dialog->priv->output_fade_bar, FALSE, FALSE, 6); + gtk_widget_show (dialog->priv->output_fade_bar); + has_settings = TRUE; } - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_LFE)) { - dialog->priv->output_lfe_bar = gvc_balance_bar_new (stream, BALANCE_TYPE_LFE); - if (dialog->priv->size_group != NULL) { - gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_lfe_bar), - dialog->priv->size_group, - TRUE); - } + /* Enable subwoofer volume bar if subwoofer is available */ + if (mate_mixer_stream_has_channel_position (stream, MATE_MIXER_CHANNEL_LFE)) { + dialog->priv->output_lfe_bar = + gvc_balance_bar_new (stream, BALANCE_TYPE_LFE); + + gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_lfe_bar), + dialog->priv->size_group, + TRUE); + gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), dialog->priv->output_lfe_bar, FALSE, FALSE, 6); + gtk_widget_show (dialog->priv->output_lfe_bar); + has_settings = TRUE; } ports = mate_mixer_stream_list_ports (stream); @@ -304,180 +384,149 @@ update_output_settings (GvcMixerDialog *dialog) MateMixerPort *port; port = mate_mixer_stream_get_active_port (stream); + if (G_UNLIKELY (port == NULL)) { + /* Select the first port if none is selected at the moment */ + port = MATE_MIXER_PORT (ports->data); + mate_mixer_stream_set_active_port (stream, port); + } - dialog->priv->output_port_combo = gvc_combo_box_new (_("Co_nnector:")); - gvc_combo_box_set_ports (GVC_COMBO_BOX (dialog->priv->output_port_combo), - ports); - - gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->output_port_combo), - mate_mixer_port_get_name (port)); - - g_object_set_data (G_OBJECT (dialog->priv->output_port_combo), "stream", stream); - g_signal_connect (G_OBJECT (dialog->priv->output_port_combo), "changed", - G_CALLBACK (port_selection_changed), dialog); + dialog->priv->output_port_combo = + create_port_combo_box (dialog, + stream, + _("Co_nnector:"), + ports, + mate_mixer_port_get_name (port)); gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box), dialog->priv->output_port_combo, TRUE, FALSE, 6); - gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->output_port_combo), dialog->priv->size_group, FALSE); - gtk_widget_show (dialog->priv->output_port_combo); + has_settings = TRUE; } - /* FIXME: We could make this into a "No settings" label instead */ - gtk_widget_set_sensitive (dialog->priv->output_balance_bar, flags & MATE_MIXER_STREAM_CAN_BALANCE); + if (has_settings == TRUE) + gtk_widget_show (dialog->priv->output_settings_frame); + else + gtk_widget_hide (dialog->priv->output_settings_frame); } static void -update_default_output (GvcMixerDialog *dialog) +on_control_default_output_notify (MateMixerControl *control, + GParamSpec *pspec, + GvcMixerDialog *dialog) { - GtkTreeModel *model; - GtkTreeIter iter; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - gtk_tree_model_get_iter_first (model, &iter); - do { - gboolean toggled; - gboolean is_default = FALSE; - gchar *name; - MateMixerStream *stream; + GtkTreeModel *model; + MateMixerStream *stream; - gtk_tree_model_get (model, &iter, - NAME_COLUMN, &name, - ACTIVE_COLUMN, &toggled, - -1); + stream = mate_mixer_control_get_default_output_stream (control); + if (stream != NULL) { + GList *streams; - stream = mate_mixer_control_get_stream (dialog->priv->control, name); - if (stream == NULL) { - g_warning ("Unable to find stream for id: %s", name); - g_free (name); - continue; - } + streams = (GList *) mate_mixer_control_list_cached_streams (dialog->priv->control); - if (stream == mate_mixer_control_get_default_output_stream (dialog->priv->control)) - is_default = TRUE; + /* Move all cached stream to the newly selected default stream */ + while (streams) { + MateMixerStream *parent; + MateMixerClientStream *client = MATE_MIXER_CLIENT_STREAM (streams->data); - gtk_list_store_set (GTK_LIST_STORE (model), - &iter, - ACTIVE_COLUMN, is_default, - -1); - g_free (name); - } while (gtk_tree_model_iter_next (model, &iter)); -} + parent = mate_mixer_client_stream_get_parent (client); -static void -on_control_default_output_notify (MateMixerControl *control, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - MateMixerStream *stream; + if (parent != stream) { + MateMixerStreamFlags flags; - g_debug ("Default output stream has changed"); + flags = mate_mixer_stream_get_flags (MATE_MIXER_STREAM (client)); - stream = mate_mixer_control_get_default_output_stream (control); + if (flags & MATE_MIXER_STREAM_OUTPUT) + mate_mixer_client_stream_set_parent (client, stream); + } + streams = streams->next; + } + } bar_set_stream (dialog, dialog->priv->output_bar, stream); - update_output_settings (dialog); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); + update_default_item (dialog, model, stream); - update_default_output (dialog); + update_output_settings (dialog); } - #define DECAY_STEP .15 static void -update_input_peak (GvcMixerDialog *dialog, gdouble v) +on_stream_monitor_value (MateMixerStream *stream, + gdouble value, + GvcMixerDialog *dialog) { GtkAdjustment *adj; if (dialog->priv->last_input_peak >= DECAY_STEP) { - if (v < dialog->priv->last_input_peak - DECAY_STEP) { - v = dialog->priv->last_input_peak - DECAY_STEP; + if (value < dialog->priv->last_input_peak - DECAY_STEP) { + value = dialog->priv->last_input_peak - DECAY_STEP; } } - dialog->priv->last_input_peak = v; + dialog->priv->last_input_peak = value; adj = gvc_level_bar_get_peak_adjustment (GVC_LEVEL_BAR (dialog->priv->input_level_bar)); - if (v >= 0) { - gtk_adjustment_set_value (adj, v); - } else { + if (value >= 0) + gtk_adjustment_set_value (adj, value); + else gtk_adjustment_set_value (adj, 0.0); - } -} - -static void -on_stream_monitor_value (MateMixerStream *stream, - gdouble value, - GvcMixerDialog *dialog) -{ - g_debug ("Monitor %.2f", value); - update_input_peak (dialog, value); } static void update_input_settings (GvcMixerDialog *dialog) { - MateMixerStream *stream; + MateMixerStream *stream; MateMixerStreamFlags flags; - const GList *ports; + const GList *ports; g_debug ("Updating input settings"); if (dialog->priv->input_port_combo != NULL) { gtk_container_remove (GTK_CONTAINER (dialog->priv->input_settings_box), dialog->priv->input_port_combo); + dialog->priv->input_port_combo = NULL; } stream = mate_mixer_control_get_default_input_stream (dialog->priv->control); if (stream == NULL) { - g_debug ("Default source stream not found"); + g_debug ("There is no default input stream"); return; } - mate_mixer_stream_monitor_set_name (stream, _("Peak detect")); - - g_signal_connect (G_OBJECT (stream), - "monitor-value", - G_CALLBACK (on_stream_monitor_value), - dialog); - flags = mate_mixer_stream_get_flags (stream); - gvc_channel_bar_set_base_volume (GVC_CHANNEL_BAR (dialog->priv->input_bar), - mate_mixer_stream_get_base_volume (stream)); + /* Enable level bar only if supported by the stream */ + if (flags & MATE_MIXER_STREAM_HAS_MONITOR) { + mate_mixer_stream_monitor_set_name (stream, _("Peak detect")); - // XXX probably wrong - gvc_channel_bar_set_is_amplified (GVC_CHANNEL_BAR (dialog->priv->input_bar), - flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME); + g_signal_connect (G_OBJECT (stream), + "monitor-value", + G_CALLBACK (on_stream_monitor_value), + dialog); + } ports = mate_mixer_stream_list_ports (stream); if (ports != NULL) { MateMixerPort *port; port = mate_mixer_stream_get_active_port (stream); + if (G_UNLIKELY (port == NULL)) { + /* Select the first port if none is selected at the moment */ + port = MATE_MIXER_PORT (ports->data); + mate_mixer_stream_set_active_port (stream, port); + } - dialog->priv->input_port_combo = gvc_combo_box_new (_("Co_nnector:")); - gvc_combo_box_set_ports (GVC_COMBO_BOX (dialog->priv->input_port_combo), - ports); - gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->input_port_combo), - mate_mixer_port_get_name (port)); - - g_object_set_data (G_OBJECT (dialog->priv->input_port_combo), - "stream", - stream); - - g_signal_connect (G_OBJECT (dialog->priv->input_port_combo), - "changed", - G_CALLBACK (port_selection_changed), - dialog); - - gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->input_port_combo), - dialog->priv->size_group, - FALSE); + dialog->priv->input_port_combo = + create_port_combo_box (dialog, + stream, + _("Co_nnector:"), + ports, + mate_mixer_port_get_name (port)); gtk_box_pack_start (GTK_BOX (dialog->priv->input_settings_box), dialog->priv->input_port_combo, @@ -492,361 +541,332 @@ on_control_default_input_notify (MateMixerControl *control, GParamSpec *pspec, GvcMixerDialog *dialog) { + GtkTreeModel *model; MateMixerStream *stream; MateMixerStream *current; - GtkAdjustment *adj; - stream = mate_mixer_control_get_default_input_stream (control); + g_debug ("Default input stream has changed"); - current = g_object_get_data (G_OBJECT (dialog->priv->input_bar), "gvc-mixer-dialog-stream"); - if (current != NULL) + current = gvc_channel_bar_get_stream (GVC_CHANNEL_BAR (dialog->priv->input_bar)); + if (current != NULL) { + /* Make sure to disable monitoring of the removed stream */ g_signal_handlers_disconnect_by_func (G_OBJECT (current), - on_stream_monitor_value, + G_CALLBACK (on_stream_monitor_value), dialog); - if (gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog->priv->notebook)) == PAGE_INPUT) { - if (current != NULL) - mate_mixer_stream_monitor_stop (current); - if (stream != NULL) - mate_mixer_stream_monitor_start (stream); + mate_mixer_stream_monitor_stop (current); } - // g_debug ("GvcMixerDialog: default source changed: %u", id); - - // XXX is the default input reffed/unreffed anywhere? - - /* Disconnect the adj, otherwise it might change if is_amplified changes */ - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->input_bar))); - - g_signal_handlers_disconnect_by_func (adj, - on_adjustment_value_changed, - dialog); - - bar_set_stream (dialog, dialog->priv->input_bar, stream); - update_input_settings (dialog); - - g_signal_connect (adj, - "value-changed", - G_CALLBACK (on_adjustment_value_changed), - dialog); + stream = mate_mixer_control_get_default_input_stream (control); + if (stream != NULL) { + GList *streams; + guint page = gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog->priv->notebook)); - update_default_input (dialog); -} + streams = (GList *) mate_mixer_control_list_cached_streams (dialog->priv->control); -static void -on_adjustment_value_changed (GtkAdjustment *adjustment, - GvcMixerDialog *dialog) -{ - MateMixerStream *stream; + /* Move all cached stream to the newly selected default stream */ + while (streams) { + MateMixerStream *parent; + MateMixerClientStream *client = MATE_MIXER_CLIENT_STREAM (streams->data); - stream = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-stream"); - if (stream != NULL) { - GObject *bar; - gdouble volume, rounded; - char *name; + parent = mate_mixer_client_stream_get_parent (client); - volume = gtk_adjustment_get_value (adjustment); - rounded = round (volume); + if (parent != stream) { + MateMixerStreamFlags flags; - bar = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-bar"); - g_object_get (bar, "name", &name, NULL); - g_debug ("Setting stream volume %lf (rounded: %lf) for bar '%s'", volume, rounded, name); - g_free (name); + flags = mate_mixer_stream_get_flags (MATE_MIXER_STREAM (client)); - /* FIXME would need to do that in the balance bar really... */ - /* Make sure we do not unmute muted streams, there's a button for that */ - if (volume == 0.0) - mate_mixer_stream_set_mute (stream, TRUE); + if (flags & MATE_MIXER_STREAM_INPUT) + mate_mixer_client_stream_set_parent (client, stream); + } + streams = streams->next; + } - mate_mixer_stream_set_volume (stream, rounded); + if (page == PAGE_INPUT) + mate_mixer_stream_monitor_start (stream); } -} - -static void -on_bar_is_muted_notify (GObject *object, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - gboolean is_muted; - MateMixerStream *stream; - is_muted = gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object)); + bar_set_stream (dialog, dialog->priv->input_bar, stream); - stream = g_object_get_data (object, "gvc-mixer-dialog-stream"); - if (stream != NULL) { - mate_mixer_stream_set_mute (stream, is_muted); - } else { - char *name; - g_object_get (object, "name", &name, NULL); - g_warning ("Unable to find stream for bar '%s'", name); - g_free (name); - } -} + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); + update_default_item (dialog, model, stream); -static GtkWidget * -lookup_bar_for_stream (GvcMixerDialog *dialog, MateMixerStream *stream) -{ - return g_hash_table_lookup (dialog->priv->bars, mate_mixer_stream_get_name (stream)); + update_input_settings (dialog); } -static GtkWidget * -lookup_combo_box_for_stream (GvcMixerDialog *dialog, - MateMixerStream *stream) +static GvcComboBox * +find_combo_box_by_stream (GvcMixerDialog *dialog, MateMixerStream *stream) { MateMixerStream *combo_stream; - const gchar *name; - - name = mate_mixer_stream_get_name (stream); if (dialog->priv->output_port_combo != NULL) { combo_stream = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo), "stream"); - if (combo_stream != NULL) { - if (!g_strcmp0 (name, mate_mixer_stream_get_name (combo_stream))) - return dialog->priv->output_port_combo; - } + if (combo_stream == stream) + return GVC_COMBO_BOX (dialog->priv->output_port_combo); } if (dialog->priv->input_port_combo != NULL) { combo_stream = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo), "stream"); - if (combo_stream != NULL) { - if (!g_strcmp0 (name, mate_mixer_stream_get_name (combo_stream))) - return dialog->priv->input_port_combo; - } + if (combo_stream == stream) + return GVC_COMBO_BOX (dialog->priv->input_port_combo); } - return NULL; } static void on_stream_description_notify (MateMixerStream *stream, - GParamSpec *pspec, - GvcMixerDialog *dialog) + GParamSpec *pspec, + GvcMixerDialog *dialog) { - update_description (dialog, NAME_COLUMN, - mate_mixer_stream_get_description (stream), - stream); -} + GtkTreeModel *model; + GtkTreeIter iter; + MateMixerStreamFlags flags; -static void -on_stream_port_notify (GObject *object, - GParamSpec *pspec, - GvcMixerDialog *dialog) -{ - GvcComboBox *combo; - MateMixerPort *port; + flags = mate_mixer_stream_get_flags (stream); - combo = GVC_COMBO_BOX (lookup_combo_box_for_stream (dialog, MATE_MIXER_STREAM (object))); - if (combo == NULL) - return; + if (flags & MATE_MIXER_STREAM_INPUT) + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); + else + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - g_signal_handlers_block_by_func (G_OBJECT (combo), - port_selection_changed, - dialog); + if (find_tree_item_by_name (model, + mate_mixer_stream_get_name (stream), + NAME_COLUMN, + &iter) == TRUE) { + const gchar *description; - g_object_get (object, "active-port", &port, NULL); - // XXX is this correct? - if (port) { - gvc_combo_box_set_active (GVC_COMBO_BOX (combo), - mate_mixer_port_get_name (port)); + description = mate_mixer_stream_get_description (stream); + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + DESCRIPTION_COLUMN, description, + -1); } - - g_signal_handlers_unblock_by_func (G_OBJECT (combo), - port_selection_changed, - dialog); } static void -on_stream_volume_notify (GObject *object, - GParamSpec *pspec, - GvcMixerDialog *dialog) +on_stream_port_notify (MateMixerStream *stream, + GParamSpec *pspec, + GvcMixerDialog *dialog) { - MateMixerStream *stream; - GtkWidget *bar; - GtkAdjustment *adj; - - stream = MATE_MIXER_STREAM (object); - - bar = lookup_bar_for_stream (dialog, stream); + GvcComboBox *combobox; + MateMixerPort *port; - if (bar == NULL) { - g_warning ("Unable to find bar for stream %s in on_stream_volume_notify()", - mate_mixer_stream_get_name (stream)); + combobox = find_combo_box_by_stream (dialog, stream); + if (G_UNLIKELY (combobox == NULL)) { + g_warn_if_reached (); return; } - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar))); + g_debug ("Stream %s port has changed", + mate_mixer_stream_get_name (stream)); - g_signal_handlers_block_by_func (adj, - on_adjustment_value_changed, + g_signal_handlers_block_by_func (G_OBJECT (combobox), + on_combo_box_port_changed, dialog); - gtk_adjustment_set_value (adj, - mate_mixer_stream_get_volume (stream)); + port = mate_mixer_stream_get_active_port (stream); + if (G_LIKELY (port != NULL)) { + const gchar *name = mate_mixer_port_get_name (port); + + g_debug ("The current port is %s", name); - g_signal_handlers_unblock_by_func (adj, - on_adjustment_value_changed, + gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), name); + } + + g_signal_handlers_unblock_by_func (G_OBJECT (combobox), + on_combo_box_port_changed, dialog); } static void -on_stream_mute_notify (GObject *object, - GParamSpec *pspec, - GvcMixerDialog *dialog) +on_stream_mute_notify (MateMixerStream *stream, + GParamSpec *pspec, + GvcMixerDialog *dialog) { - MateMixerStream *stream; - GtkWidget *bar; - gboolean is_muted; - - stream = MATE_MIXER_STREAM (object); - bar = lookup_bar_for_stream (dialog, stream); - - if (bar == NULL) { - g_warning ("Unable to find bar for stream %s in on_stream_is_muted_notify()", - mate_mixer_stream_get_name (stream)); - return; - } + MateMixerStream *input; - is_muted = mate_mixer_stream_get_mute (stream); - gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar), is_muted); + input = mate_mixer_control_get_default_input_stream (dialog->priv->control); - if (stream == mate_mixer_control_get_default_output_stream (dialog->priv->control)) { - gtk_widget_set_sensitive (dialog->priv->applications_box, - !is_muted); + /* Stop monitoring the input stream when it gets muted */ + if (stream == input) { + if (mate_mixer_stream_get_mute (stream)) + mate_mixer_stream_monitor_stop (stream); + else + mate_mixer_stream_monitor_start (stream); } - -} - -static void -save_bar_for_stream (GvcMixerDialog *dialog, - MateMixerStream *stream, - GtkWidget *bar) -{ - g_hash_table_insert (dialog->priv->bars, - (gpointer) mate_mixer_stream_get_name (stream), - bar); } static GtkWidget * -create_bar (GvcMixerDialog *dialog, - GtkSizeGroup *size_group, - gboolean symmetric) +create_bar (GvcMixerDialog *dialog, gboolean use_size_group, gboolean symmetric) { GtkWidget *bar; - bar = gvc_channel_bar_new (); - gtk_widget_set_sensitive (bar, FALSE); - if (size_group != NULL) { + bar = gvc_channel_bar_new (NULL); + + if (use_size_group == TRUE) gvc_channel_bar_set_size_group (GVC_CHANNEL_BAR (bar), - size_group, + dialog->priv->size_group, symmetric); - } - gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (bar), - GTK_ORIENTATION_HORIZONTAL); - gvc_channel_bar_set_show_mute (GVC_CHANNEL_BAR (bar), - TRUE); - g_signal_connect (bar, - "notify::is-muted", - G_CALLBACK (on_bar_is_muted_notify), - dialog); + + g_object_set (G_OBJECT (bar), + "orientation", GTK_ORIENTATION_HORIZONTAL, + "show-mute", TRUE, + "show-icons", TRUE, + "show-marks", TRUE, + "extended", TRUE, NULL); return bar; } static void -bar_set_stream (GvcMixerDialog *dialog, GtkWidget *bar, MateMixerStream *stream) +bar_set_stream (GvcMixerDialog *dialog, + GtkWidget *bar, + MateMixerStream *stream) { - GtkAdjustment *adj; - MateMixerStream *old_stream; + MateMixerStream *previous; - old_stream = g_object_get_data (G_OBJECT (bar), "gvc-mixer-dialog-stream"); - if (old_stream != NULL) { - char *name; + previous = gvc_channel_bar_get_stream (GVC_CHANNEL_BAR (bar)); + if (previous != NULL) { + const gchar *name = mate_mixer_stream_get_name (previous); - g_object_get (bar, "name", &name, NULL); - g_debug ("Disconnecting old stream '%s' from bar '%s'", - mate_mixer_stream_get_name (old_stream), name); - g_free (name); + g_debug ("Disconnecting old stream %s", name); + + g_signal_handlers_disconnect_by_func (previous, + on_stream_mute_notify, + dialog); + g_signal_handlers_disconnect_by_func (previous, + on_stream_port_notify, + dialog); - g_signal_handlers_disconnect_by_func (old_stream, on_stream_mute_notify, dialog); - g_signal_handlers_disconnect_by_func (old_stream, on_stream_volume_notify, dialog); - g_signal_handlers_disconnect_by_func (old_stream, on_stream_port_notify, dialog); + if (mate_mixer_stream_get_flags (previous) & MATE_MIXER_STREAM_INPUT) + mate_mixer_stream_monitor_stop (previous); g_hash_table_remove (dialog->priv->bars, name); } - gtk_widget_set_sensitive (bar, (stream != NULL)); - - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar))); - - // XXX already done in notify - g_signal_handlers_disconnect_by_func (adj, on_adjustment_value_changed, dialog); - - g_object_set_data (G_OBJECT (bar), "gvc-mixer-dialog-stream", stream); - g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-stream", stream); - g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-bar", bar); + gvc_channel_bar_set_stream (GVC_CHANNEL_BAR (bar), stream); if (stream != NULL) { - gboolean is_muted; + const gchar *name = mate_mixer_stream_get_name (stream); - is_muted = mate_mixer_stream_get_mute (stream); - gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar), is_muted); - - save_bar_for_stream (dialog, stream, bar); - - gtk_adjustment_set_value (adj, mate_mixer_stream_get_volume (stream)); + g_debug ("Connecting new stream %s", name); g_signal_connect (stream, "notify::mute", G_CALLBACK (on_stream_mute_notify), dialog); g_signal_connect (stream, - "notify::volume", - G_CALLBACK (on_stream_volume_notify), - dialog); - g_signal_connect (stream, "notify::active-port", G_CALLBACK (on_stream_port_notify), dialog); - g_signal_connect (adj, - "value-changed", - G_CALLBACK (on_adjustment_value_changed), - dialog); + + g_hash_table_insert (dialog->priv->bars, (gpointer) name, bar); } + + gtk_widget_set_sensitive (GTK_WIDGET (bar), (stream != NULL)); } static void add_stream (GvcMixerDialog *dialog, MateMixerStream *stream) { - GtkWidget *bar = NULL; - gboolean is_default = FALSE; - MateMixerClientStream *client = NULL; - MateMixerStreamFlags flags; + GtkWidget *bar = NULL; + MateMixerStreamFlags flags; flags = mate_mixer_stream_get_flags (stream); - if (flags & MATE_MIXER_STREAM_EVENT) - return; - if (flags & MATE_MIXER_STREAM_APPLICATION) { - const gchar *app_id; + if (MATE_MIXER_IS_CLIENT_STREAM (stream)) { + MateMixerClientStream *client ; + MateMixerClientStreamFlags client_flags; + MateMixerClientStreamRole client_role; + const gchar *name; + const gchar *icon; - client = MATE_MIXER_CLIENT_STREAM (stream); - app_id = mate_mixer_client_stream_get_app_id (client); + client = MATE_MIXER_CLIENT_STREAM (stream); + client_flags = mate_mixer_client_stream_get_flags (client); + client_role = mate_mixer_client_stream_get_role (client); - /* These applications are not displayed on the Applications tab */ - if (!g_strcmp0 (app_id, "org.mate.VolumeControl") || - !g_strcmp0 (app_id, "org.gnome.VolumeControl") || - !g_strcmp0 (app_id, "org.PulseAudio.pavucontrol")) - return; - } + /* We use a cached event stream for the effects volume slider, + * because regular streams are only created when an event sound + * is played and then immediately destroyed. + * The cached event stream should exist all the time. */ + if (client_flags & MATE_MIXER_CLIENT_STREAM_CACHED && + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) { + g_debug ("Found cached event role stream %s", + mate_mixer_stream_get_name (stream)); + + bar = dialog->priv->effects_bar; + } - if (client == NULL) { + /* Add stream to the applications page, but make sure the stream + * qualifies for the inclusion */ + if (client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) { + const gchar *app_id; + + /* Skip streams with roles we don't care about */ + if (client_role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT || + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_TEST || + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT || + client_role == MATE_MIXER_CLIENT_STREAM_ROLE_FILTER) + return; + + app_id = mate_mixer_client_stream_get_app_id (client); + + /* These applications may have associated streams because + * they do peak level monitoring, skip these too */ + if (!g_strcmp0 (app_id, "org.mate.VolumeControl") || + !g_strcmp0 (app_id, "org.gnome.VolumeControl") || + !g_strcmp0 (app_id, "org.PulseAudio.pavucontrol")) + return; + + name = mate_mixer_client_stream_get_app_name (client); + if (name == NULL) + name = mate_mixer_stream_get_description (stream); + if (name == NULL) + name = mate_mixer_stream_get_name (stream); + if (G_UNLIKELY (name == NULL)) + return; + + bar = create_bar (dialog, FALSE, FALSE); + + g_object_set (G_OBJECT (bar), + "show-marks", FALSE, + "extended", FALSE, + NULL); + + /* By default channel bars use speaker icons, use microphone + * icons instead for recording applications */ + if (flags & MATE_MIXER_STREAM_INPUT) + g_object_set (G_OBJECT (bar), + "low-icon-name", "audio-input-microphone-low", + "high-icon-name", "audio-input-microphone-high", + NULL); + + icon = mate_mixer_client_stream_get_app_icon (client); + if (icon == NULL) { + if (flags & MATE_MIXER_STREAM_INPUT) + icon = "audio-input-microphone"; + else + icon = "applications-multimedia"; + } + + gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), name); + gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar), icon); + + gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), + bar, + FALSE, FALSE, 12); + + gtk_widget_hide (dialog->priv->no_apps_label); + dialog->priv->num_apps++; + } + } else { GtkTreeModel *model = NULL; GtkTreeIter iter; MateMixerStream *input; MateMixerStream *output; const gchar *speakers = NULL; - GtkAdjustment *adj; + gboolean is_default = FALSE; input = mate_mixer_control_get_default_input_stream (dialog->priv->control); output = mate_mixer_control_get_default_output_stream (dialog->priv->control); @@ -854,28 +874,17 @@ add_stream (GvcMixerDialog *dialog, MateMixerStream *stream) if (flags & MATE_MIXER_STREAM_INPUT) { if (stream == input) { bar = dialog->priv->input_bar; - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar))); - g_signal_handlers_disconnect_by_func (G_OBJECT (adj), - on_adjustment_value_changed, - dialog); update_input_settings (dialog); is_default = TRUE; } model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - - } else if (flags & MATE_MIXER_STREAM_OUTPUT) { + } + else if (flags & MATE_MIXER_STREAM_OUTPUT) { if (stream == output) { bar = dialog->priv->output_bar; - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar))); - - gtk_widget_set_sensitive (dialog->priv->applications_box, - mate_mixer_stream_get_mute (stream) == FALSE); - g_signal_handlers_disconnect_by_func (G_OBJECT (adj), - on_adjustment_value_changed, - dialog); update_output_settings (dialog); is_default = TRUE; } @@ -885,23 +894,8 @@ add_stream (GvcMixerDialog *dialog, MateMixerStream *stream) } if (model != NULL) { - MateMixerDevice *device; - const gchar *name, - *description; - const gchar *device_name = NULL; - const gchar *icon = NULL; - - device = mate_mixer_stream_get_device (stream); - if (G_LIKELY (device != NULL)) { - device_name = mate_mixer_device_get_description (device); - if (device_name == NULL) - device_name = mate_mixer_device_get_name (device); - - icon = mate_mixer_device_get_icon (device); - } - - if (icon == NULL) - icon = "audio-card"; + const gchar *name; + const gchar *description; name = mate_mixer_stream_get_name (stream); description = mate_mixer_stream_get_description (stream); @@ -911,7 +905,6 @@ add_stream (GvcMixerDialog *dialog, MateMixerStream *stream) &iter, NAME_COLUMN, name, DESCRIPTION_COLUMN, description, - DEVICE_COLUMN, device_name, ACTIVE_COLUMN, is_default, SPEAKERS_COLUMN, speakers, -1); @@ -921,48 +914,6 @@ add_stream (GvcMixerDialog *dialog, MateMixerStream *stream) G_CALLBACK (on_stream_description_notify), dialog); } - } else { - const gchar *name; - const gchar *icon; - - bar = create_bar (dialog, dialog->priv->apps_size_group, FALSE); - - // FIXME: - // 1) We should ideally provide application name on the first line and - // description on a second line in italics - // 2) We should watch for name changes as it may include for example - // the name of the current song - name = mate_mixer_client_stream_get_app_name (client); - if (name == NULL) - name = mate_mixer_stream_get_description (stream); - if (name == NULL) - name = mate_mixer_stream_get_name (stream); - - if (name == NULL || strchr (name, '_') == NULL) { - gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), name); - } else { - char **tokens, *escaped; - - // XXX why is this here? - tokens = g_strsplit (name, "_", -1); - escaped = g_strjoinv ("__", tokens); - g_strfreev (tokens); - gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), escaped); - g_free (escaped); - } - - icon = mate_mixer_client_stream_get_app_icon (client); - if (icon == NULL) { - /* If there is no name of the application icon, set a default */ - icon = "applications-multimedia"; - } - - gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar), icon); - - gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), bar, FALSE, FALSE, 12); - gtk_widget_hide (dialog->priv->no_apps_label); - - dialog->priv->num_apps++; } if (bar != NULL) { @@ -980,80 +931,61 @@ on_control_stream_added (MateMixerControl *control, GtkWidget *bar; bar = g_hash_table_lookup (dialog->priv->bars, name); - if (bar != NULL) { - g_debug ("Ignoring already known stream %s", name); + if (G_UNLIKELY (bar != NULL)) return; - } stream = mate_mixer_control_get_stream (control, name); - if (G_LIKELY (stream != NULL)) - add_stream (dialog, stream); -} - -static gboolean -find_tree_item_by_name (GtkTreeModel *model, - const gchar *name, - guint column, - GtkTreeIter *iter) -{ - gboolean found = FALSE; - - if (!gtk_tree_model_get_iter_first (model, iter)) - return FALSE; - - do { - gchar *n; - - gtk_tree_model_get (model, iter, column, &n, -1); - - if (!g_strcmp0 (name, n)) - found = TRUE; - - g_free (n); - } while (!found && gtk_tree_model_iter_next (model, iter)); + if (G_UNLIKELY (stream == NULL)) + return; - return found; + add_stream (dialog, stream); } static void remove_stream (GvcMixerDialog *dialog, const gchar *name) { GtkWidget *bar; - gboolean found; GtkTreeIter iter; GtkTreeModel *model; - /* remove bars for applications and reset fixed bars */ + /* Remove bars for applications and reset fixed bars */ bar = g_hash_table_lookup (dialog->priv->bars, name); - if (bar == dialog->priv->output_bar - || bar == dialog->priv->input_bar - || bar == dialog->priv->effects_bar) { // XXX effects bad? verify this! - char *bar_name; - g_object_get (bar, "name", &bar_name, NULL); - g_debug ("Removing stream for bar '%s'", bar_name); - g_free (bar_name); + + if (bar == dialog->priv->input_bar || bar == dialog->priv->output_bar) { + g_debug ("Removing stream %s from bar %s", + name, + gvc_channel_bar_get_name (GVC_CHANNEL_BAR (bar))); + bar_set_stream (dialog, bar, NULL); } else if (bar != NULL) { + g_debug ("Removing application stream %s", name); + g_hash_table_remove (dialog->priv->bars, name); - gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (bar)), - bar); - dialog->priv->num_apps--; - if (dialog->priv->num_apps == 0) { - gtk_widget_show (dialog->priv->no_apps_label); + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (bar)), bar); + + if (G_UNLIKELY (dialog->priv->num_apps <= 0)) { + g_warn_if_reached (); + dialog->priv->num_apps = 1; } + + if (--dialog->priv->num_apps == 0) + gtk_widget_show (dialog->priv->no_apps_label); } - /* remove from any models */ + /* Remove from any models */ model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); - found = find_tree_item_by_name (GTK_TREE_MODEL (model), name, NAME_COLUMN, &iter); - if (found) { + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + NAME_COLUMN, + &iter) == TRUE) gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - } + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); - found = find_tree_item_by_name (GTK_TREE_MODEL (model), name, NAME_COLUMN, &iter); - if (found) { + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + NAME_COLUMN, + &iter) == TRUE) gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - } } static void @@ -1064,8 +996,28 @@ on_control_stream_removed (MateMixerControl *control, remove_stream (dialog, name); } +static void +on_control_cached_stream_removed (MateMixerControl *control, + const gchar *name, + GvcMixerDialog *dialog) +{ + GtkWidget *bar; + + bar = g_hash_table_lookup (dialog->priv->bars, name); + + if (bar != NULL) { + /* We only use a cached stream in the effects bar */ + if (G_UNLIKELY (bar != dialog->priv->effects_bar)) { + g_warn_if_reached (); + return; + } + + bar_set_stream (dialog, bar, NULL); + } +} + static gchar * -card_profile_status (MateMixerDeviceProfile *profile) +device_profile_status (MateMixerDeviceProfile *profile) { guint inputs; guint outputs; @@ -1099,7 +1051,7 @@ card_profile_status (MateMixerDeviceProfile *profile) inputs); } - if (inputs_str == NULL && outputs_str == NULL) { + if (inputs_str != NULL && outputs_str != NULL) { gchar *ret = g_strdup_printf ("%s / %s", outputs_str, inputs_str); @@ -1115,92 +1067,129 @@ card_profile_status (MateMixerDeviceProfile *profile) } static void -add_device (GvcMixerDialog *dialog, MateMixerDevice *device) +update_device_info (GvcMixerDialog *dialog, MateMixerDevice *device) { - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreeSelection *selection; + GtkTreeModel *model = NULL; + GtkTreeIter iter; MateMixerDeviceProfile *profile; - GIcon *icon; - const gchar *name; - const gchar *profile_name; - gchar *profile_status; + const gchar *description; + const gchar *profile_description = NULL; + gchar *profile_status = NULL; model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); - name = mate_mixer_device_get_name (device); - - if (find_tree_item_by_name (GTK_TREE_MODEL (model), name, HW_NAME_COLUMN, &iter) == FALSE) - gtk_list_store_append (GTK_LIST_STORE (model), &iter); - profile = mate_mixer_device_get_active_profile (device); - g_assert (profile != NULL); - icon = g_themed_icon_new_with_default_fallbacks (mate_mixer_device_get_icon (device)); + if (find_tree_item_by_name (model, + mate_mixer_device_get_name (device), + HW_NAME_COLUMN, + &iter) == FALSE) + return; - //FIXME we need the status (default for a profile?) here + description = mate_mixer_device_get_description (device); - profile_name = mate_mixer_device_profile_get_name (profile); - profile_status = card_profile_status (profile); + profile = mate_mixer_device_get_active_profile (device); + if (profile != NULL) { + profile_description = mate_mixer_device_profile_get_description (profile); + profile_status = device_profile_status (profile); + } gtk_list_store_set (GTK_LIST_STORE (model), &iter, - HW_NAME_COLUMN, mate_mixer_device_get_name (device), - HW_DESCRIPTION_COLUMN, mate_mixer_device_get_description (device), - HW_ICON_COLUMN, icon, - HW_PROFILE_COLUMN, profile_name, - HW_PROFILE_HUMAN_COLUMN, mate_mixer_device_profile_get_description (profile), + HW_DESCRIPTION_COLUMN, description, + HW_PROFILE_COLUMN, profile_description, HW_STATUS_COLUMN, profile_status, - HW_SENSITIVE_COLUMN, g_strcmp0 (profile_name, "off") != 0, -1); g_free (profile_status); +} - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->hw_treeview)); +static void +on_device_profile_notify (MateMixerDevice *device, + GParamSpec *pspec, + GvcMixerDialog *dialog) +{ + MateMixerDeviceProfile *profile; - if (gtk_tree_selection_get_selected (selection, NULL, NULL) == FALSE) { - /* Select the currently added item if nothing is selected now */ - gtk_tree_selection_select_iter (selection, &iter); - } else if (dialog->priv->hw_profile_combo != NULL) { - MateMixerDevice *selected; + g_debug ("Device profile has changed"); - /* Set the current profile if it changed for the selected card */ - selected = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), "device"); + g_signal_handlers_block_by_func (G_OBJECT (dialog->priv->hw_profile_combo), + G_CALLBACK (on_combo_box_profile_changed), + dialog); - if (!g_strcmp0 (mate_mixer_device_get_name (device), - mate_mixer_device_get_name (selected))) { - gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), - profile_name); + profile = mate_mixer_device_get_active_profile (device); + if (G_LIKELY (profile != NULL)) { + const gchar *name = mate_mixer_device_profile_get_name (profile); - g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), - "show-button", - mate_mixer_device_profile_get_num_output_streams (profile) >= 1, - NULL); - } + g_debug ("The current profile is %s", name); + + gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), + name); } + + g_signal_handlers_unblock_by_func (G_OBJECT (dialog->priv->hw_profile_combo), + G_CALLBACK (on_combo_box_profile_changed), + dialog); + + update_device_info (dialog, device); } static void -on_control_device_added (MateMixerControl *control, const gchar *name, GvcMixerDialog *dialog) +add_device (GvcMixerDialog *dialog, MateMixerDevice *device) { - MateMixerDevice *device; + GtkTreeModel *model; + GtkTreeIter iter; + MateMixerDeviceProfile *profile; + GIcon *icon; + const gchar *name; + const gchar *description; + const gchar *profile_description = NULL; + gchar *profile_status = NULL; - device = mate_mixer_control_get_device (control, name); - if (device != NULL) - add_device (dialog, device); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); + + name = mate_mixer_device_get_name (device); + description = mate_mixer_device_get_description (device); + + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + HW_NAME_COLUMN, + &iter) == FALSE) + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + + icon = g_themed_icon_new_with_default_fallbacks (mate_mixer_device_get_icon (device)); + + profile = mate_mixer_device_get_active_profile (device); + if (profile != NULL) { + profile_description = mate_mixer_device_profile_get_description (profile); + profile_status = device_profile_status (profile); + } + + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + HW_NAME_COLUMN, name, + HW_DESCRIPTION_COLUMN, description, + HW_ICON_COLUMN, icon, + HW_PROFILE_COLUMN, profile_description, + HW_STATUS_COLUMN, profile_status, + -1); + + g_free (profile_status); + + g_signal_connect (device, + "notify::active-profile", + G_CALLBACK (on_device_profile_notify), + dialog); } static void -remove_card (GvcMixerDialog *dialog, const gchar *name) +on_control_device_added (MateMixerControl *control, const gchar *name, GvcMixerDialog *dialog) { - gboolean found; - GtkTreeIter iter; - GtkTreeModel *model; + MateMixerDevice *device; - /* remove from any models */ - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); - found = find_tree_item_by_name (GTK_TREE_MODEL (model), name, HW_NAME_COLUMN, &iter); - if (found) { - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - } + device = mate_mixer_control_get_device (control, name); + if (G_UNLIKELY (device == NULL)) + return; + + add_device (dialog, device); } static void @@ -1208,43 +1197,54 @@ on_control_device_removed (MateMixerControl *control, const gchar *name, GvcMixerDialog *dialog) { - remove_card (dialog, name); + GtkTreeIter iter; + GtkTreeModel *model; + + /* Remove from any models */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); + + if (find_tree_item_by_name (GTK_TREE_MODEL (model), + name, + HW_NAME_COLUMN, + &iter) == TRUE) + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); } static void -_gtk_label_make_bold (GtkLabel *label) +make_label_bold (GtkLabel *label) { PangoFontDescription *font_desc; font_desc = pango_font_description_new (); - pango_font_description_set_weight (font_desc, - PANGO_WEIGHT_BOLD); + pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); /* This will only affect the weight of the font, the rest is * from the current state of the widget, which comes from the * theme or user prefs, since the font desc only has the - * weight flag turned on. - */ + * weight flag turned on. */ +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_override_font (GTK_WIDGET (label), font_desc); +#else gtk_widget_modify_font (GTK_WIDGET (label), font_desc); - +#endif pango_font_description_free (font_desc); } static void on_input_radio_toggled (GtkCellRendererToggle *renderer, - char *path_str, + gchar *path_str, GvcMixerDialog *dialog) { GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; - gboolean toggled; - gchar *name; + gboolean toggled = FALSE; + gchar *name = NULL; model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview)); + path = gtk_tree_path_new_from_string (path_str); - path = gtk_tree_path_new_from_string (path_str); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_path_free (path); @@ -1252,34 +1252,36 @@ on_input_radio_toggled (GtkCellRendererToggle *renderer, NAME_COLUMN, &name, ACTIVE_COLUMN, &toggled, -1); - if (toggled ^ 1) { MateMixerStream *stream; - g_debug ("Default input selected: %s", name); stream = mate_mixer_control_get_stream (dialog->priv->control, name); + if (G_UNLIKELY (stream == NULL)) { + g_warn_if_reached (); + g_free (name); + return; + } - if (stream != NULL) - mate_mixer_control_set_default_input_stream (dialog->priv->control, - stream); + g_debug ("Default input stream selection changed to %s", name); + mate_mixer_control_set_default_input_stream (dialog->priv->control, stream); } g_free (name); } static void on_output_radio_toggled (GtkCellRendererToggle *renderer, - char *path_str, + gchar *path_str, GvcMixerDialog *dialog) { GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; - gboolean toggled; - gchar *name; + gboolean toggled = FALSE; + gchar *name = NULL; model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview)); + path = gtk_tree_path_new_from_string (path_str); - path = gtk_tree_path_new_from_string (path_str); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_path_free (path); @@ -1287,44 +1289,74 @@ on_output_radio_toggled (GtkCellRendererToggle *renderer, NAME_COLUMN, &name, ACTIVE_COLUMN, &toggled, -1); - if (toggled ^ 1) { MateMixerStream *stream; - g_debug ("Default output selected: %s", name); stream = mate_mixer_control_get_stream (dialog->priv->control, name); - if (stream != NULL) - mate_mixer_control_set_default_output_stream (dialog->priv->control, - stream); + if (G_UNLIKELY (stream == NULL)) { + g_warn_if_reached (); + g_free (name); + return; + } + + g_debug ("Default output stream selection changed to %s", name); + mate_mixer_control_set_default_output_stream (dialog->priv->control, stream); } g_free (name); } static void -name_to_text (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) +stream_name_to_text (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) { - char *description, *mapping; + gchar *description; + gchar *speakers; gtk_tree_model_get (model, iter, DESCRIPTION_COLUMN, &description, - SPEAKERS_COLUMN, &mapping, + SPEAKERS_COLUMN, &speakers, -1); - if (mapping == NULL) { - g_object_set (cell, "text", description, NULL); - } else { - gchar *str = g_strdup_printf ("%s\n<i>%s</i>", description, mapping); + if (speakers != NULL) { + gchar *str = g_strdup_printf ("%s\n<i>%s</i>", + description, + speakers); g_object_set (cell, "markup", str, NULL); g_free (str); + g_free (speakers); + } else { + g_object_set (cell, "text", description, NULL); } g_free (description); - g_free (mapping); +} + +static gint +compare_stream_treeview_items (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *desc_a = NULL; + gchar *desc_b = NULL; + gint result; + + gtk_tree_model_get (model, a, + DESCRIPTION_COLUMN, &desc_a, + -1); + gtk_tree_model_get (model, b, + DESCRIPTION_COLUMN, &desc_b, + -1); + + result = g_ascii_strcasecmp (desc_a, desc_b); + + g_free (desc_a); + g_free (desc_b); + return result; } static GtkWidget * @@ -1342,7 +1374,6 @@ create_stream_treeview (GvcMixerDialog *dialog, GCallback on_toggled) G_TYPE_ICON, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING); @@ -1356,9 +1387,10 @@ create_stream_treeview (GvcMixerDialog *dialog, GCallback on_toggled) renderer, "active", ACTIVE_COLUMN, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); - g_signal_connect (renderer, + g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (on_toggled), dialog); @@ -1366,48 +1398,66 @@ create_stream_treeview (GvcMixerDialog *dialog, GCallback on_toggled) gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1, _("Name"), gtk_cell_renderer_text_new (), - name_to_text, NULL, NULL); + stream_name_to_text, + NULL, NULL); -#if 0 - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes (_("Device"), - renderer, - "text", DEVICE_COLUMN, - NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); -#endif + /* Keep the list of streams sorted by the name */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + DESCRIPTION_COLUMN, + GTK_SORT_ASCENDING); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + DESCRIPTION_COLUMN, + compare_stream_treeview_items, + NULL, NULL); return treeview; } -static void -on_profile_changed (GvcComboBox *widget, const gchar *profile, gpointer user_data) +static MateMixerStream * +find_device_test_stream (GvcMixerDialog *dialog, MateMixerDevice *device) { - MateMixerDevice *device; + GList *streams; - device = g_object_get_data (G_OBJECT (widget), "device"); - if (device == NULL) { - g_warning ("Could not find card for combobox"); - return; - } + streams = (GList *) mate_mixer_control_list_streams (dialog->priv->control); + while (streams) { + MateMixerStream *stream; - g_debug ("Profile changed to %s for device %s", - profile, - mate_mixer_device_get_name (device)); + stream = MATE_MIXER_STREAM (streams->data); - mate_mixer_device_set_active_profile (device, profile); + if (mate_mixer_stream_get_device (stream) == device && + mate_mixer_stream_get_num_channels (stream) >= 1) { + MateMixerStreamFlags flags; + + flags = mate_mixer_stream_get_flags (stream); + + if ((flags & (MATE_MIXER_STREAM_OUTPUT | + MATE_MIXER_STREAM_CLIENT)) == MATE_MIXER_STREAM_OUTPUT) + return stream; + } + streams = streams->next; + } + return FALSE; } static void -on_test_speakers_clicked (GvcComboBox *widget, gpointer user_data) +on_test_speakers_clicked (GvcComboBox *widget, GvcMixerDialog *dialog) { - GvcMixerDialog *dialog = GVC_MIXER_DIALOG (user_data); - MateMixerDevice *device; - GtkWidget *d, *speaker_test, *container; - char *title; + GtkWidget *d, + *test, + *container; + gchar *title; + MateMixerDevice *device; + MateMixerStream *stream; device = g_object_get_data (G_OBJECT (widget), "device"); - if (device == NULL) { - g_warning ("Could not find card for combobox"); + if (G_UNLIKELY (device == NULL)) { + g_warn_if_reached (); + return; + } + + stream = find_device_test_stream (dialog, device); + if (G_UNLIKELY (stream == NULL)) { + g_warn_if_reached (); return; } @@ -1418,92 +1468,139 @@ on_test_speakers_clicked (GvcComboBox *widget, gpointer user_data) GTK_WINDOW (dialog), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + "gtk-close", + GTK_RESPONSE_CLOSE, NULL); g_free (title); - speaker_test = gvc_speaker_test_new (dialog->priv->control, device); - gtk_widget_show (speaker_test); + gtk_window_set_resizable (GTK_WINDOW (d), FALSE); + + test = gvc_speaker_test_new (stream); + gtk_widget_show (test); + container = gtk_dialog_get_content_area (GTK_DIALOG (d)); - gtk_container_add (GTK_CONTAINER (container), speaker_test); + gtk_container_add (GTK_CONTAINER (container), test); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); } +static GtkWidget * +create_profile_combo_box (GvcMixerDialog *dialog, + MateMixerDevice *device, + const gchar *name, + const GList *items, + const gchar *active) +{ + GtkWidget *combobox; + + combobox = gvc_combo_box_new (name); + + gvc_combo_box_set_profiles (GVC_COMBO_BOX (combobox), items); + gvc_combo_box_set_active (GVC_COMBO_BOX (combobox), active); + + gvc_combo_box_set_size_group (GVC_COMBO_BOX (combobox), + dialog->priv->size_group, + FALSE); + + g_object_set_data_full (G_OBJECT (combobox), + "device", + g_object_ref (device), + g_object_unref); + + g_signal_connect (G_OBJECT (combobox), + "changed", + G_CALLBACK (on_combo_box_profile_changed), + dialog); + + return combobox; +} + static void -on_card_selection_changed (GtkTreeSelection *selection, - gpointer user_data) +on_device_selection_changed (GtkTreeSelection *selection, GvcMixerDialog *dialog) { - GvcMixerDialog *dialog = GVC_MIXER_DIALOG (user_data); - GtkTreeModel *model; GtkTreeIter iter; - const GList *profiles; - gchar *name; - MateMixerDevice *device; - MateMixerDeviceProfile *profile; + gchar *name; + MateMixerDevice *device; + MateMixerBackendType backend; - g_debug ("Card selection changed"); + g_debug ("Device selection changed"); if (dialog->priv->hw_profile_combo != NULL) { gtk_container_remove (GTK_CONTAINER (dialog->priv->hw_settings_box), dialog->priv->hw_profile_combo); + dialog->priv->hw_profile_combo = NULL; } if (gtk_tree_selection_get_selected (selection, NULL, &iter) == FALSE) return; - model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)); - gtk_tree_model_get (model, &iter, + gtk_tree_model_get (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview)), + &iter, HW_NAME_COLUMN, &name, -1); device = mate_mixer_control_get_device (dialog->priv->control, name); - if (device == NULL) { - g_warning ("Unable to find card for id: %s", name); + if (G_UNLIKELY (device == NULL)) { + g_warn_if_reached (); g_free (name); return; } g_free (name); - profile = mate_mixer_device_get_active_profile (device); - profiles = mate_mixer_device_list_profiles (device); - - dialog->priv->hw_profile_combo = gvc_combo_box_new (_("_Profile:")); - - g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), - "button-label", - _("Test Speakers"), - NULL); + backend = mate_mixer_control_get_backend_type (dialog->priv->control); + + /* Speaker test is only possible on PulseAudio and profile changing is + * not supported on other backends by libmatemixer, so avoid displaying + * the combo box on other backends */ + if (backend == MATE_MIXER_BACKEND_PULSEAUDIO) { + const GList *profiles; + const gchar *profile_name = NULL; + MateMixerStream *stream = NULL; + + profiles = mate_mixer_device_list_profiles (device); + if (profiles != NULL) { + MateMixerDeviceProfile *profile; + + profile = mate_mixer_device_get_active_profile (device); + if (G_UNLIKELY (profile == NULL)) { + /* Select the first profile if none is selected */ + profile = MATE_MIXER_DEVICE_PROFILE (profiles->data); + mate_mixer_device_set_active_profile (device, profile); + } - gvc_combo_box_set_profiles (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), - profiles); - gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), - mate_mixer_device_profile_get_name (profile)); + profile_name = mate_mixer_device_profile_get_name (profile); + } - gtk_box_pack_start (GTK_BOX (dialog->priv->hw_settings_box), - dialog->priv->hw_profile_combo, - TRUE, TRUE, 6); + dialog->priv->hw_profile_combo = + create_profile_combo_box (dialog, + device, + _("_Profile:"), + profiles, + profile_name); - g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), - "show-button", - mate_mixer_device_profile_get_num_output_streams (profile) >= 1, - NULL); + gtk_box_pack_start (GTK_BOX (dialog->priv->hw_settings_box), + dialog->priv->hw_profile_combo, + TRUE, TRUE, 6); - gtk_widget_show (dialog->priv->hw_profile_combo); + /* Enable the speaker test button if the selected device + * is capable of sound output */ + stream = find_device_test_stream (dialog, device); + if (stream != NULL) { + g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), + "show-button", TRUE, + "button-label", _("Test Speakers"), + NULL); - g_object_set_data (G_OBJECT (dialog->priv->hw_profile_combo), - "device", - g_object_ref (device)); + g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), + "button-clicked", + G_CALLBACK (on_test_speakers_clicked), + dialog); + } - g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), - "changed", - G_CALLBACK (on_profile_changed), - dialog); - g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), - "button-clicked", - G_CALLBACK (on_test_speakers_clicked), - dialog); + gtk_widget_show (dialog->priv->hw_profile_combo); + } } static void @@ -1525,36 +1622,71 @@ on_notebook_switch_page (GtkNotebook *notebook, } static void -card_to_text (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) +device_name_to_text (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) { - gchar *description, *status, *profile, *str; - gboolean sensitive; + gchar *description = NULL; + gchar *profile = NULL; + gchar *profile_status = NULL; gtk_tree_model_get (model, iter, HW_DESCRIPTION_COLUMN, &description, - HW_STATUS_COLUMN, &status, - HW_PROFILE_HUMAN_COLUMN, &profile, - HW_SENSITIVE_COLUMN, &sensitive, + HW_PROFILE_COLUMN, &profile, + HW_STATUS_COLUMN, &profile_status, -1); - str = g_strdup_printf ("%s\n<i>%s</i>\n<i>%s</i>", description, status, profile); - g_object_set (cell, - "markup", str, - "sensitive", sensitive, - NULL); - g_free (str); + if (profile != NULL) { + gchar *str; + + if (G_LIKELY (profile_status != NULL)) + str = g_strdup_printf ("%s\n<i>%s</i>\n<i>%s</i>", + description, + profile_status, + profile); + else + str = g_strdup_printf ("%s\n<i>%s</i>", + description, + profile); + + g_object_set (cell, "markup", str, NULL); + g_free (str); + g_free (profile); + g_free (profile_status); + } else + g_object_set (cell, "text", description, NULL); g_free (description); - g_free (status); - g_free (profile); +} + +static gint +compare_device_treeview_items (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *desc_a = NULL; + gchar *desc_b = NULL; + gint result; + + gtk_tree_model_get (model, a, + HW_DESCRIPTION_COLUMN, &desc_a, + -1); + gtk_tree_model_get (model, b, + HW_DESCRIPTION_COLUMN, &desc_b, + -1); + + result = g_ascii_strcasecmp (desc_a, desc_b); + + g_free (desc_a); + g_free (desc_b); + return result; } static GtkWidget * -create_cards_treeview (GvcMixerDialog *dialog, GCallback on_changed) +create_device_treeview (GvcMixerDialog *dialog, GCallback on_changed) { GtkWidget *treeview; GtkListStore *store; @@ -1576,37 +1708,41 @@ create_cards_treeview (GvcMixerDialog *dialog, GCallback on_changed) G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_BOOLEAN); + G_TYPE_STRING); gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); renderer = gtk_cell_renderer_pixbuf_new (); - g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DIALOG, NULL); + g_object_set (G_OBJECT (renderer), + "stock-size", + GTK_ICON_SIZE_DIALOG, + NULL); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "gicon", HW_ICON_COLUMN, - "sensitive", HW_SENSITIVE_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); - gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1, _("Name"), gtk_cell_renderer_text_new (), - card_to_text, + device_name_to_text, NULL, NULL); + /* Keep the list of streams sorted by the name */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + HW_DESCRIPTION_COLUMN, + GTK_SORT_ASCENDING); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + HW_DESCRIPTION_COLUMN, + compare_device_treeview_items, + NULL, NULL); return treeview; } -static const guint tab_accel_keys[] = { - GDK_1, GDK_2, GDK_3, GDK_4, GDK_5 -}; - static void dialog_accel_cb (GtkAccelGroup *accelgroup, GObject *object, @@ -1641,17 +1777,19 @@ gvc_mixer_dialog_constructor (GType type, GtkWidget *box; GtkWidget *sbox; GtkWidget *ebox; - GList *list; GtkTreeSelection *selection; GtkAccelGroup *accel_group; GClosure *closure; - gint i; + GtkTreeIter iter; + gint i; + GList *list; object = G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_DIALOG (object); + gtk_dialog_add_button (GTK_DIALOG (self), "gtk-close", GTK_RESPONSE_OK); main_vbox = gtk_dialog_get_content_area (GTK_DIALOG (self)); @@ -1659,17 +1797,25 @@ gvc_mixer_dialog_constructor (GType type, gtk_container_set_border_width (GTK_CONTAINER (self), 6); +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->output_stream_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); +#else self->priv->output_stream_box = gtk_hbox_new (FALSE, 12); +#endif + alignment = gtk_alignment_new (0, 0, 1, 1); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0); gtk_container_add (GTK_CONTAINER (alignment), self->priv->output_stream_box); gtk_box_pack_start (GTK_BOX (main_vbox), alignment, FALSE, FALSE, 0); - self->priv->output_bar = create_bar (self, self->priv->size_group, TRUE); + + self->priv->output_bar = create_bar (self, TRUE, TRUE); gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->output_bar), _("_Output volume: ")); + gtk_widget_set_sensitive (self->priv->output_bar, FALSE); + gtk_box_pack_start (GTK_BOX (self->priv->output_stream_box), self->priv->output_bar, TRUE, TRUE, 12); @@ -1678,6 +1824,11 @@ gvc_mixer_dialog_constructor (GType type, self->priv->notebook, TRUE, TRUE, 0); + g_signal_connect (G_OBJECT (self->priv->notebook), + "switch-page", + G_CALLBACK (on_notebook_switch_page), + self); + gtk_container_set_border_width (GTK_CONTAINER (self->priv->notebook), 5); /* Set up accels (borrowed from Empathy) */ @@ -1685,9 +1836,9 @@ gvc_mixer_dialog_constructor (GType type, gtk_window_add_accel_group (GTK_WINDOW (self), accel_group); for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { - closure = g_cclosure_new (G_CALLBACK (dialog_accel_cb), - self, - NULL); + closure = g_cclosure_new (G_CALLBACK (dialog_accel_cb), + self, + NULL); gtk_accel_group_connect (accel_group, tab_accel_keys[i], GDK_MOD1_MASK, @@ -1697,29 +1848,44 @@ gvc_mixer_dialog_constructor (GType type, g_object_unref (accel_group); - /* Effects page */ + /* Create notebook pages */ +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->sound_effects_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); +#else self->priv->sound_effects_box = gtk_vbox_new (FALSE, 6); +#endif gtk_container_set_border_width (GTK_CONTAINER (self->priv->sound_effects_box), 12); + label = gtk_label_new (_("Sound Effects")); gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), self->priv->sound_effects_box, label); - self->priv->effects_bar = create_bar (self, self->priv->size_group, TRUE); + self->priv->effects_bar = create_bar (self, TRUE, TRUE); + gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box), + self->priv->effects_bar, FALSE, FALSE, 0); + + gvc_channel_bar_set_show_marks (GVC_CHANNEL_BAR (self->priv->effects_bar), FALSE); + gvc_channel_bar_set_extended (GVC_CHANNEL_BAR (self->priv->effects_bar), FALSE); + gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->effects_bar), _("_Alert volume: ")); + gtk_widget_set_sensitive (self->priv->effects_bar, FALSE); - gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box), - self->priv->effects_bar, FALSE, FALSE, 0); self->priv->sound_theme_chooser = gvc_sound_theme_chooser_new (); + gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box), self->priv->sound_theme_chooser, TRUE, TRUE, 6); - /* Hardware page */ +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->hw_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else self->priv->hw_box = gtk_vbox_new (FALSE, 12); +#endif gtk_container_set_border_width (GTK_CONTAINER (self->priv->hw_box), 12); + label = gtk_label_new (_("Hardware")); gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), self->priv->hw_box, @@ -1727,7 +1893,7 @@ gvc_mixer_dialog_constructor (GType type, box = gtk_frame_new (_("C_hoose a device to configure:")); label = gtk_frame_get_label_widget (GTK_FRAME (box)); - _gtk_label_make_bold (GTK_LABEL (label)); + make_label_bold (GTK_LABEL (label)); gtk_label_set_use_underline (GTK_LABEL (label), TRUE); gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, TRUE, TRUE, 0); @@ -1736,8 +1902,8 @@ gvc_mixer_dialog_constructor (GType type, gtk_container_add (GTK_CONTAINER (box), alignment); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); - self->priv->hw_treeview = create_cards_treeview (self, - G_CALLBACK (on_card_selection_changed)); + self->priv->hw_treeview = create_device_treeview (self, + G_CALLBACK (on_device_selection_changed)); gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->hw_treeview); box = gtk_scrolled_window_new (NULL, NULL); @@ -1754,21 +1920,32 @@ gvc_mixer_dialog_constructor (GType type, box = gtk_frame_new (_("Settings for the selected device:")); label = gtk_frame_get_label_widget (GTK_FRAME (box)); - _gtk_label_make_bold (GTK_LABEL (label)); + make_label_bold (GTK_LABEL (label)); gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, FALSE, TRUE, 12); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->hw_settings_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else self->priv->hw_settings_box = gtk_vbox_new (FALSE, 12); +#endif + gtk_container_add (GTK_CONTAINER (box), self->priv->hw_settings_box); - /* Input page */ +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->input_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else self->priv->input_box = gtk_vbox_new (FALSE, 12); +#endif + gtk_container_set_border_width (GTK_CONTAINER (self->priv->input_box), 12); + label = gtk_label_new (_("Input")); gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), self->priv->input_box, label); - self->priv->input_bar = create_bar (self, self->priv->size_group, TRUE); + self->priv->input_bar = create_bar (self, TRUE, TRUE); gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->input_bar), _("_Input volume: ")); gvc_channel_bar_set_low_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar), @@ -1776,6 +1953,7 @@ gvc_mixer_dialog_constructor (GType type, gvc_channel_bar_set_high_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar), "audio-input-microphone-high"); gtk_widget_set_sensitive (self->priv->input_bar, FALSE); + alignment = gtk_alignment_new (0, 0, 1, 1); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); gtk_container_add (GTK_CONTAINER (alignment), self->priv->input_bar); @@ -1783,12 +1961,19 @@ gvc_mixer_dialog_constructor (GType type, alignment, FALSE, FALSE, 0); - box = gtk_hbox_new (FALSE, 6); +#if GTK_CHECK_VERSION (3, 0, 0) + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + sbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + ebox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); +#else + box = gtk_hbox_new (FALSE, 6); + sbox = gtk_hbox_new (FALSE, 6); + ebox = gtk_hbox_new (FALSE, 6); +#endif + gtk_box_pack_start (GTK_BOX (self->priv->input_box), box, FALSE, FALSE, 6); - - sbox = gtk_hbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0); @@ -1808,20 +1993,23 @@ gvc_mixer_dialog_constructor (GType type, self->priv->input_level_bar, TRUE, TRUE, 6); - ebox = gtk_hbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, FALSE, 0); gtk_size_group_add_widget (self->priv->size_group, ebox); +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->input_settings_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); +#else self->priv->input_settings_box = gtk_hbox_new (FALSE, 6); +#endif gtk_box_pack_start (GTK_BOX (self->priv->input_box), self->priv->input_settings_box, FALSE, FALSE, 0); box = gtk_frame_new (_("C_hoose a device for sound input:")); label = gtk_frame_get_label_widget (GTK_FRAME (box)); - _gtk_label_make_bold (GTK_LABEL (label)); + make_label_bold (GTK_LABEL (label)); gtk_label_set_use_underline (GTK_LABEL (label), TRUE); gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (self->priv->input_box), box, TRUE, TRUE, 0); @@ -1830,8 +2018,9 @@ gvc_mixer_dialog_constructor (GType type, gtk_container_add (GTK_CONTAINER (box), alignment); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); - self->priv->input_treeview = create_stream_treeview (self, - G_CALLBACK (on_input_radio_toggled)); + self->priv->input_treeview = + create_stream_treeview (self, G_CALLBACK (on_input_radio_toggled)); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->input_treeview); box = gtk_scrolled_window_new (NULL, NULL); @@ -1856,7 +2045,7 @@ gvc_mixer_dialog_constructor (GType type, box = gtk_frame_new (_("C_hoose a device for sound output:")); label = gtk_frame_get_label_widget (GTK_FRAME (box)); - _gtk_label_make_bold (GTK_LABEL (label)); + make_label_bold (GTK_LABEL (label)); gtk_label_set_use_underline (GTK_LABEL (label), TRUE); gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, TRUE, TRUE, 0); @@ -1883,26 +2072,43 @@ gvc_mixer_dialog_constructor (GType type, box = gtk_frame_new (_("Settings for the selected device:")); label = gtk_frame_get_label_widget (GTK_FRAME (box)); - _gtk_label_make_bold (GTK_LABEL (label)); + make_label_bold (GTK_LABEL (label)); gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, FALSE, FALSE, 12); self->priv->output_settings_box = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (box), self->priv->output_settings_box); + self->priv->output_settings_frame = box; + /* Applications */ - self->priv->applications_scrolled_window = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->applications_scrolled_window), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self->priv->applications_scrolled_window), + self->priv->applications_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->applications_window), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self->priv->applications_window), GTK_SHADOW_IN); + +#if GTK_CHECK_VERSION (3, 0, 0) + self->priv->applications_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); +#else self->priv->applications_box = gtk_vbox_new (FALSE, 12); +#endif gtk_container_set_border_width (GTK_CONTAINER (self->priv->applications_box), 12); - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (self->priv->applications_scrolled_window), + +#if GTK_CHECK_VERSION (3, 8, 0) + gtk_container_add (GTK_CONTAINER (self->priv->applications_window), + self->priv->applications_box); +#else + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (self->priv->applications_window), self->priv->applications_box); +#endif + label = gtk_label_new (_("Applications")); gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook), - self->priv->applications_scrolled_window, + self->priv->applications_window, label); + self->priv->no_apps_label = gtk_label_new (_("No application is currently playing or recording audio.")); gtk_box_pack_start (GTK_BOX (self->priv->applications_box), self->priv->no_apps_label, @@ -1910,6 +2116,8 @@ gvc_mixer_dialog_constructor (GType type, gtk_widget_show_all (main_vbox); + // XXX subscribe to cached stream stuff, in case event stream is not found + list = (GList *) mate_mixer_control_list_streams (self->priv->control); while (list) { add_stream (self, MATE_MIXER_STREAM (list->data)); @@ -1922,10 +2130,30 @@ gvc_mixer_dialog_constructor (GType type, list = list->next; } - g_signal_connect (G_OBJECT (self->priv->notebook), - "switch-page", - G_CALLBACK (on_notebook_switch_page), - self); + /* Find an event role stream */ + list = (GList *) mate_mixer_control_list_cached_streams (self->priv->control); + while (list) { + MateMixerClientStreamRole role; + + role = mate_mixer_client_stream_get_role (MATE_MIXER_CLIENT_STREAM (list->data)); + + if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) + add_stream (self, MATE_MIXER_STREAM (list->data)); + + list = list->next; + } + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview)); + + /* Select the first device in the list */ + // XXX handle no devices + if (gtk_tree_selection_get_selected (selection, NULL, NULL) == FALSE) { + GtkTreeModel *model = + gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->hw_treeview)); + + if (gtk_tree_model_get_iter_first (model, &iter)) + gtk_tree_selection_select_iter (selection, &iter); + } return object; } @@ -1948,28 +2176,36 @@ gvc_mixer_dialog_set_control (GvcMixerDialog *dialog, MateMixerControl *control) dialog->priv->control = g_object_ref (control); - g_signal_connect (dialog->priv->control, + g_signal_connect (G_OBJECT (dialog->priv->control), "stream-added", G_CALLBACK (on_control_stream_added), dialog); - g_signal_connect (dialog->priv->control, + g_signal_connect (G_OBJECT (dialog->priv->control), + "cached-stream-added", + G_CALLBACK (on_control_stream_added), + dialog); + g_signal_connect (G_OBJECT (dialog->priv->control), "stream-removed", G_CALLBACK (on_control_stream_removed), dialog); - g_signal_connect (dialog->priv->control, + g_signal_connect (G_OBJECT (dialog->priv->control), + "cached-stream-removed", + G_CALLBACK (on_control_cached_stream_removed), + dialog); + g_signal_connect (G_OBJECT (dialog->priv->control), "device-added", G_CALLBACK (on_control_device_added), dialog); - g_signal_connect (dialog->priv->control, + g_signal_connect (G_OBJECT (dialog->priv->control), "device-removed", G_CALLBACK (on_control_device_removed), dialog); - g_signal_connect (dialog->priv->control, + g_signal_connect (G_OBJECT (dialog->priv->control), "notify::default-output", G_CALLBACK (on_control_default_output_notify), dialog); - g_signal_connect (dialog->priv->control, + g_signal_connect (G_OBJECT (dialog->priv->control), "notify::default-input", G_CALLBACK (on_control_default_input_notify), dialog); @@ -1986,7 +2222,7 @@ gvc_mixer_dialog_set_property (GObject *object, GvcMixerDialog *self = GVC_MIXER_DIALOG (object); switch (prop_id) { - case PROP_MIXER_CONTROL: + case PROP_CONTROL: gvc_mixer_dialog_set_control (self, g_value_get_object (value)); break; default: @@ -2004,7 +2240,7 @@ gvc_mixer_dialog_get_property (GObject *object, GvcMixerDialog *self = GVC_MIXER_DIALOG (object); switch (prop_id) { - case PROP_MIXER_CONTROL: + case PROP_CONTROL: g_value_set_object (value, gvc_mixer_dialog_get_control (self)); break; default: @@ -2050,7 +2286,7 @@ gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass) object_class->get_property = gvc_mixer_dialog_get_property; g_object_class_install_property (object_class, - PROP_MIXER_CONTROL, + PROP_CONTROL, g_param_spec_object ("control", "Control", "MateMixer control", @@ -2068,7 +2304,6 @@ gvc_mixer_dialog_init (GvcMixerDialog *dialog) dialog->priv->bars = g_hash_table_new (g_str_hash, g_str_equal); dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); - dialog->priv->apps_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); } static void @@ -2102,7 +2337,7 @@ gvc_mixer_dialog_set_page (GvcMixerDialog *self, const gchar *page) if (page != NULL) { if (g_str_equal (page, "effects")) - num = PAGE_EVENTS; + num = PAGE_EFFECTS; else if (g_str_equal (page, "hardware")) num = PAGE_HARDWARE; else if (g_str_equal (page, "input")) diff --git a/mate-volume-control/src/gvc-mixer-dialog.h b/mate-volume-control/src/gvc-mixer-dialog.h index cc6e665..c494c1a 100644 --- a/mate-volume-control/src/gvc-mixer-dialog.h +++ b/mate-volume-control/src/gvc-mixer-dialog.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -23,6 +24,7 @@ #include <glib.h> #include <glib-object.h> + #include <libmatemixer/matemixer.h> G_BEGIN_DECLS diff --git a/mate-volume-control/src/gvc-speaker-test.c b/mate-volume-control/src/gvc-speaker-test.c index 25b836c..61fd510 100644 --- a/mate-volume-control/src/gvc-speaker-test.c +++ b/mate-volume-control/src/gvc-speaker-test.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2009 Bastien Nocera + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -22,7 +23,9 @@ #include <glib.h> #include <glib/gi18n.h> +#include <glib-object.h> #include <gtk/gtk.h> + #include <canberra.h> #include <libmatemixer/matemixer.h> @@ -31,51 +34,88 @@ #endif #include "gvc-speaker-test.h" +#include "mvc-helpers.h" #define GVC_SPEAKER_TEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestPrivate)) struct _GvcSpeakerTestPrivate { - // XXX pulse constant - GtkWidget *channel_controls[PA_CHANNEL_POSITION_MAX]; + GArray *controls; ca_context *canberra; MateMixerControl *control; - MateMixerDevice *device; + MateMixerStream *stream; }; enum { PROP_0, - PROP_CONTROL, - PROP_DEVICE, + PROP_STREAM, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES] = { NULL, }; -static void gvc_speaker_test_class_init (GvcSpeakerTestClass *klass); -static void gvc_speaker_test_init (GvcSpeakerTest *speaker_test); -static void gvc_speaker_test_dispose (GObject *object); -static void gvc_speaker_test_finalize (GObject *object); -static void update_channel_map (GvcSpeakerTest *speaker_test); +static void gvc_speaker_test_class_init (GvcSpeakerTestClass *klass); +static void gvc_speaker_test_init (GvcSpeakerTest *test); +static void gvc_speaker_test_dispose (GObject *object); +static void gvc_speaker_test_finalize (GObject *object); +#if GTK_CHECK_VERSION (3, 4, 0) +G_DEFINE_TYPE (GvcSpeakerTest, gvc_speaker_test, GTK_TYPE_GRID) +#else G_DEFINE_TYPE (GvcSpeakerTest, gvc_speaker_test, GTK_TYPE_TABLE) +#endif -static const int position_table[] = { +typedef struct { + MateMixerChannelPosition position; + guint left; + guint top; +} TablePosition; + +static const TablePosition positions[] = { /* Position, X, Y */ - MATE_MIXER_CHANNEL_FRONT_LEFT, 0, 0, - MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, 1, 0, - MATE_MIXER_CHANNEL_FRONT_CENTER, 2, 0, - MATE_MIXER_CHANNEL_MONO, 2, 0, - MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, 3, 0, - MATE_MIXER_CHANNEL_FRONT_RIGHT, 4, 0, - MATE_MIXER_CHANNEL_SIDE_LEFT, 0, 1, - MATE_MIXER_CHANNEL_SIDE_RIGHT, 4, 1, - MATE_MIXER_CHANNEL_BACK_LEFT, 0, 2, - MATE_MIXER_CHANNEL_BACK_CENTER, 2, 2, - MATE_MIXER_CHANNEL_BACK_RIGHT, 4, 2, - MATE_MIXER_CHANNEL_LFE, 3, 2 + { MATE_MIXER_CHANNEL_FRONT_LEFT, 0, 0, }, + { MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER, 1, 0, }, + { MATE_MIXER_CHANNEL_FRONT_CENTER, 2, 0, }, + { MATE_MIXER_CHANNEL_MONO, 2, 0, }, + { MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER, 3, 0, }, + { MATE_MIXER_CHANNEL_FRONT_RIGHT, 4, 0, }, + { MATE_MIXER_CHANNEL_SIDE_LEFT, 0, 1, }, + { MATE_MIXER_CHANNEL_SIDE_RIGHT, 4, 1, }, + { MATE_MIXER_CHANNEL_BACK_LEFT, 0, 2, }, + { MATE_MIXER_CHANNEL_BACK_CENTER, 2, 2, }, + { MATE_MIXER_CHANNEL_BACK_RIGHT, 4, 2, }, + { MATE_MIXER_CHANNEL_LFE, 3, 2 } }; +MateMixerStream * +gvc_speaker_test_get_stream (GvcSpeakerTest *test) +{ + g_return_val_if_fail (GVC_IS_SPEAKER_TEST (test), NULL); + + return test->priv->stream; +} + +static void +gvc_speaker_test_set_stream (GvcSpeakerTest *test, MateMixerStream *stream) +{ + guint i; + const gchar *name; + + name = mate_mixer_stream_get_name (stream); + + ca_context_change_device (test->priv->canberra, name); + + for (i = 0; i < G_N_ELEMENTS (positions); i++) { + gboolean has_position = + mate_mixer_stream_has_channel_position (stream, positions[i].position); + + gtk_widget_set_visible (g_array_index (test->priv->controls, GtkWidget *, i), + has_position); + } + + test->priv->stream = g_object_ref (stream); +} + static void gvc_speaker_test_set_property (GObject *object, guint prop_id, @@ -85,20 +125,8 @@ gvc_speaker_test_set_property (GObject *object, GvcSpeakerTest *self = GVC_SPEAKER_TEST (object); switch (prop_id) { - case PROP_CONTROL: - /* Construct-only object */ - self->priv->control = g_value_dup_object (value); - if (self->priv->control != NULL) - update_channel_map (self); - break; - - case PROP_DEVICE: - if (self->priv->device) - g_object_unref (self->priv->device); - - self->priv->device = g_value_dup_object (value); - if (self->priv->device != NULL) - update_channel_map (self); + case PROP_STREAM: + gvc_speaker_test_set_stream (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -115,11 +143,8 @@ gvc_speaker_test_get_property (GObject *object, GvcSpeakerTest *self = GVC_SPEAKER_TEST (object); switch (prop_id) { - case PROP_CONTROL: - g_value_set_object (value, self->priv->control); - break; - case PROP_DEVICE: - g_value_set_object (value, self->priv->device); + case PROP_STREAM: + g_value_set_object (value, self->priv->stream); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -137,19 +162,14 @@ gvc_speaker_test_class_init (GvcSpeakerTestClass *klass) object_class->set_property = gvc_speaker_test_set_property; object_class->get_property = gvc_speaker_test_get_property; - properties[PROP_CONTROL] = - g_param_spec_object ("control", - "Control", - "The mixer controller", - MATE_MIXER_TYPE_CONTROL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); - - properties[PROP_DEVICE] = - g_param_spec_object ("device", - "Device", - "The mixer device", - MATE_MIXER_TYPE_DEVICE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + properties[PROP_STREAM] = + g_param_spec_object ("stream", + "Stream", + "MateMixer stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPERTIES, properties); @@ -188,23 +208,41 @@ icon_name (MateMixerChannelPosition position, gboolean playing) { switch (position) { case MATE_MIXER_CHANNEL_FRONT_LEFT: - return playing ? "audio-speaker-left-testing" : "audio-speaker-left"; + return playing + ? "audio-speaker-left-testing" + : "audio-speaker-left"; case MATE_MIXER_CHANNEL_FRONT_RIGHT: - return playing ? "audio-speaker-right-testing" : "audio-speaker-right"; + return playing + ? "audio-speaker-right-testing" + : "audio-speaker-right"; case MATE_MIXER_CHANNEL_FRONT_CENTER: - return playing ? "audio-speaker-center-testing" : "audio-speaker-center"; + return playing + ? "audio-speaker-center-testing" + : "audio-speaker-center"; case MATE_MIXER_CHANNEL_BACK_LEFT: - return playing ? "audio-speaker-left-back-testing" : "audio-speaker-left-back"; + return playing + ? "audio-speaker-left-back-testing" + : "audio-speaker-left-back"; case MATE_MIXER_CHANNEL_BACK_RIGHT: - return playing ? "audio-speaker-right-back-testing" : "audio-speaker-right-back"; + return playing + ? "audio-speaker-right-back-testing" + : "audio-speaker-right-back"; case MATE_MIXER_CHANNEL_BACK_CENTER: - return playing ? "audio-speaker-center-back-testing" : "audio-speaker-center-back"; + return playing + ? "audio-speaker-center-back-testing" + : "audio-speaker-center-back"; case MATE_MIXER_CHANNEL_LFE: - return playing ? "audio-subwoofer-testing" : "audio-subwoofer"; + return playing + ? "audio-subwoofer-testing" + : "audio-subwoofer"; case MATE_MIXER_CHANNEL_SIDE_LEFT: - return playing ? "audio-speaker-left-side-testing" : "audio-speaker-left-side"; + return playing + ? "audio-speaker-left-side-testing" + : "audio-speaker-left-side"; case MATE_MIXER_CHANNEL_SIDE_RIGHT: - return playing ? "audio-speaker-right-side-testing" : "audio-speaker-right-side"; + return playing + ? "audio-speaker-right-side-testing" + : "audio-speaker-right-side"; default: return NULL; } @@ -215,92 +253,20 @@ update_button (GtkWidget *control) { GtkWidget *button; GtkWidget *image; - pa_channel_position_t position; - gboolean playing; + gboolean playing; + MateMixerChannelPosition position; button = g_object_get_data (G_OBJECT (control), "button"); - image = g_object_get_data (G_OBJECT (control), "image"); - position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position")); - playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing")); - gtk_button_set_label (GTK_BUTTON (button), playing ? _("Stop") : _("Test")); - gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name (position, playing), GTK_ICON_SIZE_DIALOG); -} + image = g_object_get_data (G_OBJECT (control), "image"); -static const char * -channel_position_string (MateMixerChannelPosition position, gboolean pretty) -{ -#ifdef HAVE_PULSEAUDIO - pa_channel_position_t pa_position; + position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position")); + playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing")); - switch (position) { - case MATE_MIXER_CHANNEL_MONO: - pa_position = PA_CHANNEL_POSITION_MONO; - break; - case MATE_MIXER_CHANNEL_FRONT_LEFT: - pa_position = PA_CHANNEL_POSITION_FRONT_LEFT; - break; - case MATE_MIXER_CHANNEL_FRONT_RIGHT: - pa_position = PA_CHANNEL_POSITION_FRONT_RIGHT; - break; - case MATE_MIXER_CHANNEL_FRONT_CENTER: - pa_position = PA_CHANNEL_POSITION_FRONT_CENTER; - break; - case MATE_MIXER_CHANNEL_LFE: - pa_position = PA_CHANNEL_POSITION_LFE; - break; - case MATE_MIXER_CHANNEL_BACK_LEFT: - pa_position = PA_CHANNEL_POSITION_REAR_LEFT; - break; - case MATE_MIXER_CHANNEL_BACK_RIGHT: - pa_position = PA_CHANNEL_POSITION_REAR_RIGHT; - break; - case MATE_MIXER_CHANNEL_BACK_CENTER: - pa_position = PA_CHANNEL_POSITION_REAR_CENTER; - break; - case MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER: - pa_position = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - break; - case MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER: - pa_position = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - break; - case MATE_MIXER_CHANNEL_SIDE_LEFT: - pa_position = PA_CHANNEL_POSITION_SIDE_LEFT; - break; - case MATE_MIXER_CHANNEL_SIDE_RIGHT: - pa_position = PA_CHANNEL_POSITION_SIDE_RIGHT; - break; - case MATE_MIXER_CHANNEL_TOP_FRONT_LEFT: - pa_position = PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - break; - case MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT: - pa_position = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - break; - case MATE_MIXER_CHANNEL_TOP_FRONT_CENTER: - pa_position = PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - break; - case MATE_MIXER_CHANNEL_TOP_CENTER: - pa_position = PA_CHANNEL_POSITION_TOP_CENTER; - break; - case MATE_MIXER_CHANNEL_TOP_BACK_LEFT: - pa_position = PA_CHANNEL_POSITION_TOP_REAR_LEFT; - break; - case MATE_MIXER_CHANNEL_TOP_BACK_RIGHT: - pa_position = PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - break; - case MATE_MIXER_CHANNEL_TOP_BACK_CENTER: - pa_position = PA_CHANNEL_POSITION_TOP_REAR_CENTER; - break; - default: - pa_position = PA_CHANNEL_POSITION_INVALID; - break; - } + gtk_button_set_label (GTK_BUTTON (button), playing ? _("Stop") : _("Test")); - if (pretty) - return pa_channel_position_to_pretty_string (pa_position); - else - return pa_channel_position_to_string (pa_position); -#endif - return NULL; + gtk_image_set_from_icon_name (GTK_IMAGE (image), + icon_name (position, playing), + GTK_ICON_SIZE_DIALOG); } static gboolean @@ -343,19 +309,20 @@ on_test_button_clicked (GtkButton *button, GtkWidget *control) g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); } else { MateMixerChannelPosition position; - const char *name; + const gchar *name; ca_proplist *proplist; position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position")); ca_proplist_create (&proplist); - ca_proplist_sets (proplist, CA_PROP_MEDIA_ROLE, "test"); + ca_proplist_sets (proplist, + CA_PROP_MEDIA_ROLE, "test"); ca_proplist_sets (proplist, CA_PROP_MEDIA_NAME, - channel_position_string (position, TRUE)); + mvc_channel_position_to_pretty_string (position)); ca_proplist_sets (proplist, CA_PROP_CANBERRA_FORCE_CHANNEL, - channel_position_string (position, FALSE)); + mvc_channel_position_to_string (position)); ca_proplist_sets (proplist, CA_PROP_CANBERRA_ENABLE, "1"); @@ -374,23 +341,30 @@ on_test_button_clicked (GtkButton *button, GtkWidget *control) ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "bell-window-system"); playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0; } - g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER(playing)); + + g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (playing)); } update_button (control); } static GtkWidget * -channel_control_new (ca_context *canberra, MateMixerChannelPosition position) +create_control (ca_context *canberra, MateMixerChannelPosition position) { - GtkWidget *control; - GtkWidget *box; - GtkWidget *label; - GtkWidget *image; - GtkWidget *test_button; - const char *name; - + GtkWidget *control; + GtkWidget *box; + GtkWidget *label; + GtkWidget *image; + GtkWidget *test_button; + const gchar *name; + +#if GTK_CHECK_VERSION (3, 0, 0) + control = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); +#else control = gtk_vbox_new (FALSE, 6); + box = gtk_hbox_new (FALSE, 0); +#endif g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER (FALSE)); g_object_set_data (G_OBJECT (control), "position", GINT_TO_POINTER (position)); g_object_set_data (G_OBJECT (control), "canberra", canberra); @@ -403,16 +377,17 @@ channel_control_new (ca_context *canberra, MateMixerChannelPosition position) g_object_set_data (G_OBJECT (control), "image", image); gtk_box_pack_start (GTK_BOX (control), image, FALSE, FALSE, 0); - label = gtk_label_new (channel_position_string (position, TRUE)); + label = gtk_label_new (mvc_channel_position_to_pretty_string (position)); gtk_box_pack_start (GTK_BOX (control), label, FALSE, FALSE, 0); test_button = gtk_button_new_with_label (_("Test")); - g_signal_connect (G_OBJECT (test_button), "clicked", - G_CALLBACK (on_test_button_clicked), control); + g_signal_connect (G_OBJECT (test_button), + "clicked", + G_CALLBACK (on_test_button_clicked), + control); g_object_set_data (G_OBJECT (control), "button", test_button); - box = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (box), test_button, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (control), box, FALSE, FALSE, 0); @@ -422,80 +397,60 @@ channel_control_new (ca_context *canberra, MateMixerChannelPosition position) } static void -create_channel_controls (GvcSpeakerTest *test) +create_controls (GvcSpeakerTest *test) { guint i; - for (i = 0; i < G_N_ELEMENTS (position_table); i += 3) { - test->priv->channel_controls[position_table[i]] = - channel_control_new (test->priv->canberra, - (MateMixerChannelPosition) position_table[i]); + for (i = 0; i < G_N_ELEMENTS (positions); i++) { + GtkWidget *control = create_control (test->priv->canberra, positions[i].position); +#if GTK_CHECK_VERSION (3, 4, 0) + gtk_grid_attach (GTK_GRID (test), + control, + positions[i].left, + positions[i].top, + 1, 1); +#else gtk_table_attach (GTK_TABLE (test), - test->priv->channel_controls[position_table[i]], - position_table[i+1], - position_table[i+1]+1, - position_table[i+2], - position_table[i+2]+1, + control, + positions[i].left, + positions[i].left + 1, + positions[i].top, + positions[i].top + 1, GTK_EXPAND, GTK_EXPAND, 0, 0); +#endif + g_array_insert_val (test->priv->controls, i, control); } } -static MateMixerStream * -get_device_output_stream (MateMixerControl *control, MateMixerDevice *device) -{ - MateMixerStream *stream; - const GList *streams; - - streams = mate_mixer_control_list_streams (control); - while (streams) { - stream = MATE_MIXER_STREAM (streams->data); - - if (mate_mixer_stream_get_device (stream) == device) { - if ((mate_mixer_stream_get_flags (stream) & - (MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_CLIENT)) == MATE_MIXER_STREAM_OUTPUT) - break; - } - stream = NULL; - streams = streams->next; - } - - return stream; -} - static void -update_channel_map (GvcSpeakerTest *test) +gvc_speaker_test_init (GvcSpeakerTest *test) { - MateMixerStream *stream; - guint i; + GtkWidget *face; - if (test->priv->control == NULL || - test->priv->device == NULL) - return; + test->priv = GVC_SPEAKER_TEST_GET_PRIVATE (test); - stream = get_device_output_stream (test->priv->control, test->priv->device); - if (G_UNLIKELY (stream == NULL)) { - g_debug ("Failed to find an output stream for sound device %s", - mate_mixer_device_get_name (test->priv->device)); - return; - } + gtk_container_set_border_width (GTK_CONTAINER (test), 12); - ca_context_change_device (test->priv->canberra, - mate_mixer_stream_get_name (stream)); + face = gtk_image_new_from_icon_name ("face-smile", GTK_ICON_SIZE_DIALOG); - for (i = 0; i < G_N_ELEMENTS (position_table); i += 3) - gtk_widget_set_visible (test->priv->channel_controls[position_table[i]], - mate_mixer_stream_has_position (stream, - position_table[i])); -} +#if GTK_CHECK_VERSION (3, 4, 0) + gtk_grid_attach (GTK_GRID (test), + face, + 1, 1, + 3, 1); -static void -gvc_speaker_test_init (GvcSpeakerTest *test) -{ - GtkWidget *icon; - test->priv = GVC_SPEAKER_TEST_GET_PRIVATE (test); + gtk_grid_set_baseline_row (GTK_GRID (test), 1); +#else + gtk_table_attach (GTK_TABLE (test), + face, + 2, 3, 1, 2, + GTK_EXPAND, + GTK_EXPAND, + 0, 0); +#endif + gtk_widget_show (face); ca_context_create (&test->priv->canberra); @@ -505,31 +460,15 @@ gvc_speaker_test_init (GvcSpeakerTest *test) ca_context_set_driver (test->priv->canberra, "pulse"); ca_context_change_props (test->priv->canberra, - CA_PROP_APPLICATION_NAME, _("MATE Volume Control Applet"), CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", + CA_PROP_APPLICATION_NAME, _("Volume Control"), CA_PROP_APPLICATION_VERSION, VERSION, CA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control", NULL); - gtk_table_resize (GTK_TABLE (test), 3, 5); - - gtk_container_set_border_width (GTK_CONTAINER (test), 12); - - gtk_table_set_homogeneous (GTK_TABLE (test), TRUE); - gtk_table_set_row_spacings (GTK_TABLE (test), 12); - gtk_table_set_col_spacings (GTK_TABLE (test), 12); - - create_channel_controls (test); - - icon = gtk_image_new_from_icon_name ("computer", GTK_ICON_SIZE_DIALOG); + test->priv->controls = g_array_new (FALSE, FALSE, sizeof (GtkWidget *)); - gtk_table_attach (GTK_TABLE (test), icon, - 2, 3, 1, 2, - GTK_EXPAND, - GTK_EXPAND, - 0, 0); - - gtk_widget_show (icon); + create_controls (test); } static void @@ -539,8 +478,7 @@ gvc_speaker_test_dispose (GObject *object) test = GVC_SPEAKER_TEST (object); - g_clear_object (&test->priv->control); - g_clear_object (&test->priv->device); + g_clear_object (&test->priv->stream); G_OBJECT_CLASS (gvc_speaker_test_parent_class)->dispose (object); } @@ -558,16 +496,24 @@ gvc_speaker_test_finalize (GObject *object) } GtkWidget * -gvc_speaker_test_new (MateMixerControl *control, MateMixerDevice *device) +gvc_speaker_test_new (MateMixerStream *stream) { GObject *test; - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); test = g_object_new (GVC_TYPE_SPEAKER_TEST, - "control", control, - "device", device, + "row-spacing", 6, + "column-spacing", 6, +#if GTK_CHECK_VERSION (3, 4, 0) + "row-homogeneous", TRUE, + "column-homogeneous", TRUE, +#else + "homogeneous", TRUE, + "n-rows", 3, + "n-columns", 5, +#endif + "stream", stream, NULL); return GTK_WIDGET (test); diff --git a/mate-volume-control/src/gvc-speaker-test.h b/mate-volume-control/src/gvc-speaker-test.h index 035df19..1c1546d 100644 --- a/mate-volume-control/src/gvc-speaker-test.h +++ b/mate-volume-control/src/gvc-speaker-test.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -23,6 +24,8 @@ #include <glib.h> #include <glib-object.h> +#include <gtk/gtk.h> + #include <libmatemixer/matemixer.h> G_BEGIN_DECLS @@ -40,19 +43,28 @@ typedef struct _GvcSpeakerTestPrivate GvcSpeakerTestPrivate; struct _GvcSpeakerTest { - GtkNotebook parent; +#if GTK_CHECK_VERSION (3, 4, 0) + GtkGrid parent; +#else + GtkTable parent; +#endif GvcSpeakerTestPrivate *priv; }; struct _GvcSpeakerTestClass { - GtkNotebookClass parent_class; +#if GTK_CHECK_VERSION (3, 4, 0) + GtkGridClass parent_class; +#else + GtkTableClass parent_class; +#endif }; GType gvc_speaker_test_get_type (void) G_GNUC_CONST; -GtkWidget * gvc_speaker_test_new (MateMixerControl *control, - MateMixerDevice *device); +GtkWidget * gvc_speaker_test_new (MateMixerStream *stream); + +MateMixerStream * gvc_speaker_test_get_stream (GvcSpeakerTest *test); G_END_DECLS diff --git a/mate-volume-control/src/gvc-stream-status-icon.c b/mate-volume-control/src/gvc-stream-status-icon.c index 4e80e91..15372df 100644 --- a/mate-volume-control/src/gvc-stream-status-icon.c +++ b/mate-volume-control/src/gvc-stream-status-icon.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -18,13 +19,11 @@ * */ -#include "config.h" - -#include <math.h> #include <glib.h> #include <glib/gi18n.h> #include <gtk/gtk.h> -#include <gdk/gdkkeysyms-compat.h> +#include <gdk/gdkkeysyms.h> + #include <libmatemixer/matemixer.h> #define MATE_DESKTOP_USE_UNSTABLE_API @@ -38,78 +37,50 @@ struct _GvcStreamStatusIconPrivate { - char **icon_names; + gchar **icon_names; GtkWidget *dock; GtkWidget *bar; guint current_icon; - char *display_name; - gboolean thaw; - MateMixerStream *mixer_stream; + gchar *display_name; + MateMixerStream *stream; }; enum { PROP_0, + PROP_STREAM, PROP_DISPLAY_NAME, - PROP_MIXER_STREAM, PROP_ICON_NAMES, + N_PROPERTIES }; +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + 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_STATUS_ICON) -static void -on_adjustment_value_changed (GtkAdjustment *adjustment, - GvcStreamStatusIcon *icon) -{ - if (icon->priv->thaw == FALSE) - mate_mixer_stream_set_volume (icon->priv->mixer_stream, - round (gtk_adjustment_get_value (adjustment))); -} - -static void -update_dock (GvcStreamStatusIcon *icon) -{ - GtkAdjustment *adj; - - g_return_if_fail (icon); - - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (icon->priv->bar))); - - icon->priv->thaw = TRUE; - gtk_adjustment_set_value (adj, - mate_mixer_stream_get_volume (icon->priv->mixer_stream)); - gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (icon->priv->bar), - mate_mixer_stream_get_mute (icon->priv->mixer_stream)); - icon->priv->thaw = FALSE; -} - static gboolean -popup_dock (GvcStreamStatusIcon *icon, - guint time) +popup_dock (GvcStreamStatusIcon *icon, guint time) { GdkRectangle area; GtkOrientation orientation; GdkDisplay *display; GdkScreen *screen; - gboolean res; int x; int y; int monitor_num; GdkRectangle monitor; GtkRequisition dock_req; - update_dock (icon); - screen = gtk_status_icon_get_screen (GTK_STATUS_ICON (icon)); - res = gtk_status_icon_get_geometry (GTK_STATUS_ICON (icon), - &screen, - &area, - &orientation); - if (! res) { + + 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; } @@ -131,68 +102,88 @@ popup_dock (GvcStreamStatusIcon *icon, #endif if (orientation == GTK_ORIENTATION_VERTICAL) { - if (area.x + area.width + dock_req.width <= monitor.x + monitor.width) { + if (area.x + area.width + dock_req.width <= monitor.x + monitor.width) x = area.x + area.width; - } else { + else x = area.x - dock_req.width; - } - if (area.y + dock_req.height <= monitor.y + monitor.height) { + + if (area.y + dock_req.height <= monitor.y + monitor.height) y = area.y; - } else { + else y = monitor.y + monitor.height - dock_req.height; - } } else { - if (area.y + area.height + dock_req.height <= monitor.y + monitor.height) { + if (area.y + area.height + dock_req.height <= monitor.y + monitor.height) y = area.y + area.height; - } else { + else y = area.y - dock_req.height; - } - if (area.x + dock_req.width <= monitor.x + monitor.width) { + + if (area.x + dock_req.width <= monitor.x + monitor.width) x = area.x; - } else { + else x = monitor.x + monitor.width - dock_req.width; - } } gtk_window_move (GTK_WINDOW (icon->priv->dock), x, y); - /* FIXME: without this, the popup window appears as a square - * after changing the orientation - */ + /* 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 */ + /* Grab focus */ gtk_grab_add (icon->priv->dock); - if (gdk_pointer_grab (gtk_widget_get_window (icon->priv->dock), TRUE, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK, NULL, NULL, - time) - != GDK_GRAB_SUCCESS) { + display = gtk_widget_get_display (icon->priv->dock); + +#if GTK_CHECK_VERSION (3, 0, 0) + do { + GdkDeviceManager *manager = gdk_display_get_device_manager (display); + + if (gdk_device_grab (gdk_device_manager_get_client_pointer (manager), + gtk_widget_get_window (icon->priv->dock), + GDK_OWNERSHIP_NONE, + TRUE, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_SCROLL_MASK, + NULL, + time) != GDK_GRAB_SUCCESS) { + gtk_grab_remove (icon->priv->dock); + gtk_widget_hide (icon->priv->dock); + } + } while (0); +#else + if (gdk_pointer_grab (gtk_widget_get_window (icon->priv->dock), + TRUE, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_SCROLL_MASK, + NULL, NULL, + time) != GDK_GRAB_SUCCESS) { gtk_grab_remove (icon->priv->dock); gtk_widget_hide (icon->priv->dock); return FALSE; } - if (gdk_keyboard_grab (gtk_widget_get_window (icon->priv->dock), TRUE, time) != GDK_GRAB_SUCCESS) { - display = gtk_widget_get_display (icon->priv->dock); + if (gdk_keyboard_grab (gtk_widget_get_window (icon->priv->dock), + TRUE, + time) != GDK_GRAB_SUCCESS) { gdk_display_pointer_ungrab (display, time); gtk_grab_remove (icon->priv->dock); gtk_widget_hide (icon->priv->dock); return FALSE; } - +#endif gtk_widget_grab_focus (icon->priv->dock); return TRUE; } static void -on_status_icon_activate (GtkStatusIcon *status_icon, - GvcStreamStatusIcon *icon) +on_status_icon_activate (GtkStatusIcon *status_icon, GvcStreamStatusIcon *icon) { popup_dock (icon, GDK_CURRENT_TIME); } @@ -202,38 +193,35 @@ on_status_icon_button_press (GtkStatusIcon *status_icon, GdkEventButton *event, GvcStreamStatusIcon *icon) { - /* middle click acts as mute/unmute */ + /* Middle click acts as mute/unmute */ if (event->button == 2) { - gboolean is_muted; - is_muted = mate_mixer_stream_get_mute (icon->priv->mixer_stream); + gboolean is_muted = mate_mixer_stream_get_mute (icon->priv->stream); - mate_mixer_stream_set_mute (icon->priv->mixer_stream, !is_muted); + mate_mixer_stream_set_mute (icon->priv->stream, !is_muted); return TRUE; } return FALSE; } static void -on_menu_mute_toggled (GtkMenuItem *item, - GvcStreamStatusIcon *icon) +on_menu_mute_toggled (GtkMenuItem *item, GvcStreamStatusIcon *icon) { gboolean is_muted; + is_muted = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)); - gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (icon->priv->bar), is_muted); + + mate_mixer_stream_set_mute (icon->priv->stream, is_muted); } static void -on_menu_activate_open_volume_control (GtkMenuItem *item, - GvcStreamStatusIcon *icon) +on_menu_activate_open_volume_control (GtkMenuItem *item, + GvcStreamStatusIcon *icon) { GError *error = NULL; - // XXX update to libmate-desktop 1.9.1 and uncomment this - /* mate_spawn_command_line_on_screen (gtk_widget_get_screen (icon->priv->dock), "mate-volume-control", &error); - */ if (error != NULL) { GtkWidget *dialog; @@ -243,7 +231,7 @@ on_menu_activate_open_volume_control (GtkMenuItem *item, GTK_BUTTONS_CLOSE, _("Failed to start Sound Preferences: %s"), error->message); - g_signal_connect (dialog, + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_destroy), NULL); @@ -260,27 +248,38 @@ on_status_icon_popup_menu (GtkStatusIcon *status_icon, { GtkWidget *menu; GtkWidget *item; - GtkWidget *image; menu = gtk_menu_new (); - item = gtk_check_menu_item_new_with_mnemonic (_("_Mute")); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), - mate_mixer_stream_get_mute (icon->priv->mixer_stream)); - g_signal_connect (item, + mate_mixer_stream_get_mute (icon->priv->stream)); + g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (on_menu_mute_toggled), icon); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); +#if GTK_CHECK_VERSION (3, 10, 0) + item = gtk_menu_item_new_with_mnemonic (_("_Sound Preferences")); +#else 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 (item, + + do { + GtkWidget *image; + + 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); + } while (0); +#endif + 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); @@ -302,25 +301,28 @@ on_status_icon_scroll_event (GtkStatusIcon *status_icon, } static void -gvc_icon_release_grab (GvcStreamStatusIcon *icon, - GdkEventButton *event) +gvc_icon_release_grab (GvcStreamStatusIcon *icon, GdkEventButton *event) { - GdkDisplay *display; +#if GTK_CHECK_VERSION (3, 0, 0) + gdk_device_ungrab (event->device, event->time); +#else + GdkDisplay *display; - /* ungrab focus */ display = gtk_widget_get_display (GTK_WIDGET (icon->priv->dock)); + gdk_display_keyboard_ungrab (display, event->time); gdk_display_pointer_ungrab (display, event->time); +#endif gtk_grab_remove (icon->priv->dock); - /* hide again */ + /* Hide again */ gtk_widget_hide (icon->priv->dock); } static gboolean -on_dock_button_press (GtkWidget *widget, - GdkEventButton *event, - GvcStreamStatusIcon *icon) +on_dock_button_press (GtkWidget *widget, + GdkEventButton *event, + GvcStreamStatusIcon *icon) { if (event->type == GDK_BUTTON_PRESS) { gvc_icon_release_grab (icon, event); @@ -335,33 +337,34 @@ popdown_dock (GvcStreamStatusIcon *icon) { GdkDisplay *display; - /* ungrab focus */ display = gtk_widget_get_display (icon->priv->dock); + +#if GTK_CHECK_VERSION (3, 0, 0) + GdkDeviceManager *manager = gdk_display_get_device_manager (display); + + gdk_device_ungrab (gdk_device_manager_get_client_pointer (manager), + GDK_CURRENT_TIME); +#else gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME); gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); gtk_grab_remove (icon->priv->dock); - - /* hide again */ +#endif + /* Hide again */ gtk_widget_hide (icon->priv->dock); } -/* This is called when the grab is broken for - * either the dock, or the scale itself */ +/* This is called when the grab is broken for either the dock, or the scale */ static void -gvc_icon_grab_notify (GvcStreamStatusIcon *icon, - gboolean was_grabbed) +gvc_icon_grab_notify (GvcStreamStatusIcon *icon, gboolean was_grabbed) { - if (was_grabbed != FALSE) { + if (was_grabbed != FALSE) return; - } - if (!gtk_widget_has_grab (icon->priv->dock)) { + if (gtk_widget_has_grab (icon->priv->dock) == FALSE) return; - } - if (gtk_widget_is_ancestor (gtk_grab_get_current (), icon->priv->dock)) { + if (gtk_widget_is_ancestor (gtk_grab_get_current (), icon->priv->dock)) return; - } popdown_dock (icon); } @@ -380,7 +383,6 @@ on_dock_grab_broken_event (GtkWidget *widget, GvcStreamStatusIcon *icon) { gvc_icon_grab_notify (icon, FALSE); - return FALSE; } @@ -389,17 +391,10 @@ on_dock_key_release (GtkWidget *widget, GdkEventKey *event, GvcStreamStatusIcon *icon) { - if (event->keyval == GDK_Escape) { + if (event->keyval == GDK_KEY_Escape) { popdown_dock (icon); return TRUE; } - -#if 0 - if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event)) { - /* The popup hasn't managed the event, pass onto the button */ - gtk_bindings_activate_event (GTK_OBJECT (user_data), event); - } -#endif return TRUE; } @@ -416,171 +411,187 @@ on_dock_scroll_event (GtkWidget *widget, static void update_icon (GvcStreamStatusIcon *icon) { - gint64 volume; + guint volume = 0; gdouble decibel = 0; - gint64 normal_volume; - gboolean is_muted; - guint n; - char *markup; - gboolean can_decibel = FALSE; + guint normal = 0; + gboolean muted = FALSE; + guint n = 0; + gchar *markup; + const gchar *description; MateMixerStreamFlags flags; - if (icon->priv->mixer_stream == NULL) { + if (icon->priv->stream == NULL) { + /* Do not bother creating a tooltip for an unusable icon as it + * has no practical use */ + gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), FALSE); return; - } - - volume = mate_mixer_stream_get_volume (icon->priv->mixer_stream); - is_muted = mate_mixer_stream_get_mute (icon->priv->mixer_stream); + } else + gtk_status_icon_set_has_tooltip (GTK_STATUS_ICON (icon), TRUE); - flags = mate_mixer_stream_get_flags (icon->priv->mixer_stream); + flags = mate_mixer_stream_get_flags (icon->priv->stream); - if (flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) { - decibel = mate_mixer_stream_get_decibel (icon->priv->mixer_stream); - can_decibel = TRUE; - } + if (flags & MATE_MIXER_STREAM_HAS_MUTE) + muted = mate_mixer_stream_get_mute (icon->priv->stream); - normal_volume = mate_mixer_stream_get_normal_volume (icon->priv->mixer_stream); + if (flags & MATE_MIXER_STREAM_HAS_VOLUME) { + volume = mate_mixer_stream_get_volume (icon->priv->stream); + normal = mate_mixer_stream_get_normal_volume (icon->priv->stream); - /* select image */ - if (volume <= 0 || is_muted) { - n = 0; - } else { - n = 3 * volume / normal_volume + 1; - if (n < 1) { - n = 1; - } else if (n > 3) { - n = 3; - } + /* 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_HAS_DECIBEL_VOLUME) + decibel = mate_mixer_stream_get_decibel (icon->priv->stream); - /* apparently status icon will reset icon even if - * if doesn't change */ + /* Apparently status icon will reset icon even if it doesn't change */ if (icon->priv->current_icon != n) { gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), - icon->priv->icon_names [n]); + icon->priv->icon_names[n]); icon->priv->current_icon = n; } + description = mate_mixer_stream_get_description (icon->priv->stream); - if (is_muted) { - markup = g_strdup_printf ( - "<b>%s: %s</b>\n<small>%s</small>", + if (muted) { + markup = g_strdup_printf ("<b>%s: %s</b>\n<small>%s</small>", icon->priv->display_name, _("Muted"), - mate_mixer_stream_get_description (icon->priv->mixer_stream)); - } else if (can_decibel && (decibel > -MATE_MIXER_INFINITY)) { - markup = g_strdup_printf ( - "<b>%s: %.0f%%</b>\n<small>%0.2f dB\n%s</small>", - icon->priv->display_name, - 100 * (float) volume / normal_volume, - decibel, - mate_mixer_stream_get_description (icon->priv->mixer_stream)); - } else if (can_decibel) { - markup = g_strdup_printf ( - "<b>%s: %.0f%%</b>\n<small>-∞ dB\n%s</small>", - icon->priv->display_name, - 100 * (float) volume / normal_volume, - mate_mixer_stream_get_description (icon->priv->mixer_stream)); + description); + } else if (flags & MATE_MIXER_STREAM_HAS_VOLUME) { + gdouble display_volume = 100 * volume / normal; + + if (flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) { + if (decibel > -MATE_MIXER_INFINITY) { + markup = g_strdup_printf ("<b>%s: %.0f%%</b>\n" + "<small>%0.2f dB\n%s</small>", + icon->priv->display_name, + display_volume, + decibel, + description); + } else { + markup = g_strdup_printf ("<b>%s: %.0f%%</b>\n" + "<small>-∞ dB\n%s</small>", + icon->priv->display_name, + display_volume, + description); + } + } else { + markup = g_strdup_printf ("<b>%s: %.0f%%</b>\n<small>%s</small>", + icon->priv->display_name, + display_volume, + description); + } } else { - markup = g_strdup_printf ( - "<b>%s: %.0f%%</b>\n<small>%s</small>", + markup = g_strdup_printf ("<b>%s</b>\n<small>%s</small>", icon->priv->display_name, - 100 * (float) volume / normal_volume, - mate_mixer_stream_get_description (icon->priv->mixer_stream)); + description); } + gtk_status_icon_set_tooltip_markup (GTK_STATUS_ICON (icon), markup); + g_free (markup); } void gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon *icon, - const char **names) + const gchar **names) { g_return_if_fail (GVC_IS_STREAM_STATUS_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 ((char **)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 */ + gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), names[0]); update_icon (icon); - g_object_notify (G_OBJECT (icon), "icon-names"); + + g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_ICON_NAMES]); } static void -on_stream_volume_notify (GObject *object, +on_stream_volume_notify (MateMixerStream *stream, GParamSpec *pspec, GvcStreamStatusIcon *icon) { update_icon (icon); - update_dock (icon); } static void -on_stream_mute_notify (GObject *object, +on_stream_mute_notify (MateMixerStream *stream, GParamSpec *pspec, GvcStreamStatusIcon *icon) { update_icon (icon); - update_dock (icon); } void gvc_stream_status_icon_set_display_name (GvcStreamStatusIcon *icon, - const char *name) + const gchar *name) { g_return_if_fail (GVC_STREAM_STATUS_ICON (icon)); g_free (icon->priv->display_name); + icon->priv->display_name = g_strdup (name); update_icon (icon); - g_object_notify (G_OBJECT (icon), "display-name"); + + g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_DISPLAY_NAME]); } void -gvc_stream_status_icon_set_mixer_stream (GvcStreamStatusIcon *icon, - MateMixerStream *stream) +gvc_stream_status_icon_set_stream (GvcStreamStatusIcon *icon, + MateMixerStream *stream) { g_return_if_fail (GVC_STREAM_STATUS_ICON (icon)); - if (stream != NULL) { + if (icon->priv->stream == stream) + return; + + if (stream != NULL) g_object_ref (stream); - } - if (icon->priv->mixer_stream != NULL) { - g_signal_handlers_disconnect_by_func (icon->priv->mixer_stream, + if (icon->priv->stream != NULL) { + g_signal_handlers_disconnect_by_func (icon->priv->stream, G_CALLBACK (on_stream_volume_notify), icon); - g_signal_handlers_disconnect_by_func (icon->priv->mixer_stream, + g_signal_handlers_disconnect_by_func (icon->priv->stream, G_CALLBACK (on_stream_mute_notify), icon); - g_object_unref (icon->priv->mixer_stream); + g_object_unref (icon->priv->stream); } - icon->priv->mixer_stream = stream; - - if (icon->priv->mixer_stream != NULL) { - GtkAdjustment *adj; - - g_object_ref (icon->priv->mixer_stream); - - icon->priv->thaw = TRUE; - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (icon->priv->bar))); - gtk_adjustment_set_value (adj, - mate_mixer_stream_get_volume (icon->priv->mixer_stream)); - icon->priv->thaw = FALSE; + icon->priv->stream = stream; - g_signal_connect (icon->priv->mixer_stream, + if (icon->priv->stream != NULL) { + g_signal_connect (icon->priv->stream, "notify::volume", G_CALLBACK (on_stream_volume_notify), icon); - g_signal_connect (icon->priv->mixer_stream, + g_signal_connect (icon->priv->stream, "notify::mute", G_CALLBACK (on_stream_mute_notify), icon); + + // XXX when no stream set some default icon and "unset" dock + update_icon (icon); } - update_icon (icon); + gvc_channel_bar_set_stream (GVC_CHANNEL_BAR (icon->priv->bar), stream); - g_object_notify (G_OBJECT (icon), "mixer-stream"); + g_object_notify_by_pspec (G_OBJECT (icon), properties[PROP_STREAM]); } static void @@ -592,8 +603,8 @@ gvc_stream_status_icon_set_property (GObject *object, GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object); switch (prop_id) { - case PROP_MIXER_STREAM: - gvc_stream_status_icon_set_mixer_stream (self, g_value_get_object (value)); + case PROP_STREAM: + gvc_stream_status_icon_set_stream (self, g_value_get_object (value)); break; case PROP_DISPLAY_NAME: gvc_stream_status_icon_set_display_name (self, g_value_get_string (value)); @@ -614,17 +625,16 @@ gvc_stream_status_icon_get_property (GObject *object, GParamSpec *pspec) { GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object); - GvcStreamStatusIconPrivate *priv = self->priv; switch (prop_id) { - case PROP_MIXER_STREAM: - g_value_set_object (value, priv->mixer_stream); + case PROP_STREAM: + g_value_set_object (value, self->priv->stream); break; case PROP_DISPLAY_NAME: - g_value_set_string (value, priv->display_name); + g_value_set_string (value, self->priv->display_name); break; case PROP_ICON_NAMES: - g_value_set_boxed (value, priv->icon_names); + g_value_set_boxed (value, self->priv->icon_names); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -633,86 +643,6 @@ gvc_stream_status_icon_get_property (GObject *object, } static void -on_bar_is_muted_notify (GObject *object, - GParamSpec *pspec, - GvcStreamStatusIcon *icon) -{ - mate_mixer_stream_set_mute (icon->priv->mixer_stream, - gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object))); -} - -static GObject * -gvc_stream_status_icon_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_params) -{ - GObject *object; - GvcStreamStatusIcon *icon; - GtkWidget *frame; - GtkWidget *box; - GtkAdjustment *adj; - - object = G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->constructor (type, n_construct_properties, construct_params); - - icon = GVC_STREAM_STATUS_ICON (object); - - gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon), - icon->priv->icon_names[0]); - - /* window */ - icon->priv->dock = gtk_window_new (GTK_WINDOW_POPUP); - gtk_widget_set_name (icon->priv->dock, "gvc-stream-status-icon-popup-window"); - g_signal_connect (icon->priv->dock, - "button-press-event", - G_CALLBACK (on_dock_button_press), - icon); - g_signal_connect (icon->priv->dock, - "key-release-event", - G_CALLBACK (on_dock_key_release), - icon); - g_signal_connect (icon->priv->dock, - "scroll-event", - G_CALLBACK (on_dock_scroll_event), - icon); - g_signal_connect (icon->priv->dock, - "grab-notify", - G_CALLBACK (on_dock_grab_notify), - icon); - g_signal_connect (icon->priv->dock, - "grab-broken-event", - G_CALLBACK (on_dock_grab_broken_event), - icon); - - gtk_window_set_decorated (GTK_WINDOW (icon->priv->dock), FALSE); - - 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); - - box = gtk_vbox_new (FALSE, 6); - gtk_container_set_border_width (GTK_CONTAINER (box), 2); - gtk_container_add (GTK_CONTAINER (frame), box); - - icon->priv->bar = gvc_channel_bar_new (); - gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar), - GTK_ORIENTATION_VERTICAL); - - gtk_box_pack_start (GTK_BOX (box), icon->priv->bar, TRUE, FALSE, 0); - g_signal_connect (icon->priv->bar, - "notify::is-muted", - G_CALLBACK (on_bar_is_muted_notify), - icon); - - adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (icon->priv->bar))); - g_signal_connect (adj, - "value-changed", - G_CALLBACK (on_adjustment_value_changed), - icon); - - return object; -} - -static void gvc_stream_status_icon_dispose (GObject *object) { GvcStreamStatusIcon *icon = GVC_STREAM_STATUS_ICON (object); @@ -722,7 +652,7 @@ gvc_stream_status_icon_dispose (GObject *object) icon->priv->dock = NULL; } - g_clear_object (&icon->priv->mixer_stream); + g_clear_object (&icon->priv->stream); G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->dispose (object); } @@ -732,36 +662,33 @@ gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->constructor = gvc_stream_status_icon_constructor; object_class->finalize = gvc_stream_status_icon_finalize; - object_class->dispose = gvc_stream_status_icon_dispose; + object_class->dispose = gvc_stream_status_icon_dispose; object_class->set_property = gvc_stream_status_icon_set_property; object_class->get_property = gvc_stream_status_icon_get_property; - g_object_class_install_property (object_class, - PROP_MIXER_STREAM, - g_param_spec_object ("mixer-stream", - "mixer stream", - "mixer stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - 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_object_class_install_property (object_class, - 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)); + properties[PROP_STREAM] = + g_param_spec_object ("stream", + "Stream", + "MateMixer stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + 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); + + 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_object_class_install_properties (object_class, N_PROPERTIES, properties); g_type_class_add_private (klass, sizeof (GvcStreamStatusIconPrivate)); } @@ -769,43 +696,84 @@ gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass) static void on_status_icon_visible_notify (GvcStreamStatusIcon *icon) { - gboolean visible; - - g_object_get (icon, "visible", &visible, NULL); - if (! visible) { - if (icon->priv->dock != NULL) { - gtk_widget_hide (icon->priv->dock); - } - } + if (gtk_status_icon_get_visible (GTK_STATUS_ICON (icon)) == FALSE) + gtk_widget_hide (icon->priv->dock); } static void gvc_stream_status_icon_init (GvcStreamStatusIcon *icon) { + GtkWidget *frame; + GtkWidget *box; + icon->priv = GVC_STREAM_STATUS_ICON_GET_PRIVATE (icon); - g_signal_connect (icon, + g_signal_connect (G_OBJECT (icon), "activate", G_CALLBACK (on_status_icon_activate), icon); - g_signal_connect (icon, + g_signal_connect (G_OBJECT (icon), "button-press-event", G_CALLBACK (on_status_icon_button_press), icon); - g_signal_connect (icon, + g_signal_connect (G_OBJECT (icon), "popup-menu", G_CALLBACK (on_status_icon_popup_menu), icon); - g_signal_connect (icon, + g_signal_connect (G_OBJECT (icon), "scroll-event", G_CALLBACK (on_status_icon_scroll_event), icon); - g_signal_connect (icon, + g_signal_connect (G_OBJECT (icon), "notify::visible", G_CALLBACK (on_status_icon_visible_notify), NULL); - icon->priv->thaw = FALSE; + /* 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); + +#if GTK_CHECK_VERSION (3, 0, 0) + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); +#else + box = gtk_vbox_new (FALSE, 6); +#endif + + 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); } static void @@ -822,10 +790,10 @@ gvc_stream_status_icon_finalize (GObject *object) GvcStreamStatusIcon * gvc_stream_status_icon_new (MateMixerStream *stream, - const char **icon_names) + const gchar **icon_names) { return g_object_new (GVC_TYPE_STREAM_STATUS_ICON, - "mixer-stream", stream, + "stream", stream, "icon-names", icon_names, NULL); } diff --git a/mate-volume-control/src/gvc-stream-status-icon.h b/mate-volume-control/src/gvc-stream-status-icon.h index cd32f64..9b61898 100644 --- a/mate-volume-control/src/gvc-stream-status-icon.h +++ b/mate-volume-control/src/gvc-stream-status-icon.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2014 Michal Ratajsky <[email protected]> * * 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 @@ -52,13 +53,13 @@ struct _GvcStreamStatusIconClass GType gvc_stream_status_icon_get_type (void) G_GNUC_CONST; GvcStreamStatusIcon * gvc_stream_status_icon_new (MateMixerStream *stream, - const char **icon_names); + const gchar **icon_names); void gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon *icon, - const char **icon_names); + const gchar **icon_names); void gvc_stream_status_icon_set_display_name (GvcStreamStatusIcon *icon, - const char *display_name); -void gvc_stream_status_icon_set_mixer_stream (GvcStreamStatusIcon *icon, + const gchar *display_name); +void gvc_stream_status_icon_set_stream (GvcStreamStatusIcon *icon, MateMixerStream *stream); G_END_DECLS diff --git a/mate-volume-control/src/mvc-helpers.c b/mate-volume-control/src/mvc-helpers.c index 2861bbd..b82d6c8 100644 --- a/mate-volume-control/src/mvc-helpers.c +++ b/mate-volume-control/src/mvc-helpers.c @@ -18,8 +18,11 @@ * */ +#include "config.h" + #include <glib.h> #include <glib/gi18n.h> +#include <gtk/gtk.h> #include <libmatemixer/matemixer.h> @@ -30,7 +33,7 @@ #include "mvc-helpers.h" #ifdef HAVE_PULSEAUDIO -static pa_position_t +static pa_channel_position_t position_to_pulse (MateMixerChannelPosition position) { switch (position) { @@ -101,55 +104,236 @@ mvc_channel_map_to_pretty_string (MateMixerStream *stream) { g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); +#define HAS_POSITION(p) (mate_mixer_stream_has_channel_position (stream, (p))) + /* Modeled after PulseAudio 5.0, probably could be extended with other combinations */ switch (mate_mixer_stream_get_num_channels (stream)) { case 1: - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_MONO)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_MONO)) return _("Mono"); break; case 2: - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_RIGHT)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT)) return _("Stereo"); break; case 4: - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_RIGHT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_RIGHT)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT)) return _("Surround 4.0"); break; case 5: - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_RIGHT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_RIGHT)) - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_LFE)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) return _("Surround 4.1"); - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_CENTER)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER)) return _("Surround 5.0"); break; case 6: - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_RIGHT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_CENTER) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_RIGHT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_LFE)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) return _("Surround 5.1"); break; case 8: - if (mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_RIGHT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_FRONT_CENTER) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_BACK_RIGHT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_SIDE_LEFT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_SIDE_RIGHT) && - mate_mixer_stream_has_position (stream, MATE_MIXER_CHANNEL_LFE)) + if (HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_FRONT_CENTER) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_BACK_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_SIDE_LEFT) && + HAS_POSITION (MATE_MIXER_CHANNEL_SIDE_RIGHT) && + HAS_POSITION (MATE_MIXER_CHANNEL_LFE)) return _("Surround 7.1"); break; } +#undef HAS_POSITION + return NULL; } + +#if GTK_CHECK_VERSION (3, 0, 0) +/* Taken from gtkstyle.c */ +static void rgb_to_hls (gdouble *r, gdouble *g, gdouble *b); +static void hls_to_rgb (gdouble *h, gdouble *l, gdouble *s); + +void +mvc_color_shade (GdkRGBA *a, GdkRGBA *b, gdouble k) +{ + gdouble red; + gdouble green; + gdouble blue; + + red = (gdouble) a->red / 65535.0; + green = (gdouble) a->green / 65535.0; + blue = (gdouble) a->blue / 65535.0; + + rgb_to_hls (&red, &green, &blue); + + green *= k; + if (green > 1.0) + green = 1.0; + else if (green < 0.0) + green = 0.0; + + blue *= k; + if (blue > 1.0) + blue = 1.0; + else if (blue < 0.0) + blue = 0.0; + + hls_to_rgb (&red, &green, &blue); + + b->red = red * 65535.0; + b->green = green * 65535.0; + b->blue = blue * 65535.0; +} + +static void +rgb_to_hls (gdouble *r, gdouble *g, gdouble *b) +{ + gdouble min; + gdouble max; + gdouble red; + gdouble green; + gdouble blue; + gdouble h, l, s; + gdouble delta; + + red = *r; + green = *g; + blue = *b; + + if (red > green) { + if (red > blue) + max = red; + else + max = blue; + + if (green < blue) + min = green; + else + min = blue; + } else { + if (green > blue) + max = green; + else + max = blue; + + if (red < blue) + min = red; + else + min = blue; + } + + l = (max + min) / 2; + s = 0; + h = 0; + + if (max != min) { + if (l <= 0.5) + s = (max - min) / (max + min); + else + s = (max - min) / (2 - max - min); + + delta = max - min; + if (red == max) + h = (green - blue) / delta; + else if (green == max) + h = 2 + (blue - red) / delta; + else if (blue == max) + h = 4 + (red - green) / delta; + + h *= 60; + if (h < 0.0) + h += 360; + } + + *r = h; + *g = l; + *b = s; +} + +static void +hls_to_rgb (gdouble *h, gdouble *l, gdouble *s) +{ + gdouble hue; + gdouble lightness; + gdouble saturation; + gdouble m1, m2; + gdouble r, g, b; + + lightness = *l; + saturation = *s; + + if (lightness <= 0.5) + m2 = lightness * (1 + saturation); + else + m2 = lightness + saturation - lightness * saturation; + m1 = 2 * lightness - m2; + + if (saturation == 0) { + *h = lightness; + *l = lightness; + *s = lightness; + } else { + hue = *h + 120; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + r = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + r = m2; + else if (hue < 240) + r = m1 + (m2 - m1) * (240 - hue) / 60; + else + r = m1; + + hue = *h; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + g = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + g = m2; + else if (hue < 240) + g = m1 + (m2 - m1) * (240 - hue) / 60; + else + g = m1; + + hue = *h - 120; + while (hue > 360) + hue -= 360; + while (hue < 0) + hue += 360; + + if (hue < 60) + b = m1 + (m2 - m1) * hue / 60; + else if (hue < 180) + b = m2; + else if (hue < 240) + b = m1 + (m2 - m1) * (240 - hue) / 60; + else + b = m1; + + *h = r; + *l = g; + *s = b; + } +} +#endif diff --git a/mate-volume-control/src/mvc-helpers.h b/mate-volume-control/src/mvc-helpers.h index 248c843..16e48db 100644 --- a/mate-volume-control/src/mvc-helpers.h +++ b/mate-volume-control/src/mvc-helpers.h @@ -31,6 +31,12 @@ const gchar *mvc_channel_position_to_string (MateMixerChannelPosition pos const gchar *mvc_channel_position_to_pretty_string (MateMixerChannelPosition position); const gchar *mvc_channel_map_to_pretty_string (MateMixerStream *stream); +#if GTK_CHECK_VERSION (3, 0, 0) +void mvc_color_shade (GdkRGBA *a, + GdkRGBA *b, + gdouble k); +#endif + G_END_DECLS #endif /* __MVC_HELPERS_H */ |