~ubuntu-branches/ubuntu/utopic/rhythmbox/utopic-proposed

« back to all changes in this revision

Viewing changes to widgets/rb-volume.c

Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
 
/*
3
 
 *  arch-tag: Implementation of Rhythmbox volume control button
4
 
 * 
5
 
 *  Copyright (C) 2003 Colin Walters <walters@rhythmbox.org>
6
 
 *
7
 
 * Some portions are:
8
 
 *   (C) Copyright 2001, Richard Hult
9
 
 *
10
 
 *   Author: Richard Hult <rhult@codefactory.se>
11
 
 *
12
 
 *   Loosely based on the mixer applet:
13
 
 *
14
 
 *   GNOME audio mixer module
15
 
 *   (C) 1998 The Free Software Foundation
16
 
 *
17
 
 *   Author: Michael Fulbright <msf@redhat.com>:
18
 
 *
19
 
 *   Based on:
20
 
 *
21
 
 *   GNOME time/date display module.
22
 
 *   (C) 1997 The Free Software Foundation
23
 
 *
24
 
 *   Authors: Miguel de Icaza
25
 
 *            Federico Mena
26
 
 *
27
 
 *  This program is free software; you can redistribute it and/or modify
28
 
 *  it under the terms of the GNU General Public License as published by
29
 
 *  the Free Software Foundation; either version 2 of the License, or
30
 
 *  (at your option) any later version.
31
 
 *
32
 
 *  This program is distributed in the hope that it will be useful,
33
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
34
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35
 
 *  GNU General Public License for more details.
36
 
 *
37
 
 *  You should have received a copy of the GNU General Public License
38
 
 *  along with this program; if not, write to the Free Software
39
 
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40
 
 *
41
 
 */
42
 
 
43
 
#include <gtk/gtk.h>
44
 
#include <gdk/gdkkeysyms.h>
45
 
#include <config.h>
46
 
#include <libgnome/gnome-i18n.h>
47
 
#include <libgnome/gnome-url.h>
48
 
 
49
 
#include "rb-volume.h"
50
 
#include "rb-debug.h"
51
 
#include "rb-stock-icons.h"
52
 
#include "eel-gconf-extensions.h"
53
 
#include "rb-preferences.h"
54
 
#include "rb-util.h"
55
 
 
56
 
static void rb_volume_class_init (RBVolumeClass *klass);
57
 
static void rb_volume_init (RBVolume *link);
58
 
static void rb_volume_finalize (GObject *object);
59
 
static void rb_volume_sync_volume (RBVolume *volume);
60
 
static void clicked_cb (GtkButton *button, RBVolume *volume);
61
 
static gboolean scroll_cb (GtkWidget *widget, GdkEvent *event, gpointer unused);
62
 
static gboolean scale_button_release_event_cb (GtkWidget *widget,
63
 
                                               GdkEventButton *event, RBVolume *volume);
64
 
static gboolean scale_button_event_cb (GtkWidget *widget, GdkEventButton *event,
65
 
                                       RBVolume *volume);
66
 
static gboolean scale_key_press_event_cb (GtkWidget *widget, GdkEventKey *event,
67
 
                                          RBVolume *volume);
68
 
static void mixer_value_changed_cb (GtkAdjustment *adj, RBVolume *volume);
69
 
static void volume_changed_cb (GConfClient *client, guint cnxn_id,
70
 
                               GConfEntry *entry, RBVolume *volume);
71
 
 
72
 
#define VOLUME_MAX 1.0
73
 
 
74
 
struct RBVolumePrivate
75
 
{
76
 
        GtkWidget *button;
77
 
 
78
 
        GtkWidget *window;
79
 
 
80
 
        GtkWidget *scale;
81
 
        GtkAdjustment *adj;
82
 
 
83
 
        GtkWidget *max_image;
84
 
        GtkWidget *medium_image;
85
 
        GtkWidget *min_image;
86
 
        GtkWidget *zero_image;
87
 
};
88
 
 
89
 
enum
90
 
{
91
 
        PROP_0,
92
 
};
93
 
 
94
 
static GtkEventBoxClass *parent_class = NULL;
95
 
 
96
 
GType
97
 
rb_volume_get_type (void)
98
 
