diff options
Diffstat (limited to 'gst-mixer/src/track.c')
-rw-r--r-- | gst-mixer/src/track.c | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/gst-mixer/src/track.c b/gst-mixer/src/track.c new file mode 100644 index 0000000..9b742be --- /dev/null +++ b/gst-mixer/src/track.c @@ -0,0 +1,647 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * track.c: layout of a single mixer track + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <glib/gi18n.h> +#include <string.h> + +#include "button.h" +#include "track.h" +#include "volume.h" + +static const struct { + gchar *label, + *pixmap; +} pix[] = { + { "cd", "media-optical" }, + { "line", "gvc-line-in" }, + { "aux", "gvc-line-in" }, + { "mic", "audio-input-microphone" }, + { "cap", "gvc-line-in" }, + { "mix", "multimedia-volume-control" }, + { "pcm", "gvc-tone" }, + { "headphone", "gvc-headphones" }, + { "phone", "phone" }, + { "speaker", "audio-volume-high" }, + { "front", "audio-volume-high" }, + { "surround", "audio-volume-high" }, + { "side", "audio-volume-high" }, + { "center", "audio-volume-high" }, + { "lfe", "audio-volume-high" }, + { "video", "video-display" }, + { "volume", "gvc-tone" }, + { "master", "gvc-tone" }, + { "3d", "gvc-3d-sound" }, + { "beep", "keyboard" }, + { "record", "audio-input-microphone" }, + { NULL, NULL } +}; + +/* + * UI responses. + */ + +static void +cb_mute_toggled (MateVolumeControlButton *button, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + + gst_mixer_set_mute (ctrl->mixer, ctrl->track, + !mate_volume_control_button_get_active (button)); +} + +static void +cb_record_toggled (MateVolumeControlButton *button, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + + gst_mixer_set_record (ctrl->mixer, ctrl->track, + mate_volume_control_button_get_active (button)); +} + +/* Tells us whether toggling a switch should change the corresponding + * GstMixerTrack's MUTE or RECORD flag. + */ +static gboolean +should_toggle_record_switch (const GstMixerTrack *track) +{ + return (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_INPUT) && + !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_RECORD)); +} + + +static void +cb_toggle_changed (GtkToggleButton *button, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + + if (should_toggle_record_switch (ctrl->track)) { + gst_mixer_set_record (ctrl->mixer, ctrl->track, + gtk_toggle_button_get_active (button)); + } else { + gst_mixer_set_mute (ctrl->mixer, ctrl->track, + !gtk_toggle_button_get_active (button)); + } +} + +static void +cb_option_changed (GtkComboBox *box, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + gchar *opt; + + opt = gtk_combo_box_get_active_text (box); + if (opt) + gst_mixer_set_option (ctrl->mixer, GST_MIXER_OPTIONS (ctrl->track), opt); + g_free (opt); +} + +/* + * Timeout to check for changes in mixer outside ourselves. + */ + +void +mate_volume_control_track_update (MateVolumeControlTrack *trkw) +{ + gboolean mute, record; + gboolean vol_is_zero = FALSE, slider_is_zero = FALSE; + GstMixer *mixer; + GstMixerTrack *track; + gint i; + gint *dummy; + + g_return_if_fail (trkw != NULL); + + track = trkw->track; + mixer = trkw->mixer; + + /* trigger an update of the mixer state */ + if (GST_IS_MIXER_OPTIONS (track)) { + const GList *opt; + GstMixerOptions *options = GST_MIXER_OPTIONS (track); + const char *active_opt; + active_opt = gst_mixer_get_option (mixer, options); + + for (i = 0, opt = gst_mixer_options_get_values (options); + opt != NULL; + opt = opt->next, i++) { + if (g_str_equal (active_opt, opt->data)) { + gtk_combo_box_set_active (GTK_COMBO_BOX (trkw->options), i); + } + } + + return; + } + + dummy = g_new (gint, MAX (track->num_channels, 1)); + gst_mixer_get_volume (mixer, track, dummy); + g_free (dummy); + + mute = GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) ? + TRUE : FALSE; + record = GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD) ? + TRUE : FALSE; + + if (trkw->sliderbox) { + mate_volume_control_volume_update (MATE_VOLUME_CONTROL_VOLUME (trkw->sliderbox)); + mate_volume_control_volume_ask ( + MATE_VOLUME_CONTROL_VOLUME (trkw->sliderbox), + &vol_is_zero, &slider_is_zero); + + if (trkw->mute && !slider_is_zero && vol_is_zero) + mute = TRUE; + } + + if (trkw->mute) { + if (mate_volume_control_button_get_active (trkw->mute) == mute) { + mate_volume_control_button_set_active (trkw->mute, !mute); + } + } + + if (trkw->record) { + if (mate_volume_control_button_get_active (trkw->record) != record) { + mate_volume_control_button_set_active (trkw->record, record); + } + } + + if (trkw->toggle) { + if (should_toggle_record_switch (trkw->track)) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (trkw->toggle), + GST_MIXER_TRACK_HAS_FLAG (trkw->track, GST_MIXER_TRACK_RECORD)); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (trkw->toggle), + !GST_MIXER_TRACK_HAS_FLAG (trkw->track, GST_MIXER_TRACK_MUTE)); + } + } + + /* FIXME: + * - options. + */ +} + + +static gboolean +cb_check (gpointer data) +{ + mate_volume_control_track_update (data); + + return TRUE; +} + +/* + * Actual UI code. + */ + +static MateVolumeControlTrack * +mate_volume_control_track_add_title (GtkTable *table, + gint tab_pos, + GtkOrientation or, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep) +{ + MateVolumeControlTrack *ctrl; + gchar *ulabel = NULL; + gchar *str = NULL; + gint i; + gboolean need_timeout = TRUE; + + need_timeout = ((gst_mixer_get_mixer_flags (GST_MIXER (mixer)) & + GST_MIXER_FLAG_AUTO_NOTIFICATIONS) == 0); + + /* start */ + ctrl = g_new0 (MateVolumeControlTrack, 1); + ctrl->mixer = mixer; + g_object_ref (G_OBJECT (track)); + ctrl->track = track; + ctrl->left_separator = l_sep; + ctrl->right_separator = r_sep; + ctrl->visible = TRUE; + ctrl->table = table; + ctrl->pos = tab_pos; + if (need_timeout) + ctrl->id = g_timeout_add (200, cb_check, ctrl); + ctrl->flagbuttonbox = NULL; + + /* find image from label string (optional) */ + if (g_object_class_find_property (G_OBJECT_GET_CLASS (track), "untranslated-label")) + g_object_get (track, "untranslated-label", &ulabel, NULL); + + if (ulabel == NULL) + g_object_get (track, "label", &ulabel, NULL); + + if (ulabel) { + gint pos; + + /* make case insensitive */ + for (pos = 0; ulabel[pos] != '\0'; pos++) + ulabel[pos] = g_ascii_tolower (ulabel[pos]); + + for (i = 0; pix[i].label != NULL; i++) { + if (g_strrstr (ulabel, pix[i].label) != NULL) { + str = pix[i].pixmap; + break; + } + } + + g_free (ulabel); + } + + if ((str != NULL) && (track->num_channels != 0)) { + if ((ctrl->image = gtk_image_new_from_icon_name (str, GTK_ICON_SIZE_MENU)) != NULL) { + gtk_misc_set_alignment (GTK_MISC (ctrl->image), 0.5, 0.5); + if (or == GTK_ORIENTATION_VERTICAL) { + gtk_table_attach (GTK_TABLE (table), ctrl->image, + tab_pos, tab_pos + 1, 0, 1, + GTK_EXPAND, 0, 0, 0); + } else { + gtk_table_attach (GTK_TABLE (table), ctrl->image, + 0, 1, tab_pos, tab_pos + 1, + 0, GTK_EXPAND, 0, 0); + } + gtk_widget_show (ctrl->image); + } + } + + /* text label */ + if (or == GTK_ORIENTATION_HORIZONTAL) + str = g_strdup_printf (_("%s:"), track->label); + else + str = g_strdup (track->label); + ctrl->label = gtk_label_new (str); + if (or == GTK_ORIENTATION_HORIZONTAL) { + g_free (str); + gtk_misc_set_alignment (GTK_MISC (ctrl->label), 0.0, 0.5); + } + if (or == GTK_ORIENTATION_VERTICAL) { + gtk_table_attach (table, ctrl->label, + tab_pos, tab_pos + 1, 1, 2, + GTK_EXPAND, 0, 0, 0); + } else { + gtk_table_attach (table, ctrl->label, + 1, 2, tab_pos, tab_pos + 1, + GTK_FILL, GTK_EXPAND, 0, 0); + } + gtk_widget_show (ctrl->label); + + return ctrl; +} + +static void +mate_volume_control_track_put_switch (GtkTable *table, + gint tab_pos, + MateVolumeControlTrack *ctrl) +{ + GtkWidget *button; + AtkObject *accessible; + gchar *accessible_name, *msg; + + /* container box */ + ctrl->buttonbox = gtk_hbox_new (FALSE, 0); + gtk_table_attach (GTK_TABLE (table), ctrl->buttonbox, + tab_pos, tab_pos + 1, + 3, 4, GTK_EXPAND, 0, 0, 0); + gtk_widget_show (ctrl->buttonbox); + + /* if we weren't supposed to show the mute button, then don't create it */ + if (GST_MIXER_TRACK_HAS_FLAG (ctrl->track, GST_MIXER_TRACK_NO_MUTE)) { + return; + } + + /* mute button */ + msg = g_strdup_printf (_("Mute/Unmute %s"), ctrl->track->label); + button = mate_volume_control_button_new ("audio-volume-high", + "audio-volume-muted", + msg); + ctrl->mute = MATE_VOLUME_CONTROL_BUTTON (button); + g_free (msg); + + mate_volume_control_button_set_active ( + MATE_VOLUME_CONTROL_BUTTON (button), + !GST_MIXER_TRACK_HAS_FLAG (ctrl->track, GST_MIXER_TRACK_MUTE)); + + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (cb_mute_toggled), ctrl); + + /* a11y */ + accessible = gtk_widget_get_accessible (button); + if (GTK_IS_ACCESSIBLE (accessible)) { + accessible_name = g_strdup_printf (_("Track %s: mute"), + ctrl->track->label); + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } + + /* show */ + gtk_box_pack_start (GTK_BOX (ctrl->buttonbox), button, + FALSE, FALSE, 0); + gtk_widget_show (button); +} + +MateVolumeControlTrack * +mate_volume_control_track_add_playback (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + + /* switch and options exception (no sliders) */ + if (track->num_channels == 0) { + if (GST_IS_MIXER_OPTIONS (track)) { + return (mate_volume_control_track_add_option (table, tab_pos, mixer, track, + l_sep, r_sep, fbox)); + } + return (mate_volume_control_track_add_switch (table, tab_pos, mixer, track, + l_sep, r_sep, fbox)); + } + + /* image, title */ + ctrl = mate_volume_control_track_add_title (table, tab_pos, + GTK_ORIENTATION_VERTICAL, + mixer, track, l_sep, r_sep); + + ctrl->sliderbox = mate_volume_control_volume_new (ctrl->mixer, + ctrl->track, 6); + gtk_table_attach (GTK_TABLE (table), ctrl->sliderbox, + tab_pos, tab_pos + 1, 2, 3, + GTK_EXPAND, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (ctrl->sliderbox); + + /* mute button */ + mate_volume_control_track_put_switch (table, tab_pos, ctrl); + + return ctrl; +} + +MateVolumeControlTrack * +mate_volume_control_track_add_recording (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + GtkWidget *button; + AtkObject *accessible; + gchar *accessible_name, *msg; + + ctrl = mate_volume_control_track_add_playback (table, tab_pos, mixer, + track, l_sep, r_sep, fbox); + if (track->num_channels == 0) { + return ctrl; + } + + /* FIXME: + * - there's something fishy about this button, it + * is always FALSE. + */ + if (!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_RECORD)) { + /* only the record button here */ + msg = g_strdup_printf (_("Toggle audio recording from %s"), + ctrl->track->label); + button = mate_volume_control_button_new ("audio-input-microphone", + "audio-input-microphone-muted", + msg); + ctrl->record = MATE_VOLUME_CONTROL_BUTTON (button); + g_free (msg); + mate_volume_control_button_set_active (MATE_VOLUME_CONTROL_BUTTON (button), + GST_MIXER_TRACK_HAS_FLAG (track, + GST_MIXER_TRACK_RECORD)); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (cb_record_toggled), ctrl); + + /* a11y */ + accessible = gtk_widget_get_accessible (button); + if (GTK_IS_ACCESSIBLE (accessible)) { + accessible_name = g_strdup_printf (_("Track %s: audio recording"), + track->label); + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } + + /* attach, show */ + gtk_box_pack_start (GTK_BOX (ctrl->buttonbox), button, + FALSE, FALSE, 0); + gtk_widget_show (button); + } + + return ctrl; +} + +MateVolumeControlTrack * +mate_volume_control_track_add_switch (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + GtkWidget *toggle; + gint volume; + + /* image, title */ + toggle = gtk_check_button_new (); + + /* this is a hack - we query volume to initialize switch state */ + gst_mixer_get_volume (mixer, track, &volume); + + if (should_toggle_record_switch (track)) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE)); + } + + if (fbox == NULL) { + fbox = gtk_table_new(0, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (fbox), 6); + } + table = GTK_TABLE (fbox); + + ctrl = mate_volume_control_track_add_title (table, tab_pos, + GTK_ORIENTATION_HORIZONTAL, + mixer, track, l_sep, r_sep); + ctrl->toggle = toggle; + ctrl->flagbuttonbox = fbox; + + /* attach'n'show */ + gtk_table_attach (table, toggle, + 2, 3, tab_pos, tab_pos + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (cb_toggle_changed), ctrl); + gtk_widget_show (toggle); + + return ctrl; +} + +MateVolumeControlTrack * +mate_volume_control_track_add_option (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + GstMixerOptions *options = GST_MIXER_OPTIONS (track); + const GList *opt, *opts; + AtkObject *accessible; + gchar *accessible_name; + gint i = 0; + const gchar *active_opt; + + if (fbox == NULL) { + fbox = gtk_table_new(0, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (fbox), 6); + } + table = GTK_TABLE (fbox); + + ctrl = mate_volume_control_track_add_title (table, tab_pos, + GTK_ORIENTATION_HORIZONTAL, + mixer, track, l_sep, r_sep); + + /* optionmenu */ + active_opt = gst_mixer_get_option (mixer, options); + if (active_opt != NULL) { + ctrl->options = gtk_combo_box_new_text (); + opts = gst_mixer_options_get_values (options); + for (opt = opts; opt != NULL; opt = opt->next, i++) { + if (opt->data == NULL) + continue; + + gtk_combo_box_append_text (GTK_COMBO_BOX (ctrl->options), opt->data); + + if (g_str_equal (active_opt, opt->data)) { + gtk_combo_box_set_active (GTK_COMBO_BOX (ctrl->options), i); + } + } + } + + /* a11y */ + accessible = gtk_widget_get_accessible (ctrl->options); + if (GTK_IS_ACCESSIBLE (accessible)) { + accessible_name = g_strdup_printf (_("%s Option Selection"), + ctrl->track->label); + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } + gtk_widget_show (ctrl->options); + g_signal_connect (ctrl->options, "changed", + G_CALLBACK (cb_option_changed), ctrl); + + ctrl->flagbuttonbox = fbox; + + /* attach'n'show */ + gtk_table_attach (table, ctrl->options, + 2, 3, tab_pos, tab_pos + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); + gtk_widget_show (ctrl->options); + + return ctrl; +} + +void +mate_volume_control_track_free (MateVolumeControlTrack *track) +{ + if (track->id != 0) { + g_source_remove (track->id); + track->id = 0; + } + + g_object_unref (G_OBJECT (track->track)); + + g_free (track); +} + +void +mate_volume_control_track_show (MateVolumeControlTrack *track, + gboolean visible) +{ +#define func(w) \ + if (w != NULL) { \ + if (visible) { \ + gtk_widget_show (w); \ + } else { \ + gtk_widget_hide (w); \ + } \ + } + + func (track->label); + func (track->image); + func (track->sliderbox); + func (track->buttonbox); + func (track->toggle); + func (track->options); + + track->visible = visible; + + /* get rid of spacing between hidden tracks */ + if (visible) { + if (track->options) { + gtk_table_set_row_spacing (track->table, + track->pos, 6); + if (track->pos > 0) + gtk_table_set_row_spacing (track->table, + track->pos - 1, 6); + } else if (!track->toggle) { + gtk_table_set_col_spacing (track->table, + track->pos, 6); + if (track->pos > 0) + gtk_table_set_col_spacing (track->table, + track->pos - 1, 6); + } + } else { + if (track->options) { + gtk_table_set_row_spacing (track->table, + track->pos, 0); + if (track->pos > 0) + gtk_table_set_row_spacing (track->table, + track->pos - 1, 0); + } else if (!track->toggle) { + gtk_table_set_col_spacing (track->table, + track->pos, 0); + if (track->pos > 0) + gtk_table_set_col_spacing (track->table, + track->pos - 1, 0); + } + } +} |