~blackskad/gnomeradio/dev-vol-button

« back to all changes in this revision

Viewing changes to src/bacon-volume.c

  • Committer: mfcn
  • Date: 2006-02-26 17:05:36 UTC
  • Revision ID: svn-v3-trunk0:ba97a3d1-ec25-0410-b1c6-e06ad936ea6c:trunk:127
        * Add src/bacon-volume.c, src/bacon-volume.h from totem
        * src/Makfile.am: add src/bacon-volume.c, src/bacon-volume.h
        * configure.in: version 1.6.99
        * src/gui.c, src/gui.h, src/lirc.c: Remove volume-slider and
        mute-button and use bacon-volume-button instead

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Volume Button / popup widget
 
2
 * (c) copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Library General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
 * Boston, MA 02111-1307, USA.
 
18
 */
 
19
 
 
20
#ifdef HAVE_CONFIG_H
 
21
#include "config.h"
 
22
#endif
 
23
 
 
24
#define _GNU_SOURCE
 
25
#include <math.h>
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include <glib/gi18n.h>
 
29
#include <gtk/gtk.h>
 
30
#include "bacon-volume.h"
 
31
 
 
32
#define SCALE_SIZE 100
 
33
#define CLICK_TIMEOUT 250
 
34
 
 
35
enum {
 
36
  SIGNAL_VALUE_CHANGED,
 
37
  NUM_SIGNALS
 
38
};
 
39
 
 
40
static void     bacon_volume_button_class_init  (BaconVolumeButtonClass * klass);
 
41
static void     bacon_volume_button_init        (BaconVolumeButton * button);
 
42
static void     bacon_volume_button_dispose     (GObject        * object);
 
43
 
 
44
static gboolean bacon_volume_button_scroll      (GtkWidget      * widget,
 
45
                                                 GdkEventScroll * event);
 
46
static gboolean bacon_volume_button_press       (GtkWidget      * widget,
 
47
                                                 GdkEventButton * event);
 
48
static gboolean cb_dock_press                   (GtkWidget      * widget,
 
49
                                                 GdkEventButton * event,
 
50
                                                 gpointer         data);
 
51
 
 
52
static gboolean cb_button_press                 (GtkWidget      * widget,
 
53
                                                 GdkEventButton * event,
 
54
                                                 gpointer         data);
 
55
static gboolean cb_button_release               (GtkWidget      * widget,
 
56
                                                 GdkEventButton * event,
 
57
                                                 gpointer         data);
 
58
static void     bacon_volume_scale_value_changed(GtkRange       * range);
 
59
 
 
60
/* see below for scale definitions */
 
61
static GtkWidget *bacon_volume_scale_new        (BaconVolumeButton * button,
 
62
                                                 float min, float max,
 
63
                                                 float step);
 
64
 
 
65
static GtkButtonClass *parent_class = NULL;
 
66
static guint signals[NUM_SIGNALS] = { 0 };
 
67
 
 
68
GType
 
69
bacon_volume_button_get_type (void)
 
70
{
 
71
  static GType bacon_volume_button_type = 0;
 
72
 
 
73
  if (!bacon_volume_button_type) {
 
74
    static const GTypeInfo bacon_volume_button_info = {
 
75
      sizeof (BaconVolumeButtonClass),
 
76
      NULL,
 
77
      NULL,
 
78
      (GClassInitFunc) bacon_volume_button_class_init,
 
79
      NULL,
 
80
      NULL,
 
81
      sizeof (BaconVolumeButton),
 
82
      0,
 
83
      (GInstanceInitFunc) bacon_volume_button_init,
 
84
      NULL
 
85
    };
 
86
 
 
87
    bacon_volume_button_type =
 
88
        g_type_register_static (GTK_TYPE_BUTTON, 
 
89
                                "BaconVolumeButton",
 
90
                                &bacon_volume_button_info, 0);
 
91
  }
 
92
 
 
93
  return bacon_volume_button_type;
 
94
}
 
95
 
 
96
static void
 
97
bacon_volume_button_class_init (BaconVolumeButtonClass *klass)
 