{
99
 
        static GType rb_volume_type = 0;
100
 
 
101
 
        if (rb_volume_type == 0)
102
 
        {
103
 
                static const GTypeInfo our_info =
104
 
                {
105
 
                        sizeof (RBVolumeClass),
106
 
                        NULL,
107
 
                        NULL,
108
 
                        (GClassInitFunc) rb_volume_class_init,
109
 
                        NULL,
110
 
                        NULL,
111
 
                        sizeof (RBVolume),
112
 
                        0,
113
 
                        (GInstanceInitFunc) rb_volume_init
114
 
                };
115
 
 
116
 
                rb_volume_type = g_type_register_static (GTK_TYPE_EVENT_BOX,
117
 
                                                       "RBVolume",
118
 
                                                       &our_info, 0);
119
 
        }
120
 
 
121
 
        return rb_volume_type;
122
 
}
123
 
 
124
 
static void
125
 
rb_volume_class_init (RBVolumeClass *klass)
126
 
{
127
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
128
 
 
129
 
        parent_class = g_type_class_peek_parent (klass);
130
 
 
131
 
        object_class->finalize = rb_volume_finalize;
132
 
}
133
 
 
134
 
static void
135
 
rb_volume_init (RBVolume *volume)
136
 
{
137
 
        GtkWidget *frame;
138
 
        GtkWidget *inner_frame;
139
 
        GtkWidget *pluslabel, *minuslabel;
140
 
        GtkWidget *event;
141
 
        GtkWidget *box;
142
 
 
143
 
        volume->priv = g_new0 (RBVolumePrivate, 1);
144
 
 
145
 
        volume->priv->button = gtk_button_new ();
146
 
 
147
 
        gtk_container_add (GTK_CONTAINER (volume), volume->priv->button);
148
 
 
149
 
        volume->priv->max_image = rb_image_new_from_stock (RB_STOCK_VOLUME_MAX,
150
 
                                                           GTK_ICON_SIZE_LARGE_TOOLBAR);
151
 
        g_object_ref (G_OBJECT (volume->priv->max_image));
152
 
        volume->priv->medium_image = rb_image_new_from_stock (RB_STOCK_VOLUME_MEDIUM,
153
 
                                                             GTK_ICON_SIZE_LARGE_TOOLBAR);
154
 
        g_object_ref (G_OBJECT (volume->priv->medium_image));
155
 
        volume->priv->min_image = rb_image_new_from_stock (RB_STOCK_VOLUME_MIN,
156
 
                                                           GTK_ICON_SIZE_LARGE_TOOLBAR);
157
 
        g_object_ref (G_OBJECT (volume->priv->min_image));
158
 
        volume->priv->zero_image = rb_image_new_from_stock (RB_STOCK_VOLUME_ZERO,
159
 
                                                            GTK_ICON_SIZE_LARGE_TOOLBAR);
160
 
        g_object_ref (G_OBJECT (volume->priv->zero_image));
161
 
 
162
 
        gtk_container_add (GTK_CONTAINER (volume->priv->button), volume->priv->max_image);
163
 
 
164
 
        g_signal_connect_object (G_OBJECT (volume->priv->button), "clicked",
165
 
                                 G_CALLBACK (clicked_cb), volume, 0);
166
 
        g_signal_connect_object (G_OBJECT (volume->priv->button), "scroll_event",
167
 
                                 G_CALLBACK (scroll_cb),
168
 
                                 volume, 0);
169
 
        gtk_widget_show_all (GTK_WIDGET (volume));
170
 
 
171
 
        volume->priv->window = gtk_window_new (GTK_WINDOW_POPUP);
172
 
 
173
 
        volume->priv->adj = GTK_ADJUSTMENT (gtk_adjustment_new (50,
174
 
                                                               0.0,
175
 
                                                               VOLUME_MAX,
176
 
                                                               VOLUME_MAX/20,
177
 
                                                               VOLUME_MAX/10,
178
 
                                                               0.0));
179
 
        g_signal_connect_object (volume->priv->adj,
180
 
                                 "value-changed",
181
 
                                 (GCallback) mixer_value_changed_cb,
182
 
                                 volume, 0);
183
 
 
184
 
        frame = gtk_frame_new (NULL);
185
 
        gtk_container_set_border_width (GTK_CONTAINER (frame), 0);
186
 
        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
187
 
 
188
 
        inner_frame = gtk_frame_new (NULL);
189
 
        gtk_container_set_border_width (GTK_CONTAINER (inner_frame), 0);
190
 
        gtk_frame_set_shadow_type (GTK_FRAME (inner_frame), GTK_SHADOW_NONE);
191
 
 
192
 
        event = gtk_event_box_new ();
193
 
        /* This signal is to not let button press close the popup when the press is
194
 
        ** in the scale */
195
 
        g_signal_connect_after (event, "button_press_event",
196
 
                                G_CALLBACK (scale_button_event_cb), volume);
197
 
 
198
 
        box = gtk_vbox_new (FALSE, 0);
199
 
        volume->priv->scale = gtk_vscale_new (volume->priv->adj);
200
 
        gtk_range_set_inverted (GTK_RANGE (volume->priv->scale), TRUE);
201
 
        gtk_widget_set_size_request (volume->priv->scale, -1, 100);
202
 
 
203
 
        g_signal_connect_object (G_OBJECT (volume->priv->window), "scroll_event",
204
 
                                 G_CALLBACK (scroll_cb),
205
 
                                 volume, 0);
206
 
 
207
 
        g_signal_connect_object (G_OBJECT (volume->priv->window),
208
 
                                 "button-press-event",
209
 
                                 (GCallback) scale_button_release_event_cb,
210
 
                                 volume, 0);
211
 
 
212
 
        /* button event on the scale widget are not catched by its parent window
213
 
        ** so we must connect to this widget as well */
214
 
        g_signal_connect_object (G_OBJECT (volume->priv->scale),
215
 
                                 "button-release-event",
216
 
                                 (GCallback) scale_button_release_event_cb,
217
 
                                 volume, 0);
218
 
 
219
 
        g_signal_connect_object (G_OBJECT (volume->priv->scale),
220
 
                                 "key-press-event",
221
 
                                 (GCallback) scale_key_press_event_cb,
222
 
                                 volume, 0);
223
 
 
224
 
        gtk_scale_set_draw_value (GTK_SCALE (volume->priv->scale), FALSE);
225
 
 
226
 
        gtk_range_set_update_policy (GTK_RANGE (volume->priv->scale),
227
 
                                     GTK_UPDATE_CONTINUOUS);
228
 
 
229
 
        gtk_container_add (GTK_CONTAINER (volume->priv->window), frame);
230
 
 
231
 
        gtk_container_add (GTK_CONTAINER (frame), inner_frame);
232
 
 
233
 
        /* Translators - The + and - refer to increasing and decreasing the volume.
234
 
        ** I don't know if there are sensible alternatives in other languages */
235
 
        pluslabel = gtk_label_new (_("+"));
236
 
        minuslabel = gtk_label_new (_("-"));
237
 
 
238
 
        gtk_box_pack_start (GTK_BOX (box), pluslabel, FALSE, FALSE, 0);
239
 
        gtk_box_pack_end (GTK_BOX (box), minuslabel, FALSE, FALSE, 0);
240
 
        gtk_box_pack_start (GTK_BOX (box), volume->priv->scale, TRUE, TRUE, 0);
241
 
 
242
 
        gtk_container_add (GTK_CONTAINER (event), box);
243
 
        gtk_container_add (GTK_CONTAINER (inner_frame), event);
244
 
 
245
 
        eel_gconf_notification_add (CONF_STATE_VOLUME,
246
 
                                    (GConfClientNotifyFunc) volume_changed_cb,
247
 
                                    volume);
248
 
        rb_volume_sync_volume (volume);
249
 
}
250
 
 
251
 
