1
/* GNOME Volume Control
2
* Copyright (C) 2003-2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
4
* volume.c: representation of a track's volume channels
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Library General Public
8
* License as published by the Free Software Foundation; either
9
* version 2 of the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Library General Public License for more details.
16
* You should have received a copy of the GNU Library General Public
17
* License along with this library; if not, write to the
18
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19
* Boston, MA 02111-1307, USA.
26
#define _ISOC99_SOURCE
32
#include <gst/mixer/mixer.h>
38
static void gnome_volume_control_volume_class_init (GnomeVolumeControlVolumeClass *klass);
39
static void gnome_volume_control_volume_init (GnomeVolumeControlVolume *el);
40
static void gnome_volume_control_volume_dispose (GObject *object);
42
static void gnome_volume_control_volume_size_req (GtkWidget *widget,
44
static void gnome_volume_control_volume_size_alloc (GtkWidget *widget,
45
GtkAllocation *alloc);
46
static gboolean gnome_volume_control_volume_expose (GtkWidget *widget,
47
GdkEventExpose *expose);
49
static void cb_volume_changed (GtkAdjustment *adj,
51
static void cb_lock_toggled (GtkToggleButton *button,
54
static gboolean cb_check (gpointer data);
56
static GtkFixedClass *parent_class = NULL;
59
gnome_volume_control_volume_get_type (void)
61
static GType gnome_volume_control_volume_type = 0;
63
if (!gnome_volume_control_volume_type) {
64
static const GTypeInfo gnome_volume_control_volume_info = {
65
sizeof (GnomeVolumeControlVolumeClass),
68
(GClassInitFunc) gnome_volume_control_volume_class_init,
71
sizeof (GnomeVolumeControlVolume),
73
(GInstanceInitFunc) gnome_volume_control_volume_init,
77
gnome_volume_control_volume_type =
78
g_type_register_static (GTK_TYPE_FIXED,
79
"GnomeVolumeControlVolume",
80
&gnome_volume_control_volume_info, 0);
83
return gnome_volume_control_volume_type;
87
gnome_volume_control_volume_class_init (GnomeVolumeControlVolumeClass *klass)
89
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
90
GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
92
parent_class = g_type_class_ref (GTK_TYPE_FIXED);
94
gobject_class->dispose = gnome_volume_control_volume_dispose;
95
gtkwidget_class->size_allocate = gnome_volume_control_volume_size_alloc;
96
gtkwidget_class->size_request = gnome_volume_control_volume_size_req;
97
gtkwidget_class->expose_event = gnome_volume_control_volume_expose;
101
gnome_volume_control_volume_init (GnomeVolumeControlVolume *vol)
103
gtk_fixed_set_has_window (GTK_FIXED (vol), TRUE);
116
cb_mouseover (GtkScale *scale,
117
GdkEventCrossing *event,
120
GnomeVolumeControlVolume *vol = data;
125
for (items = vol->scales; items != NULL; items = items->next, n++)
126
if (items->data == scale)
128
g_assert (items != NULL);
130
/* somewhat dirty hack that will suffice for now. 1 chan
131
* means mono, two means stereo (left/right) and > 2 means
132
* alsa, where channel order is front, rear, center, lfe,
134
if (vol->track->num_channels == 1) {
136
} else if (vol->track->num_channels == 2) {
137
chan = (n == 0) ? _("left") : _("right");
140
case 0: chan = _("front left"); break;
141
case 1: chan = _("front right"); break;
142
case 2: chan = _("rear left"); break;
143
case 3: chan = _("rear right"); break;
144
case 4: chan = _("front center"); break;
145
/* Translators: This is the name of a surround sound channel. It
146
* stands for "Low-Frequency Effects". If you're not sure that
147
* this has an established and different translation in your
148
* language, leave it unchanged. */
149
case 5: chan = _("LFE"); break;
150
case 6: chan = _("side left"); break;
151
case 7: chan = _("side right"); break;
152
default: chan = _("unknown"); break;
156
/* Here, we can actually tell people that this
157
* is a slider that will change channel X. */
158
msg = g_strdup_printf (_("Volume of %s channel on %s"),
159
chan, vol->track->label);
160
gnome_appbar_push (vol->appbar, msg);
167
cb_mouseout (GtkScale *scale,
168
GdkEventCrossing *event,
171
GnomeVolumeControlVolume *vol = data;
173
gnome_appbar_pop (vol->appbar);
179
get_scale (GnomeVolumeControlVolume *vol,
185
AtkObject *accessible;
186
gchar *accessible_name;
188
adj = gtk_adjustment_new (volume,
189
vol->track->min_volume,
190
vol->track->max_volume,
192
g_signal_connect (adj, "value_changed",
193
G_CALLBACK (cb_volume_changed), vol);
194
slider = gtk_vscale_new (GTK_ADJUSTMENT (adj));
195
gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE);
196
gtk_range_set_inverted (GTK_RANGE (slider), TRUE);
197
g_signal_connect (slider, "enter-notify-event",
198
G_CALLBACK (cb_mouseover), vol);
199
g_signal_connect (slider, "leave-notify-event",
200
G_CALLBACK (cb_mouseout), vol);
203
accessible = gtk_widget_get_accessible (slider);
204
if (GTK_IS_ACCESSIBLE (accessible)) {
205
if (vol->track->num_channels == 1) {
206
accessible_name = g_strdup_printf (_("Track %s"),
209
gchar *accessible_desc = g_strdup_printf (_("Channel %d of track %s"),
212
accessible_name = g_strdup_printf (_("Track %s, channel %d"),
213
vol->track->label, num_chan + 1);
214
atk_object_set_description (accessible, accessible_desc);
215
g_free (accessible_desc);
217
atk_object_set_name (accessible, accessible_name);
218
g_free (accessible_name);
225
get_button (GnomeVolumeControlVolume *vol,
228
AtkObject *accessible;
229
gchar *accessible_name, *msg;
232
msg = g_strdup_printf (("Lock channels for %s together"), vol->track->label);
233
vol->button = gnome_volume_control_button_new ("chain.png",
237
g_signal_connect (vol->button, "clicked",
238
G_CALLBACK (cb_lock_toggled), vol);
239
for (n = 1; n < vol->track->num_channels; n++) {
240
/* default, unlocked */
241
if (volumes[n] != volumes[0])
244
gnome_volume_control_button_set_active (GNOME_VOLUME_CONTROL_BUTTON (vol->button),
245
n == vol->track->num_channels);
248
accessible = gtk_widget_get_accessible (vol->button);
249
if (GTK_IS_ACCESSIBLE (accessible)) {
250
accessible_name = g_strdup_printf (_("Track %s: lock channels together"),
252
atk_object_set_name (accessible, accessible_name);
253
g_free (accessible_name);
258
gnome_volume_control_volume_new (GstMixer *mixer,
259
GstMixerTrack *track,
263
GnomeVolumeControlVolume *vol;
267
vol = g_object_new (GNOME_VOLUME_CONTROL_TYPE_VOLUME, NULL);
268
gst_object_ref (GST_OBJECT (mixer));
270
vol->track = g_object_ref (G_OBJECT (track));
271
vol->appbar = appbar;
273
vol->padding = padding;
276
volumes = g_new0 (gint, track->num_channels);
277
gst_mixer_get_volume (mixer, track, volumes);
278
for (n = 0; n < track->num_channels; n++) {
281
/* we will reposition the widget once we're drawing up */
282
slider = get_scale (vol, n, volumes[n]);
283
gtk_fixed_put (GTK_FIXED (vol), slider, 0, 0);
284
gtk_widget_show (slider);
285
vol->scales = g_list_append (vol->scales, slider);
289
get_button (vol, volumes);
290
if (track->num_channels > 1) {
291
gtk_fixed_put (GTK_FIXED (vol), vol->button, 0, 0);
292
gtk_widget_show (vol->button);
297
/* GStreamer signals */
298
vol->id = g_timeout_add (100, cb_check, vol);
300
return GTK_WIDGET (vol);
304
gnome_volume_control_volume_dispose (GObject *object)
306
GnomeVolumeControlVolume *vol = GNOME_VOLUME_CONTROL_VOLUME (object);
309
g_source_remove (vol->id);
314
g_object_unref (G_OBJECT (vol->track));
319
gst_object_unref (GST_OBJECT (vol->mixer));
324
g_list_free (vol->scales);
328
G_OBJECT_CLASS (parent_class)->dispose (object);
332
* Gtk/GDK virtual functions for size negotiation.
336
gnome_volume_control_volume_size_req (GtkWidget *widget,
339
GnomeVolumeControlVolume *vol = GNOME_VOLUME_CONTROL_VOLUME (widget);
340
GtkRequisition but_req, scale_req;
343
/* request size of kids */
344
GTK_WIDGET_GET_CLASS (vol->button)->size_request (vol->button, &but_req);
345
GTK_WIDGET_GET_CLASS (vol->scales->data)->size_request (vol->scales->data,
347
if (scale_req.height < 100)
348
scale_req.height = 100;
350
/* calculate our own size from that */
351
req->width = scale_req.width * vol->track->num_channels +
352
vol->padding * (vol->track->num_channels - 1);
353
req->height = scale_req.height + but_req.height /*+ vol->padding*/;
357
gnome_volume_control_volume_size_alloc (GtkWidget *widget,
358
GtkAllocation *alloc)
360
GnomeVolumeControlVolume *vol = GNOME_VOLUME_CONTROL_VOLUME (widget);
361
GtkRequisition but_req, scale_req;
362
GtkAllocation but_all, scale_all;
363
gint x_offset, but_deco_y_offset, but_x_offset, but_deco_width, n = 0;
367
if (alloc->x == widget->allocation.x &&
368
alloc->y == widget->allocation.y &&
369
alloc->width == widget->allocation.width &&
370
alloc->height == widget->allocation.height)
373
/* request size of kids */
374
GTK_WIDGET_GET_CLASS (vol->button)->size_request (vol->button, &but_req);
375
GTK_WIDGET_GET_CLASS (vol->scales->data)->size_request (vol->scales->data,
379
x_offset = (alloc->width - ((vol->track->num_channels * scale_req.width) +
380
(vol->track->num_channels - 1) * vol->padding)) / 2;
381
scale_all.width = scale_req.width;
382
scale_all.height = alloc->height - but_req.height;
384
but_deco_width = alloc->width - (2 * x_offset);
385
but_all.width = but_req.width;
386
but_all.height = but_req.height;
387
but_all.x = x_offset + (but_deco_width - but_req.width) / 2;
388
but_all.y = alloc->height - but_req.height;
391
for (scales = vol->scales; scales != NULL; scales = scales->next, n++) {
392
scale_all.x = x_offset + n * (scale_req.width + vol->padding);
393
gtk_fixed_move (GTK_FIXED (vol), scales->data, scale_all.x, scale_all.y);
394
gtk_widget_set_size_request (scales->data, scale_all.width, scale_all.height);
398
if (vol->track->num_channels > 1) {
399
gtk_fixed_move (GTK_FIXED (vol), vol->button, but_all.x, but_all.y);
400
gtk_widget_set_size_request (vol->button, but_all.width, but_all.height);
403
/* parent will resize window */
404
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, alloc);
408
gnome_volume_control_volume_expose (GtkWidget *widget,
409
GdkEventExpose *expose)
411
GnomeVolumeControlVolume *vol = GNOME_VOLUME_CONTROL_VOLUME (widget);
413
/* clear background */
414
gdk_window_clear_area (widget->window, 0, 0,
415
widget->allocation.width,
416
widget->allocation.height);
418
if (vol->track->num_channels > 1) {
419
gint x_offset, y_offset, height, width;
420
GtkRequisition scale_req, but_req;
423
/* request size of kids */
424
GTK_WIDGET_GET_CLASS (vol->button)->size_request (vol->button, &but_req);
425
GTK_WIDGET_GET_CLASS (vol->scales->data)->size_request (vol->scales->data,
429
x_offset = (widget->allocation.width -
430
((vol->track->num_channels * scale_req.width) +
431
(vol->track->num_channels - 1) * vol->padding)) / 2;
432
y_offset = widget->allocation.height - but_req.height;
433
width = widget->allocation.width - (2 * x_offset + but_req.width);
434
height = but_req.height / 2;
435
points[0].y = y_offset + 3;
436
points[1].y = points[2].y = points[0].y + height - 3;
438
/* draw chainbutton decorations */
439
points[0].x = points[1].x = x_offset + 3;
440
points[2].x = points[0].x + width - 6;
441
gtk_paint_polygon (widget->style, widget->window,
442
GTK_WIDGET_STATE (widget),
443
GTK_SHADOW_ETCHED_IN,
444
&expose->area, widget, "hseparator",
447
points[0].x = points[1].x = widget->allocation.width - x_offset - 3;
448
points[2].x = points[0].x - width + 6;
449
gtk_paint_polygon (widget->style, widget->window,
450
GTK_WIDGET_STATE (widget),
451
GTK_SHADOW_ETCHED_IN,
452
&expose->area, widget, "hseparator",
456
/* take care of redrawing the kids */
457
return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, expose);
465
cb_volume_changed (GtkAdjustment *_adj,
468
GnomeVolumeControlVolume *vol = data;
469
gint *volumes, i = 0;
475
volumes = g_new (gint, vol->track->num_channels);
477
for (scales = vol->scales; scales != NULL; scales = scales->next) {
478
GtkAdjustment *adj = gtk_range_get_adjustment (scales->data);
480
if (gnome_volume_control_button_get_active (
481
GNOME_VOLUME_CONTROL_BUTTON (vol->button))) {
482
gtk_adjustment_set_value (adj, gtk_adjustment_get_value (_adj));
483
volumes[i++] = rint (gtk_adjustment_get_value (_adj));
485
volumes[i++] = rint (gtk_adjustment_get_value (adj));
489
gst_mixer_set_volume (vol->mixer, vol->track, volumes);
496
* To be called after unmuting.
500
gnome_volume_control_volume_sync (GnomeVolumeControlVolume * vol)
502
cb_volume_changed (gtk_range_get_adjustment (vol->scales->data), vol);
506
cb_lock_toggled (GtkToggleButton *button,
509
GnomeVolumeControlVolume *vol = data;
511
if (gnome_volume_control_button_get_active (
512
GNOME_VOLUME_CONTROL_BUTTON (vol->button))) {
513
/* get the mean value, and set it on the first adjustment.
514
* the cb_volume_changed () callback will take care of the
516
gint volume = 0, num = 0;
519
for (scales = vol->scales ; scales != NULL; scales = scales->next) {
520
GtkAdjustment *adj = gtk_range_get_adjustment (scales->data);
523
volume += gtk_adjustment_get_value (adj);
527
if (vol->scales != NULL) {
528
gtk_adjustment_set_value (gtk_range_get_adjustment (vol->scales->data),
535
* See if our volume is zero.
539
gnome_volume_control_volume_ask (GnomeVolumeControlVolume * vol,
540
gboolean *real_zero, gboolean *slider_zero)
543
gint *volumes, n, tot = 0;
545
volumes = g_new (gint, vol->track->num_channels);
546
gst_mixer_get_volume (vol->mixer, vol->track, volumes);
547
for (n = 0; n < vol->track->num_channels; n++)
550
*real_zero = (tot == 0);
553
for (n = 0, scales = vol->scales;
554
scales != NULL; scales = scales->next, n++) {
555
GtkAdjustment *adj = gtk_range_get_adjustment (scales->data);
557
if (rint (gtk_adjustment_get_value (adj)) != 0) {
558
*slider_zero = FALSE;
565
* Timeout to check for volume changes.
569
cb_check (gpointer data)
571
GnomeVolumeControlVolume *vol = data;
573
gboolean real_zero, slider_zero;
576
/* don't do callbacks */
581
volumes = g_new (gint, vol->track->num_channels);
582
gst_mixer_get_volume (vol->mixer, vol->track, volumes);
583
gnome_volume_control_volume_ask (vol, &real_zero, &slider_zero);
584
if (real_zero || GST_MIXER_TRACK_HAS_FLAG (vol->track,
585
GST_MIXER_TRACK_MUTE)) {
592
for (n = 0, scales = vol->scales;
593
scales != NULL; scales = scales->next, n++) {
594
GtkAdjustment *adj = gtk_range_get_adjustment (scales->data);
596
if ((gint) gtk_adjustment_get_value (adj) != volumes[n]) {
597
gtk_range_set_value (scales->data, volumes[n]);
600
/* should we release lock? */
601
if (volumes[n] != volumes[0]) {
602
gnome_volume_control_button_set_active (
603
GNOME_VOLUME_CONTROL_BUTTON (vol->button), FALSE);