~blackskad/gnomeradio/dev-vol-button

« back to all changes in this revision

Viewing changes to src/bacon-volume.c

  • Committer: Thomas Meire
  • Date: 2008-12-20 19:34:59 UTC
  • Revision ID: blackskad@awesomeness-20081220193459-x0n1bieryqc6vps4
Replaced bacon-volume-button with GtkVolumeButton

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
 
}