static void
252
 
rb_volume_finalize (GObject *object)
253
 
{
254
 
        RBVolume *volume;
255
 
 
256
 
        g_return_if_fail (object != NULL);
257
 
        g_return_if_fail (RB_IS_VOLUME (object));
258
 
 
259
 
        volume = RB_VOLUME (object);
260
 
 
261
 
        g_return_if_fail (volume->priv != NULL);
262
 
 
263
 
        g_free (volume->priv);
264
 
 
265
 
        G_OBJECT_CLASS (parent_class)->finalize (object);
266
 
}
267
 
 
268
 
RBVolume *
269
 
rb_volume_new (void)
270
 
{
271
 
        RBVolume *volume;
272
 
 
273
 
        volume = RB_VOLUME (g_object_new (RB_TYPE_VOLUME, NULL));
274
 
 
275
 
        g_return_val_if_fail (volume->priv != NULL, NULL);
276
 
 
277
 
        return volume;
278
 
}
279
 
 
280
 
static void
281
 
rb_volume_sync_volume (RBVolume *volume)
282
 
{
283
 
        float vol;
284
 
 
285
 
        vol = eel_gconf_get_float (CONF_STATE_VOLUME);
286
 
        rb_debug ("current volume is %f", vol);
287
 
        rb_volume_set_volume (volume, vol);
288
 
}
289
 
 
290
 
