~ubuntu-branches/ubuntu/saucy/totem/saucy-proposed

« back to all changes in this revision

Viewing changes to src/bacon-volume.c

  • Committer: Bazaar Package Importer
  • Author(s): Loic Minier, Sebastien Bacher, Loic Minier
  • Date: 2007-03-08 14:51:55 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20070308145155-cnu1r0s1z4ffcxza
Tags: 2.16.5-3
[ Sebastien Bacher ]
* debian/patches/30_dlopen_noremove_dbus_glib.dpatch:
  - fix "crash because NPPVpluginKeepLibraryInMemory is broken in gecko",
    patch from Alexander Sack (GNOME bug #415389)

[ Loic Minier ]
* Urgency medium.

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