98
{
 
99
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
100
  GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
 
101
 
 
102
  parent_class = g_type_class_ref (GTK_TYPE_BUTTON);
 
103
 
 
104
  /* events */
 
105
  gobject_class->dispose = bacon_volume_button_dispose;
 
106
  gtkwidget_class->button_press_event = bacon_volume_button_press;
 
107
  gtkwidget_class->scroll_event = bacon_volume_button_scroll;
 
108
 
 
109
  /* signals */
 
110
  signals[SIGNAL_VALUE_CHANGED] = g_signal_new ("value-changed",
 
111
      G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
 
112
      G_STRUCT_OFFSET (BaconVolumeButtonClass, value_changed),
 
113
      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
 
114
}
 
115
 
 
116
static void
 
117
bacon_volume_button_init (BaconVolumeButton *button)
 
118
{
 
119
  button->timeout = FALSE;
 
120
  button->click_id = 0;
 
121
  button->dock = button->scale = NULL;
 
122
#ifndef HAVE_GTK_ONLY
 
123
  button->theme = gtk_icon_theme_get_default ();
 
124
#endif
 
125
}
 
126
 
 
127
static void
 
128
bacon_volume_button_dispose (GObject *object)
 
129
{
 
130
  BaconVolumeButton *button = BACON_VOLUME_BUTTON (object);
 
131
 
 
132
  if (button->dock) {
 
133
    gtk_widget_destroy (button->dock);
 
134
    button->dock = NULL;
 
135
  }
 
136
 
 
137
  if (button->theme) {
 
138
    g_object_unref (G_OBJECT (button->theme));
 
139
    button->theme = NULL;
 
140
  }
 
141
 
 
142
  if (button->click_id != 0) {
 
143
    g_source_remove (button->click_id);
 
144
    button->click_id = 0;
 
145
  }
 
146
 
 
147
  G_OBJECT_CLASS (parent_class)->dispose (object);
 
148
}
 
149
 
 
150
/*
 
151
 * public API.
 
152
 */
 
153
 
 
154
GtkWidget *
 
155
bacon_volume_button_new (GtkIconSize size,
 
156
                         float min, float max,
 
157
                         float step)
 
158
{
 
159
  BaconVolumeButton *button;
 
160
  GtkWidget *frame, *box;
 
161
 
 
162
  button = g_object_new (BACON_TYPE_VOLUME_BUTTON, NULL);
 
163
  button->size = size;
 
164
  gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
 
165
 
 
166
#ifndef HAVE_GTK_ONLY
 
167
  /* image */
 
168
  button->image = gtk_image_new ();
 
169
  gtk_container_add (GTK_CONTAINER (button), button->image);
 
170
  gtk_widget_show_all (button->image);
 
171
#endif
 
172
 
 
173
  /* window */
 
174
  button->dock = gtk_window_new (GTK_WINDOW_POPUP);
 
175
  g_signal_connect (button->dock, "button-press-event",
 
176
                    G_CALLBACK (cb_dock_press), button);
 
177
  gtk_window_set_decorated (GTK_WINDOW (button->dock), FALSE);
 
178
 
 
179
  /* frame */
 
180
  frame = gtk_frame_new (NULL);
 
181
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
 
182
  gtk_container_add (GTK_CONTAINER (button->dock), frame);
 
183
  box = gtk_vbox_new (FALSE, 0);
 
184
  gtk_container_add (GTK_CONTAINER (frame), box);
 
185
 
 
186
  /* + */
 
187
  button->plus = gtk_button_new_with_label (_("+"));
 
188
  gtk_button_set_relief (GTK_BUTTON (button->plus), GTK_RELIEF_NONE);
 
189
  g_signal_connect (button->plus, "button-press-event",
 
190
                    G_CALLBACK (cb_button_press), button);
 
191
  g_signal_connect (button->plus, "button-release-event",
 
192
                    G_CALLBACK (cb_button_release), button);
 
193
  gtk_box_pack_start (GTK_BOX (box), button->plus, TRUE, FALSE, 0);
 
194
 
 
195
  /* scale */
 
196
  button->scale = bacon_volume_scale_new (button, min, max, step);
 
197
  gtk_widget_set_size_request (button->scale, -1, SCALE_SIZE);
 
198
  gtk_scale_set_draw_value (GTK_SCALE (button->scale), FALSE);
 
199
  gtk_range_set_inverted (GTK_RANGE (button->scale), TRUE);
 
200
  gtk_box_pack_start (GTK_BOX (box), button->scale, TRUE, FALSE, 0);
 
201
 
 
202
  /* - */
 
203
  button->min = gtk_button_new_with_label (_("-"));
 
204
  gtk_button_set_relief (GTK_BUTTON (button->min), GTK_RELIEF_NONE);
 
205
  g_signal_connect (button->min, "button-press-event",
 
206
                   G_CALLBACK (cb_button_press), button);
 
207
  g_signal_connect (button->min, "button-release-event",
 
208
                    G_CALLBACK (cb_button_release), button);
 
209
  gtk_box_pack_start (GTK_BOX (box), button->min, TRUE, FALSE, 0);
 
210
 
 
211
  /* call callback once so original icon is drawn */
 
212
  bacon_volume_scale_value_changed (GTK_RANGE (button->scale));
 
213
 
 
214
  return GTK_WIDGET (button);
 
215
}
 
216
 
 
217
float
 
218
bacon_volume_button_get_value (BaconVolumeButton * button)
 
219
{
 
220
  g_return_val_if_fail (button != NULL, 0);
 
221
 
 
222
  return gtk_range_get_value (GTK_RANGE (button->scale));
 
223
}
 
224
 
 
225
void
 
226
bacon_volume_button_set_value (BaconVolumeButton * button,
 
227
                               float value)
 
228
{
 
229
  g_return_if_fail (button != NULL);
 
230
 
 
231
  gtk_range_set_value (GTK_RANGE (button->scale), value);
 
232
}
 
233
 
 
234
/*
 
235
 * button callbacks.
 
236
 */
 
237
 
 
238
static gboolean
 
239
bacon_volume_button_scroll (GtkWidget      * widget,
 
240
                            GdkEventScroll * event)
 
241
{
 
242
  BaconVolumeButton *button = BACON_VOLUME_BUTTON (widget);
 
243
  GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
 
244
  float d;
 
245
 
 
246
  if (event->type != GDK_SCROLL)
 
247
    return FALSE;
 
248
 
 
249
  d = bacon_volume_button_get_value (button);
 
250
  if (event->direction == GDK_SCROLL_UP) {
 
251
    d += adj->step_increment;
 
252
    if (d > adj->upper)
 
253
      d = adj->upper;
 
254
  } else {
 
255
    d -= adj->step_increment;
 
256
    if (d < adj->lower)
 
257
      d = adj->lower;
 
258
  }
 
259
  bacon_volume_button_set_value (button, d);
 
260
 
 
261
  return TRUE;
 
262
}
 
263
 
 
264
static gboolean
 
265
bacon_volume_button_press (GtkWidget      * widget,
 
266
                           GdkEventButton * event)
 
267
{
 
268
  BaconVolumeButton *button = BACON_VOLUME_BUTTON (widget);
 
269
  GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
 
270
  gint x, y, m, dx, dy, sx, sy, ystartoff, mouse_y;
 
271
  float v;
 
272
  GdkEventButton *e;
 
273
 
 
274
  /* position roughly */
 
275
  gdk_window_get_origin (widget->window, &x, &y);
 
276
  x += widget->allocation.x;
 
277
  y += widget->allocation.y;
 
278
  gtk_window_move (GTK_WINDOW (button->dock), x, y - (SCALE_SIZE / 2));
 
279
  gtk_widget_show_all (button->dock);
 
280
  gdk_window_get_origin (button->dock->window, &dx, &dy);
 
281
  dy += button->dock->allocation.y;
 
282
  gdk_window_get_origin (button->scale->window, &sx, &sy);
 
283
  sy += button->scale->allocation.y;
 
284
  ystartoff = sy - dy;
 
285
  mouse_y = event->y;
 
286
  button->timeout = TRUE;
 
287
 
 
288
  /* position (needs widget to be shown already) */
 
289
  v = bacon_volume_button_get_value (button) / (adj->upper - adj->lower);
 
290
  x += (widget->allocation.width - button->dock->allocation.width) / 2;
 
291
  y -= ystartoff;
 
292
  y -= GTK_RANGE (button->scale)->min_slider_size / 2;
 
293
  m = button->scale->allocation.height -
 
294
      GTK_RANGE (button->scale)->min_slider_size;
 
295
  y -= m * (1.0 - v);
 
296
  y += mouse_y;
 
297
  gtk_window_move (GTK_WINDOW (button->dock), x, y);
 
298
  gdk_window_get_origin (button->scale->window, &sx, &sy);
 
299
 
 
300
  GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
 
301
 
 
302
  /* grab focus */
 
303
  gtk_widget_grab_focus (button->dock);
 
304
  gtk_grab_add (button->dock);
 
305
  gdk_pointer_grab (button->dock->window, TRUE,
 
306
      GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
 
307
      GDK_POINTER_MOTION_MASK, NULL, NULL, GDK_CURRENT_TIME);
 
308
  gdk_keyboard_grab (button->dock->window, TRUE, GDK_CURRENT_TIME);
 
309
 
 
310
  /* forward event to the slider */
 
311
  e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
 
312
  e->window = button->scale->window;
 
313
 
 
314
  /* position: the X position isn't relevant, halfway will work just fine.
 
315
   * The vertical position should be *exactly* in the middle of the slider
 
316
   * of the scale; if we don't do that correctly, it'll move from its current
 
317
   * position, which means a position change on-click, which is bad. */
 
318
  e->x = button->scale->allocation.width / 2;
 
319
  m = button->scale->allocation.height -
 
320
      GTK_RANGE (button->scale)->min_slider_size;
 
321
  e->y = ((1.0 - v) * m) + GTK_RANGE (button->scale)->min_slider_size / 2;
 
322
  gtk_widget_event (button->scale, (GdkEvent *) e);
 
323
  e->window = event->window;
 
324
  gdk_event_free ((GdkEvent *) e);
 
325
 
 
326
  button->pop_time = event->time;
 
327
 
 
328
  return TRUE;
 
329
}
 
330
 
 
331
/*
 
332
 * +/- button callbacks.
 
333
 */
 
334
 
 
335
static gboolean
 
336
cb_button_timeout (gpointer data)
 
337
{
 
338
  BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
 
339
  GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
 
340
  float val;
 
341
  gboolean res = TRUE;
 
342
 
 
343
  if (button->click_id == 0)
 
344
    return FALSE;
 
345
 
 
346
  val = bacon_volume_button_get_value (button);
 
347
  val += button->direction;
 
348
  if (val <= adj->lower) {
 
349
    res = FALSE;
 
350
    val = adj->lower;
 
351
  } else if (val > adj->upper) {
 
352
    res = FALSE;
 
353
    val = adj->upper;
 
354
  }
 
355
  bacon_volume_button_set_value (button, val);
 
356
 
 
357
  if (!res) {
 
358
    g_source_remove (button->click_id);
 
359
    button->click_id = 0;
 
360
  }
 
361
 
 
362
  return res;
 
363
}
 
364
 
 
365
static gboolean
 
366
cb_button_press (GtkWidget      * widget,
 
367
                 GdkEventButton * event,
 
368
                 gpointer         data)
 
369
{
 
370
  BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
 
371
  GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
 
372
 
 
373
  if (button->click_id != 0)
 
374
    g_source_remove (button->click_id);
 
375
  button->direction = (widget == button->plus) ?
 
376
      fabs (adj->page_increment) : - fabs (adj->page_increment);
 
377
  button->click_id = g_timeout_add (CLICK_TIMEOUT,
 
378
                                    (GSourceFunc) cb_button_timeout, button);
 
379
  cb_button_timeout (button);
 
380
 
 
381
  return TRUE;
 
382
}
 
383
 
 
384
static gboolean
 
385
cb_button_release (GtkWidget      * widget,
 
386
                   GdkEventButton * event,
 
387
                   gpointer         data)
 
388
{
 
389
  BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
 
390
 
 
391
  if (button->click_id != 0) {
 
392
    g_source_remove (button->click_id);
 
393
    button->click_id = 0;
 
394
  }
 
395
 
 
396
  return TRUE;
 
397
}
 
398
 
 
399
/*
 
400
 * Scale callbacks.
 
401
 */
 
402
 
 
403
static void
 
404
bacon_volume_release_grab (BaconVolumeButton *button,
 
405
                           GdkEventButton * event)
 
406
{
 
407
  GdkEventButton *e;
 
408
 
 
409
  /* ungrab focus */
 
410
  gdk_keyboard_ungrab (GDK_CURRENT_TIME);
 
411
  gdk_pointer_ungrab (GDK_CURRENT_TIME);
 
412
  gtk_grab_remove (button->dock);
 
413
 
 
414
  /* hide again */
 
415
  gtk_widget_hide (button->dock);
 
416
  button->timeout = FALSE;
 
417
 
 
418
  e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
 
419
  e->window = GTK_WIDGET (button)->window;
 
420
  e->type = GDK_BUTTON_RELEASE;
 
421
  gtk_widget_event (GTK_WIDGET (button), (GdkEvent *) e);
 
422
  e->window = event->window;
 
423
  gdk_event_free ((GdkEvent *) e);
 
424
}
 
425
 
 
426
static gboolean
 
427
cb_dock_press (GtkWidget      * widget,
 
428
               GdkEventButton * event,
 
429
               gpointer         data)
 
430
{
 
431
  //GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *) event);
 
432
  BaconVolumeButton *button = BACON_VOLUME_BUTTON (data);
 
433
 
 
434
  if (/*ewidget == button->dock &&*/ event->type == GDK_BUTTON_PRESS) {
 
435
    bacon_volume_release_grab (button, event);
 
436
    return TRUE;
 
437
  }
 
438
 
 
439
  return FALSE;
 
440
}
 
441
 
 
442
/*
 
443
 * Scale stuff.
 
444
 */
 
445
 
 
446
#define BACON_TYPE_VOLUME_SCALE \
 
447
  (bacon_volume_scale_get_type ())
 
448
#define BACON_VOLUME_SCALE(obj) \
 
449
  (G_TYPE_CHECK_INSTANCE_CAST ((obj), BACON_TYPE_VOLUME_SCALE, \
 
450
                               BaconVolumeScale))
 
451
 
 
452
typedef struct _BaconVolumeScale {
 
453
  GtkVScale parent;
 
454
  BaconVolumeButton *button;
 
455
} BaconVolumeScale;
 
456
 
 
457
static GType    bacon_volume_scale_get_type      (void);
 
458
 
 
459
static void     bacon_volume_scale_class_init    (GtkVScaleClass * klass);
 
460
 
 
461
static gboolean bacon_volume_scale_press         (GtkWidget      * widget,
 
462
                                                  GdkEventButton * event);
 
463
static gboolean bacon_volume_scale_release       (GtkWidget      * widget,
 
464
                                                  GdkEventButton * event);
 
465
 
 
466
static GtkVScaleClass *scale_parent_class = NULL;
 
467
 
 
468
static GType
 
469
bacon_volume_scale_get_type (void)
 
470
{
 
471
  static GType bacon_volume_scale_type = 0;
 
472
 
 
473
  if (!bacon_volume_scale_type) {
 
474
    static const GTypeInfo bacon_volume_scale_info = {
 
475
      sizeof (GtkVScaleClass),
 
476
      NULL,
 
477
      NULL,
 
478
      (GClassInitFunc) bacon_volume_scale_class_init,
 
479
      NULL,
 
480
      NULL,
 
481
      sizeof (BaconVolumeScale),
 
482
      0,
 
483
      NULL,
 
484
      NULL
 
485
    };
 
486
 
 
487
    bacon_volume_scale_type =
 
488
        g_type_register_static (GTK_TYPE_VSCALE,
 
489
                                "BaconVolumeScale",
 
490
                                &bacon_volume_scale_info, 0);
 
491
  }
 
492
 
 
493
  return bacon_volume_scale_type;
 
494
}
 
495
 
 
496
static void
 
497
bacon_volume_scale_class_init (GtkVScaleClass * klass)
 
498
{
 
499
  GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
 
500
  GtkRangeClass *gtkrange_class = GTK_RANGE_CLASS (klass);
 
501
 
 
502
  scale_parent_class = g_type_class_ref (GTK_TYPE_VSCALE);
 
503
 
 
504
  gtkwidget_class->button_press_event = bacon_volume_scale_press;
 
505
  gtkwidget_class->button_release_event = bacon_volume_scale_release;
 
506
  gtkrange_class->value_changed = bacon_volume_scale_value_changed;
 
507
}
 
508
 
 
509
static GtkWidget *
 
510
bacon_volume_scale_new (BaconVolumeButton * button,
 
511
                        float min, float max,
 
512
                        float step)
 
513
{
 
514
  BaconVolumeScale *scale = g_object_new (BACON_TYPE_VOLUME_SCALE, NULL);
 
515
  GtkObject *adj;
 
516
 
 
517
  adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
 
518
  gtk_range_set_adjustment (GTK_RANGE (scale), GTK_ADJUSTMENT (adj));
 
519
  scale->button = button;
 
520
 
 
521
  return GTK_WIDGET (scale);
 
522
}
 
523
 
 
524
static gboolean
 
525
bacon_volume_scale_press (GtkWidget      * widget,
 
526
                          GdkEventButton * event)
 
527
{
 
528
  BaconVolumeScale *scale = BACON_VOLUME_SCALE (widget);
 
529
  BaconVolumeButton *button = scale->button;
 
530
 
 
531
  /* the scale will grab input; if we have input grabbed, all goes
 
532
   * horribly wrong, so let's not do that. */
 
533
  gtk_grab_remove (button->dock);
 
534
 
 
535
  return GTK_WIDGET_CLASS (scale_parent_class)->button_press_event (widget, event);
 
536
}
 
537
 
 
538
static gboolean
 
539
bacon_volume_scale_release (GtkWidget      * widget,
 
540
                            GdkEventButton * event)
 
541
{
 
542
  BaconVolumeScale *scale = BACON_VOLUME_SCALE (widget);
 
543
  BaconVolumeButton *button = scale->button;
 
544
  gboolean res;
 
545
 
 
546
  if (button->timeout) {
 
547
    /* if we did a quick click, leave the window open; else, hide it */
 
548
    if (event->time > button->pop_time + CLICK_TIMEOUT) {
 
549
      bacon_volume_release_grab (button, event);
 
550
      GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
 
551
      return TRUE;
 
552
    }
 
553
    button->timeout = FALSE;
 
554
  }
 
555
 
 
556
  res = GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
 
557
 
 
558
  /* the scale will release input; right after that, we *have to* grab
 
559
   * it back so we can catch out-of-scale clicks and hide the popup,
 
560
   * so I basically want a g_signal_connect_after_always(), but I can't
 
561
   * find that, so we do this complex 'first-call-parent-then-do-actual-
 
562
   * action' thingy... */
 
563
  gtk_grab_add (button->dock);
 
564
 
 
565
  return res;
 
566
}
 
567
 
 
568
static void
 
569
bacon_volume_scale_value_changed (GtkRange * range)
 
570
{
 
571
  BaconVolumeScale *scale = BACON_VOLUME_SCALE (range);
 
572
  BaconVolumeButton *button = scale->button;
 
573
  GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (button->scale));
 
574
  float step = (adj->upper - adj->lower) / 4;
 
575
  float val = gtk_range_get_value (range);
 
576
  gint w, h;
 
577
#ifdef HAVE_GTK_ONLY
 
578
  char *s;
 
579
 
 
580
  /* update label */
 
581
  s = g_strdup_printf ("%d", lrintf (val));
 
582
  gtk_button_set_label (GTK_BUTTON (button), s);
 
583
  g_free (s);
 
584
#else
 
585
  const char *s;
 
586
  GdkPixbuf *buf;
 
587
 
 
588
  if (val == adj->lower)
 
589
    s = "stock_volume-mute";
 
590
  else if (val > adj->lower && val <= adj->lower + step)
 
591
    s = "stock_volume-0";
 
592
  else if (val > adj->lower + step && val <= adj->lower + step * 2)
 
593
    s = "stock_volume-min";
 
594
  else if (val > adj->lower + step * 2 && val <= adj->lower + step * 3)
 
595
    s = "stock_volume-med";
 
596
  else
 
597
    s = "stock_volume-max";
 
598
 
 
599
  /* update image */
 
600
  gtk_icon_size_lookup (button->size, &w, &h);
 
601
  buf = gtk_icon_theme_load_icon (button->theme, s, w, 0, NULL);
 
602
  gtk_image_set_from_pixbuf (GTK_IMAGE (button->image), buf);
 
603
#endif
 
604
 
 
605
  /* signal */
 
606
  g_signal_emit (button, signals[SIGNAL_VALUE_CHANGED], 0);
 
607
}