void
291
 
rb_volume_set_volume (RBVolume *volume, float vol)
292
 
{
293
 
        GtkWidget *image;
294
 
 
295
 
        gtk_container_remove (GTK_CONTAINER (volume->priv->button),
296
 
                              gtk_bin_get_child (GTK_BIN (volume->priv->button)));
297
 
 
298
 
        if (vol <= 0)
299
 
                image = volume->priv->zero_image;
300
 
        else if (vol <= (VOLUME_MAX / 3.0))
301
 
                image = volume->priv->min_image;
302
 
        else if (vol <= 2.0 * (VOLUME_MAX / 3.0))
303
 
                image = volume->priv->medium_image;
304
 
        else
305
 
                image = volume->priv->max_image;
306
 
 
307
 
        gtk_widget_show (image);
308
 
        gtk_container_add (GTK_CONTAINER (volume->priv->button), image);
309
 
 
310
 
        gtk_adjustment_set_value (volume->priv->adj, vol);
311
 
}
312
 
 
313
 
static void
314
 
clicked_cb (GtkButton *button, RBVolume *volume)
315
 
{
316
 
        GtkRequisition  req;
317
 
        GdkGrabStatus pointer, keyboard;
318
 
        gint x, y;
319
 
        gint button_width, button_height;
320
 
        gint window_width, window_height;
321
 
        gint spacing = 5;
322
 
        gint max_y;
323
 
 
324
 
        gint volume_slider_x;
325
 
        gint volume_slider_y;
326
 
        
327
 
        rb_debug ("volume clicked");
328
 
 
329
 
/*      if (GTK_WIDGET_VISIBLE (GTK_WIDGET (volume->priv->window))) */
330
 
/*              return; */
331
 
 
332
 
        /*
333
 
         * Position the popup right next to the button.
334
 
         */
335
 
        
336
 
        max_y = gdk_screen_height ();
337
 
        
338
 
        gtk_widget_size_request (GTK_WIDGET (volume->priv->window), &req);
339
 
 
340
 
        gdk_window_get_origin (gtk_widget_get_parent_window (GTK_BIN (volume->priv->button)->child), &x, &y);
341
 
        gdk_drawable_get_size (gtk_widget_get_parent_window (GTK_BIN (volume->priv->button)->child), &button_width, &button_height);
342
 
        rb_debug ("window origin: %d %d; size: %d %d", x, y, button_width, button_height);
343
 
 
344
 
 
345
 
        
346
 
        gtk_widget_show_all (volume->priv->window);
347
 
        gdk_drawable_get_size (gtk_widget_get_parent_window (GTK_BIN (volume->priv->window)->child), &window_width, &window_height);
348
 
        
349
 
        volume_slider_x = x + (button_width - window_width) / 2;
350
 
        
351
 
        if (y + button_width + window_height + spacing < max_y) {
352
 
                /* if volume slider will fit on the screen, display it under
353
 
                 * the volume button
354
 
                 */
355
 
                volume_slider_y = y + button_width + spacing;
356
 
        } else {
357
 
                /* otherwise display it above the volume button */
358
 
                volume_slider_y = y - window_height - spacing;
359
 
        }
360
 
        
361
 
        gtk_window_move (GTK_WINDOW (volume->priv->window), volume_slider_x, volume_slider_y);
362
 
 
363
 
        /*
364
 
         * Grab focus and pointer.
365
 
         */
366
 
        rb_debug ("grabbing focus");
367
 
        gtk_widget_grab_focus (volume->priv->window);
368
 
        gtk_grab_add (volume->priv->window);
369
 
 
370
 
        pointer = gdk_pointer_grab (volume->priv->window->window,
371
 
                                    TRUE,
372
 
                                    (GDK_BUTTON_PRESS_MASK |
373
 
                                     GDK_BUTTON_RELEASE_MASK |
374
 
                                     GDK_POINTER_MOTION_MASK |
375
 
                                     GDK_SCROLL_MASK),
376
 
                                    NULL, NULL, GDK_CURRENT_TIME);
377
 
 
378
 
        keyboard = gdk_keyboard_grab (volume->priv->window->window,
379
 
                                      TRUE,
380
 
                                      GDK_CURRENT_TIME);
381
 
 
382
 
        if (pointer != GDK_GRAB_SUCCESS || keyboard != GDK_GRAB_SUCCESS) {
383
 
                /* We could not grab. */
384
 
                rb_debug ("grab failed");
385
 
                gtk_grab_remove (volume->priv->window);
386
 
                gtk_widget_hide (volume->priv->window);
387
 
 
388
 
                if (pointer == GDK_GRAB_SUCCESS) {
389
 
                        gdk_keyboard_ungrab (GDK_CURRENT_TIME);
390
 
                }
391
 
                if (keyboard == GDK_GRAB_SUCCESS) {
392
 
                        gdk_pointer_ungrab (GDK_CURRENT_TIME);
393
 
                }
394
 
 
395
 
                g_warning ("Could not grab X server!");
396
 
                return;
397
 
        }
398
 
 
399
 
        /* gtk_frame_set_shadow_type (GTK_FRAME (volume->priv->frame), GTK_SHADOW_IN); */
400
 
}
401
 
 
402
 
static gboolean
403
 
scroll_cb (GtkWidget *widget, GdkEvent *event, gpointer unused)
404
 
{
405
 
        float volume = eel_gconf_get_float (CONF_STATE_VOLUME);
406
 
 
407
 
        switch(event->scroll.direction) {
408
 
        case GDK_SCROLL_UP:
409
 
                volume += 0.1;
410
 
                if (volume > 1.0)
411
 
                        volume = 1.0;
412
 
                break;
413
 
        case GDK_SCROLL_DOWN:
414
 
                volume -= 0.1;
415
 
                if (volume < 0)
416
 
                        volume = 0;
417
 
                break;
418
 
        case GDK_SCROLL_LEFT:
419
 
        case GDK_SCROLL_RIGHT:
420
 
                break;
421
 
        }
422
 
 
423
 
        rb_debug ("got scroll, setting volume to %f", volume);
424
 
        eel_gconf_set_float (CONF_STATE_VOLUME, volume);
425
 
 
426
 
        return FALSE;
427
 
}
428
 
 
429
 
 
430
 
static void
431
 
rb_volume_popup_hide (RBVolume *volume)
432
 
{
433
 
        rb_debug ("hiding popup");
434
 
        gtk_grab_remove (volume->priv->window);
435
 
        gdk_pointer_ungrab (GDK_CURRENT_TIME);
436
 
        gdk_keyboard_ungrab (GDK_CURRENT_TIME);
437
 
 
438
 
        gtk_widget_hide (GTK_WIDGET (volume->priv->window));
439
 
 
440
 
/*      gtk_frame_set_shadow_type (GTK_FRAME (data->frame), GTK_SHADOW_NONE); */
441
 
}
442
 
 
443
 
static gboolean
444
 
scale_button_release_event_cb (GtkWidget *widget, GdkEventButton *event, RBVolume *volume)
445
 
{
446
 
        rb_debug ("scale release");
447
 
        rb_volume_popup_hide (volume);
448
 
        return FALSE;
449
 
}
450
 
 
451
 
static gboolean
452
 
scale_button_event_cb (GtkWidget *widget, GdkEventButton *event, RBVolume *volume)
453
 
{
454
 
        rb_debug ("event");
455
 
        return TRUE;
456
 
}
457
 
 
458
 
static gboolean
459
 
scale_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, RBVolume *volume)
460
 
{
461
 
        rb_debug ("got key press");
462
 
        switch (event->keyval) {
463
 
        case GDK_KP_Enter:
464
 
        case GDK_ISO_Enter:
465
 
        case GDK_3270_Enter:
466
 
        case GDK_Return:
467
 
        case GDK_space:
468
 
        case GDK_KP_Space:
469
 
                rb_volume_popup_hide (volume);
470
 
                return TRUE;
471
 
        default:
472
 
                break;
473
 
        }
474
 
 
475
 
        return FALSE;
476
 
}
477
 
 
478
 
static void
479
 
mixer_value_changed_cb (GtkAdjustment *adj, RBVolume *volume)
480
 
{
481
 
        float vol = gtk_adjustment_get_value (volume->priv->adj);
482
 
 
483
 
        rb_debug ("setting volume to %f", vol);
484
 
 
485
 
        eel_gconf_set_float (CONF_STATE_VOLUME, vol);
486
 
}
487
 
 
488
 
static void volume_changed_cb (GConfClient *client, guint cnxn_id,
489
 
                               GConfEntry *entry, RBVolume *volume)
490
 
{
491
 
        rb_debug ("volume changed");
492
 
 
493
 
        rb_volume_sync_volume (volume);
494
 
}