]> Pileus Git - ~andy/gtk/blob - gtk/gtkscalebutton.c
Add some missing symbols.
[~andy/gtk] / gtk / gtkscalebutton.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2005 Ronald S. Bultje
3  * Copyright (C) 2006, 2007 Christian Persch
4  * Copyright (C) 2006 Jan Arne Petersen
5  * Copyright (C) 2005-2007 Red Hat, Inc.
6  *
7  * Authors:
8  * - Ronald S. Bultje <rbultje@ronald.bitfreak.net>
9  * - Bastien Nocera <bnocera@redhat.com>
10  * - Jan Arne Petersen <jpetersen@jpetersen.org>
11  * - Christian Persch <chpe@svn.gnome.org>
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the
25  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26  * Boston, MA 02111-1307, USA.
27  */
28
29 /*
30  * Modified by the GTK+ Team and others 2007.  See the AUTHORS
31  * file for a list of people on the GTK+ Team.  See the ChangeLog
32  * files for a list of changes.  These files are distributed with
33  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
34  */
35
36 #include <config.h>
37
38 #define _GNU_SOURCE
39 #include <math.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include "gtkmain.h"
44 #include "gtkintl.h"
45 #include "gtkrange.h"
46 #include "gtkbindings.h"
47 #include "gtkscale.h"
48 #include "gtkvscale.h"
49 #include "gtkframe.h"
50 #include "gtkvbox.h"
51 #include "gtkwindow.h"
52 #include "gtkmarshalers.h"
53 #include "gtkstock.h"
54
55 #include <gdk-pixbuf/gdk-pixbuf.h>
56 #include <gdk/gdkkeysyms.h>
57 #include "gtkscalebutton.h"
58
59 #include "gtkalias.h"
60
61 #define SCALE_SIZE 100
62 #define CLICK_TIMEOUT 250
63
64 enum {
65   VALUE_CHANGED,
66   POPUP,
67   POPDOWN,
68   LAST_SIGNAL
69 };
70
71 enum {
72   PROP_0,
73   PROP_VALUE,
74   PROP_SIZE,
75   PROP_ADJUSTMENT
76 };
77
78 struct _GtkScaleButtonPrivate {
79   GtkWidget *dock, *scale, *image;
80   GtkIconSize size;
81   guint click_id;
82   gdouble direction;
83   guint32 pop_time;
84   gchar **icon_list;
85   gint click_timeout;
86   guint timeout : 1;
87 } _GtkScaleButtonPrivate;
88
89 static void     gtk_scale_button_class_init     (GtkScaleButtonClass *klass);
90 static void     gtk_scale_button_init           (GtkScaleButton      *button);
91 static void     gtk_scale_button_dispose        (GObject             *object);
92 static void     gtk_scale_button_set_property   (GObject             *object,
93                                                  guint                prop_id,
94                                                  const GValue        *value,
95                                                  GParamSpec          *pspec);
96 static void     gtk_scale_button_get_property   (GObject             *object,
97                                                  guint                prop_id,
98                                                  GValue              *value,
99                                                  GParamSpec          *pspec);
100 static gboolean gtk_scale_button_scroll         (GtkWidget           *widget,
101                                                  GdkEventScroll      *event);
102 static void gtk_scale_button_screen_changed     (GtkWidget           *widget,
103                                                  GdkScreen           *previous_screen);
104 static gboolean gtk_scale_button_press          (GtkWidget           *widget,
105                                                  GdkEventButton      *event);
106 static gboolean gtk_scale_button_key_release    (GtkWidget           *widget,
107                                                  GdkEventKey         *event);
108 static void gtk_scale_button_popup_from_bindings (GtkWidget *widget);
109 static void gtk_scale_button_popdown_from_bindings (GtkWidget *widget);
110 static gboolean cb_dock_button_press            (GtkWidget           *widget,
111                                                  GdkEventButton      *event,
112                                                  gpointer             user_data);
113 static gboolean cb_dock_key_release             (GtkWidget           *widget,
114                                                  GdkEventKey         *event,
115                                                  gpointer             user_data);
116 static gboolean cb_button_press                 (GtkWidget           *widget,
117                                                  GdkEventButton      *event,
118                                                  gpointer             user_data);
119 static gboolean cb_button_release               (GtkWidget           *widget,
120                                                  GdkEventButton      *event,
121                                                  gpointer             user_data);
122 static void cb_dock_grab_notify                 (GtkWidget           *widget,
123                                                  gboolean             was_grabbed,
124                                                  gpointer             user_data);
125 static gboolean cb_dock_grab_broken_event       (GtkWidget           *widget,
126                                                  gboolean             was_grabbed,
127                                                  gpointer             user_data);
128 static void cb_scale_grab_notify                (GtkWidget           *widget,
129                                                  gboolean             was_grabbed,
130                                                  gpointer             user_data);
131 static void gtk_scale_button_update_icon        (GtkScaleButton      *button);
132 static void gtk_scale_button_scale_value_changed(GtkRange            *range);
133
134 /* see below for scale definitions */
135 static GtkWidget *gtk_scale_button_scale_new    (GtkScaleButton      *button,
136                                                  gdouble              min,
137                                                  gdouble              max,
138                                                  gdouble              step);
139
140 static GtkButtonClass *parent_class = NULL;
141 static guint signals[LAST_SIGNAL] = { 0 };
142
143 G_DEFINE_TYPE (GtkScaleButton, gtk_scale_button, GTK_TYPE_BUTTON)
144
145 static void
146 gtk_scale_button_class_init (GtkScaleButtonClass *klass)
147 {
148   GtkBindingSet *binding_set;
149   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
150   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
151
152   parent_class = g_type_class_peek_parent (klass);
153
154   gobject_class->dispose = gtk_scale_button_dispose;
155   gobject_class->set_property = gtk_scale_button_set_property;
156   gobject_class->get_property = gtk_scale_button_get_property;
157   widget_class->button_press_event = gtk_scale_button_press;
158   widget_class->key_release_event = gtk_scale_button_key_release;
159   widget_class->scroll_event = gtk_scale_button_scroll;
160   widget_class->screen_changed = gtk_scale_button_screen_changed;
161
162   g_object_class_install_property (gobject_class,
163                                    PROP_VALUE,
164                                    g_param_spec_double ("value",
165                                                         P_("Value"),
166                                                         P_("The value of the scale"),
167                                                         -G_MAXDOUBLE,
168                                                         G_MAXDOUBLE,
169                                                         0,
170                                                         G_PARAM_READWRITE));
171
172   g_object_class_install_property (gobject_class,
173                                    PROP_SIZE,
174                                    g_param_spec_enum ("size",
175                                                       P_("Icon size"),
176                                                       P_("The icon size"),
177                                                       GTK_TYPE_ICON_SIZE,
178                                                       GTK_ICON_SIZE_SMALL_TOOLBAR,
179                                                       G_PARAM_READWRITE));
180
181   g_object_class_install_property (gobject_class,
182                                    PROP_ADJUSTMENT,
183                                    g_param_spec_object ("adjustment",
184                                                         P_("Adjustment"),
185                                                         P_("The GtkAdjustment that contains the current value of this scale button object"),
186                                                         GTK_TYPE_ADJUSTMENT,
187                                                         G_PARAM_READWRITE));
188
189   /**
190    * GtkScaleButton::value-changed:
191    * @button: the object that received the signal
192    *
193    * Emitted when the value field has changed.
194    *
195    * Since: 2.12
196    *
197    */
198   signals[VALUE_CHANGED] =
199     g_signal_new (I_("value-changed"),
200                   G_TYPE_FROM_CLASS (klass),
201                   G_SIGNAL_RUN_LAST,
202                   G_STRUCT_OFFSET (GtkScaleButtonClass, value_changed),
203                   NULL, NULL,
204                   _gtk_marshal_VOID__DOUBLE,
205                   G_TYPE_NONE, 1, G_TYPE_DOUBLE);
206   signals[POPUP] =
207     _gtk_binding_signal_new (I_("popup"),
208                              G_OBJECT_CLASS_TYPE (klass),
209                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
210                              G_CALLBACK (gtk_scale_button_popup_from_bindings),
211                              NULL, NULL,
212                              g_cclosure_marshal_VOID__VOID,
213                              G_TYPE_NONE, 0);
214   signals[POPDOWN] =
215     _gtk_binding_signal_new (I_("popdown"),
216                              G_OBJECT_CLASS_TYPE (klass),
217                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
218                              G_CALLBACK (gtk_scale_button_popdown_from_bindings),
219                              NULL, NULL,
220                              g_cclosure_marshal_VOID__VOID,
221                              G_TYPE_NONE, 0);
222
223   /* Key bindings */
224   binding_set = gtk_binding_set_by_class (widget_class);
225
226   gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
227                                 "popup", 0);
228   gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
229                                 "popup", 0);
230   gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
231                                 "popup", 0);
232   gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
233                                 "popup", 0);
234   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
235                                 "popdown", 0);
236 }
237
238 static void
239 gtk_scale_button_init (GtkScaleButton *button)
240 {
241   GtkWidget *frame, *box;
242   GtkScaleButtonPrivate *priv;
243
244   button->priv = g_new0 (GtkScaleButtonPrivate, 1);
245   priv = button->priv;
246
247   priv->timeout = FALSE;
248   priv->click_id = 0;
249   priv->click_timeout = CLICK_TIMEOUT;
250
251   gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
252   gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
253
254   /* image */
255   priv->image = gtk_image_new ();
256   gtk_container_add (GTK_CONTAINER (button), priv->image);
257   gtk_widget_show_all (priv->image);
258
259   /* window */
260   priv->dock = gtk_window_new (GTK_WINDOW_POPUP);
261   g_signal_connect (priv->dock, "button-press-event",
262                     G_CALLBACK (cb_dock_button_press), button);
263   g_signal_connect (priv->dock, "key-release-event",
264                     G_CALLBACK (cb_dock_key_release), button);
265   g_signal_connect (priv->dock, "grab-notify",
266                     G_CALLBACK (cb_dock_grab_notify), button);
267   g_signal_connect (priv->dock, "grab-broken-event",
268                     G_CALLBACK (cb_dock_grab_broken_event), button);
269   gtk_window_set_decorated (GTK_WINDOW (priv->dock), FALSE);
270
271   /* frame */
272   frame = gtk_frame_new (NULL);
273   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
274   gtk_container_add (GTK_CONTAINER (priv->dock), frame);
275   box = gtk_vbox_new (FALSE, 0);
276   gtk_container_add (GTK_CONTAINER (frame), box);
277
278   /* + */
279   button->plus_button = gtk_button_new_with_label ("+");
280   gtk_button_set_relief (GTK_BUTTON (button->plus_button), GTK_RELIEF_NONE);
281   g_signal_connect (button->plus_button, "button-press-event",
282                     G_CALLBACK (cb_button_press), button);
283   g_signal_connect (button->plus_button, "button-release-event",
284                     G_CALLBACK (cb_button_release), button);
285   gtk_box_pack_start (GTK_BOX (box), button->plus_button, TRUE, FALSE, 0);
286
287   /* scale */
288   priv->scale = gtk_scale_button_scale_new (button, 0., 100., 2.);
289   gtk_widget_set_size_request (priv->scale, -1, SCALE_SIZE);
290   gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE);
291   gtk_range_set_inverted (GTK_RANGE (priv->scale), TRUE);
292   gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, FALSE, 0);
293   g_signal_connect (priv->scale, "grab-notify",
294                     G_CALLBACK (cb_scale_grab_notify), button);
295
296   /* - */
297   button->minus_button = gtk_button_new_with_label ("-");
298   gtk_button_set_relief (GTK_BUTTON (button->minus_button), GTK_RELIEF_NONE);
299   g_signal_connect (button->minus_button, "button-press-event",
300                    G_CALLBACK (cb_button_press), button);
301   g_signal_connect (button->minus_button, "button-release-event",
302                     G_CALLBACK (cb_button_release), button);
303   gtk_box_pack_start (GTK_BOX (box), button->minus_button, TRUE, FALSE, 0);
304
305   /* set button text and size */
306   priv->size = GTK_ICON_SIZE_SMALL_TOOLBAR;
307   gtk_scale_button_update_icon (button);
308 }
309
310 static void
311 gtk_scale_button_set_property (GObject       *object,
312                                guint          prop_id,
313                                const GValue  *value,
314                                GParamSpec    *pspec)
315 {
316   GtkScaleButton *button;
317
318   button = GTK_SCALE_BUTTON (object);
319
320   switch (prop_id)
321     {
322     case PROP_VALUE:
323       gtk_scale_button_set_value (button, g_value_get_double (value));
324       break;
325     case PROP_SIZE:
326       {
327         GtkIconSize size;
328         size = g_value_get_enum (value);
329         if (button->priv->size != size)
330           {
331             button->priv->size = size;
332             gtk_scale_button_update_icon (button);
333           }
334       }
335       break;
336     case PROP_ADJUSTMENT:
337       gtk_scale_button_set_adjustment (button, g_value_get_object (value));
338       break;
339     default:
340       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
341       break;
342     }
343 }
344
345 static void 
346 gtk_scale_button_get_property (GObject     *object,
347                                guint        prop_id,
348                                GValue      *value,
349                                GParamSpec  *pspec)
350 {
351   GtkScaleButton *button;
352   GtkScaleButtonPrivate *priv;
353
354   button = GTK_SCALE_BUTTON (object);
355   priv = button->priv;
356
357   switch (prop_id)
358     {
359     case PROP_VALUE:
360       g_value_set_float (value, gtk_scale_button_get_value (button));
361       break;
362     case PROP_SIZE:
363       g_value_set_enum (value, priv->size);
364       break;
365     case PROP_ADJUSTMENT:
366       g_value_set_object (value, gtk_scale_button_get_adjustment (button));
367       break;
368     default:
369       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370       break;
371     }
372 }
373
374 static void
375 gtk_scale_button_dispose (GObject *object)
376 {
377   GtkScaleButton *button;
378   GtkScaleButtonPrivate *priv;
379
380   button = GTK_SCALE_BUTTON (object);
381
382   priv = button->priv;
383
384   if (priv && priv->dock)
385     {
386       gtk_widget_destroy (priv->dock);
387       priv->dock = NULL;
388     }
389
390   if (priv && priv->click_id != 0)
391     {
392       g_source_remove (priv->click_id);
393       priv->click_id = 0;
394     }
395
396   if (priv && priv->icon_list)
397     {
398       g_strfreev (priv->icon_list);
399       priv->icon_list = NULL;
400     }
401
402   g_free (priv);
403   button->priv = NULL;
404
405   G_OBJECT_CLASS (parent_class)->dispose (object);
406 }
407
408 /**
409  * gtk_scale_button_new
410  * @size: a stock icon size
411  * @lower: the minimum value of the scale (usually 0)
412  * @upper: the maximum value of the scale (usually 100)
413  * @step_increment: the stepping of value when a scroll-wheel event,
414  * or up/down arrow event occurs (usually 2)
415  * @icons: a %NULL-terminated array of icon names, or %NULL if
416  * you want to set the list later with gtk_scale_button_set_icons(). See
417  * gtk_scale_button_set_icons() for details.
418  *
419  * Creates a #GtkScaleButton, with a range between lower and upper, with
420  * a stepping of step_increment.
421  *
422  * Return value: a new #GtkScaleButton
423  *
424  * Since: 2.12
425  */
426 GtkWidget *
427 gtk_scale_button_new (GtkIconSize   size,
428                       gdouble       lower,
429                       gdouble       upper,
430                       gdouble       step_increment,
431                       const gchar **icons)
432 {
433   GtkScaleButton *button;
434   GtkScaleButtonPrivate *priv;
435
436   button = g_object_new (GTK_TYPE_SCALE_BUTTON, NULL);
437   priv = button->priv;
438
439   if (icons != NULL)
440     gtk_scale_button_set_icons (button, icons);
441
442   gtk_range_set_range (GTK_RANGE (priv->scale), lower, upper);
443   gtk_range_set_increments (GTK_RANGE (priv->scale),
444                             step_increment,
445                             10 * step_increment);
446
447   if (priv->size != size)
448     {
449       priv->size = size;
450       gtk_scale_button_update_icon (button);
451     }
452
453   return GTK_WIDGET (button);
454 }
455
456 /**
457  * gtk_scale_button_get_value
458  * @button: a #GtkScaleButton
459  *
460  * Gets the current value of the scale button.
461  *
462  * Return value: current value of the scale button
463  *
464  * Since: 2.12
465  */
466 gdouble
467 gtk_scale_button_get_value (GtkScaleButton * button)
468 {
469   GtkScaleButtonPrivate *priv;
470
471   g_return_val_if_fail (GTK_IS_SCALE_BUTTON (button), 0);
472
473   priv = button->priv;
474
475   return gtk_range_get_value (GTK_RANGE (priv->scale));
476 }
477
478 /**
479  * gtk_scale_button_set_value
480  * @button: a #GtkScaleButton
481  * @value: new value of the scale button
482  *
483  * Sets the current value of the scale; if the value is outside the minimum or 
484  * maximum range values, it will be clamped to fit inside them. The scale button 
485  * emits the "value_changed" signal if the value changes.
486  *
487  * Since: 2.12
488  */
489 void
490 gtk_scale_button_set_value (GtkScaleButton *button,
491                             gdouble         value)
492 {
493   GtkScaleButtonPrivate *priv;
494
495   g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
496
497   priv = button->priv;
498
499   gtk_range_set_value (GTK_RANGE (priv->scale), value);
500 }
501
502 /**
503  * gtk_scale_button_set_icons
504  * @button: a #GtkScaleButton
505  * @icons: a %NULL-terminated array of icon names
506  *
507  * Sets the icons to be used by the scale button. The first item in the array 
508  * will be used in the button when the current value is the lowest value, the 
509  * second item for the highest value. All the subsequent icons will be used for 
510  * all the other values, spread evenly over the range of values.
511  *
512  * If there's only one icon in the @icons array, it will be used for all the
513  * values. If only two icons are in the @icons array, the first one will be
514  * used for the bottom 50% of the scale, and the second one for the top 50%.
515  * So it is recommended to use at least 3 icons so that the #GtkScaleButton
516  * reflects the current value of the scale better for the users.
517  *
518  * Since 2.12
519  */
520 void
521 gtk_scale_button_set_icons (GtkScaleButton  *button,
522                             const gchar    **icons)
523 {
524   GtkScaleButtonPrivate *priv;
525
526   g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
527   g_return_if_fail (icons != NULL);
528   g_return_if_fail (icons[0] != NULL);
529
530   priv = button->priv;
531
532   g_strfreev (priv->icon_list);
533   priv->icon_list = g_strdupv ((gchar **) icons);
534   gtk_scale_button_update_icon (button);
535 }
536
537 /**
538  * gtk_scale_button_get_adjustment
539  * @button: a #GtkScaleButton
540  *
541  * Gets the #GtkAdjustment associated with the #GtkScaleButton's scale.
542  * See gtk_range_get_adjustment() for details.
543  *
544  * Since: 2.12
545  */
546 GtkAdjustment*
547 gtk_scale_button_get_adjustment (GtkScaleButton *button)
548 {
549   GtkRange *range;
550
551   g_return_val_if_fail (GTK_IS_SCALE_BUTTON (button), NULL);
552
553   range = GTK_RANGE (button->priv->scale);
554   g_return_val_if_fail (range != NULL, NULL);
555
556   return gtk_range_get_adjustment (range);
557 }
558
559 /**
560  * gtk_scale_button_set_adjustment
561  * @button: a #GtkScaleButton
562  * @adjustment: a #GtkAdjustment
563  *
564  * Sets the #GtkAdjustment to be used as a model for the #GtkScaleButton's scale.
565  * See gtk_range_set_adjustment() for details.
566  *
567  * Since: 2.12
568  */
569 void
570 gtk_scale_button_set_adjustment (GtkScaleButton *button,
571                                  GtkAdjustment  *adjustment)
572 {
573   g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
574   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
575
576   gtk_range_set_adjustment (GTK_RANGE (button->priv->scale),
577                             adjustment);
578 }
579
580 /*
581  * button callbacks.
582  */
583
584 static gboolean
585 gtk_scale_button_scroll (GtkWidget      *widget,
586                          GdkEventScroll *event)
587 {
588   GtkScaleButton *button;
589   GtkScaleButtonPrivate *priv;
590   GtkAdjustment *adj;
591   gdouble d;
592
593   button = GTK_SCALE_BUTTON (widget);
594   priv = button->priv;
595   adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
596
597   if (event->type != GDK_SCROLL)
598     return FALSE;
599
600   d = gtk_scale_button_get_value (button);
601   if (event->direction == GDK_SCROLL_UP)
602     {
603       d += adj->step_increment;
604       if (d > adj->upper)
605         d = adj->upper;
606     }
607   else
608     {
609       d -= adj->step_increment;
610       if (d < adj->lower)
611         d = adj->lower;
612     }
613   gtk_scale_button_set_value (button, d);
614
615   return TRUE;
616 }
617
618 static void
619 gtk_scale_button_screen_changed (GtkWidget *widget,
620                                  GdkScreen *previous_screen)
621 {
622   GtkScaleButton *button = (GtkScaleButton *) widget;
623   GtkScaleButtonPrivate *priv;
624   GdkScreen *screen;
625   GValue value = { 0, };
626
627   if (gtk_widget_has_screen (widget) == FALSE)
628     return;
629
630   priv = button->priv;
631
632   screen = gtk_widget_get_screen (widget);
633   g_value_init (&value, G_TYPE_INT);
634   if (gdk_screen_get_setting (screen,
635                               "gtk-double-click-time",
636                               &value) == FALSE)
637     {
638       priv->click_timeout = CLICK_TIMEOUT;
639       return;
640     }
641
642   priv->click_timeout = g_value_get_int (&value);
643 }
644
645 static gboolean
646 gtk_scale_popup (GtkWidget *widget,
647                  GdkEvent  *event,
648                  guint32    time)
649 {
650   GtkScaleButton *button;
651   GtkScaleButtonPrivate *priv;
652   GtkAdjustment *adj;
653   gint x, y, m, dx, dy, sx, sy, ystartoff;
654   gdouble v;
655   GdkDisplay *display;
656   GdkScreen *screen;
657
658   button = GTK_SCALE_BUTTON (widget);
659   priv = button->priv;
660   adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
661
662   display = gtk_widget_get_display (widget);
663   screen = gtk_widget_get_screen (widget);
664
665   /* position roughly */
666   gtk_window_set_screen (GTK_WINDOW (priv->dock), screen);
667
668   gdk_window_get_origin (widget->window, &x, &y);
669   x += widget->allocation.x;
670   y += widget->allocation.y;
671   gtk_window_move (GTK_WINDOW (priv->dock), x, y - (SCALE_SIZE / 2));
672   gtk_widget_show_all (priv->dock);
673   gdk_window_get_origin (priv->dock->window, &dx, &dy);
674   dy += priv->dock->allocation.y;
675   gdk_window_get_origin (priv->scale->window, &sx, &sy);
676   sy += priv->scale->allocation.y;
677   ystartoff = sy - dy;
678   priv->timeout = TRUE;
679
680   /* position (needs widget to be shown already) */
681   v = gtk_scale_button_get_value (button) / (adj->upper - adj->lower);
682   x += (widget->allocation.width - priv->dock->allocation.width) / 2;
683   y -= ystartoff;
684   y -= GTK_RANGE (priv->scale)->min_slider_size / 2;
685   m = priv->scale->allocation.height -
686       GTK_RANGE (priv->scale)->min_slider_size;
687   y -= m * (1.0 - v);
688
689   /* Make sure the dock stays inside the monitor */
690   if (event->type == GDK_BUTTON_PRESS)
691     {
692       int monitor;
693       GdkEventButton *button_event = (GdkEventButton *) event;
694       GdkRectangle rect;
695       GtkWidget *d;
696
697       d = GTK_WIDGET (priv->dock);
698       monitor = gdk_screen_get_monitor_at_point (screen,
699                                                  button_event->x_root,
700                                                  button_event->y_root);
701       gdk_screen_get_monitor_geometry (screen, monitor, &rect);
702
703       y += button_event->y;
704       if (y < rect.y)
705         y = rect.y;
706       else if (y + d->allocation.height > rect.height + rect.y)
707         y = rect.y + rect.height - d->allocation.height;
708
709       if (x < rect.x)
710         x = rect.x;
711       else if (x + d->allocation.width > rect.width + rect.x)
712         x = rect.x + rect.width - d->allocation.width;
713     }
714
715   gtk_window_move (GTK_WINDOW (priv->dock), x, y);
716
717   if (event->type == GDK_BUTTON_PRESS)
718     GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, (GdkEventButton *) event);
719
720   /* grab focus */
721   gtk_grab_add (priv->dock);
722
723   if (gdk_pointer_grab (priv->dock->window, TRUE,
724                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
725                         GDK_POINTER_MOTION_MASK, NULL, NULL, time)
726       != GDK_GRAB_SUCCESS)
727     {
728       gtk_grab_remove (priv->dock);
729       gtk_widget_hide (priv->dock);
730       return FALSE;
731     }
732
733   if (gdk_keyboard_grab (priv->dock->window, TRUE, time) != GDK_GRAB_SUCCESS)
734     {
735       gdk_display_pointer_ungrab (display, time);
736       gtk_grab_remove (priv->dock);
737       gtk_widget_hide (priv->dock);
738       return FALSE;
739     }
740
741   gtk_widget_grab_focus (priv->dock);
742
743   if (event->type == GDK_BUTTON_PRESS)
744     {
745       GdkEventButton *e;
746       GdkEventButton *button_event = (GdkEventButton *) event;
747
748       /* forward event to the slider */
749       e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
750       e->window = priv->scale->window;
751
752       /* position: the X position isn't relevant, halfway will work just fine.
753        * The vertical position should be *exactly* in the middle of the slider
754        * of the scale; if we don't do that correctly, it'll move from its current
755        * position, which means a position change on-click, which is bad. */
756       e->x = priv->scale->allocation.width / 2;
757       m = priv->scale->allocation.height -
758         GTK_RANGE (priv->scale)->min_slider_size;
759       e->y = ((1.0 - v) * m) + GTK_RANGE (priv->scale)->min_slider_size / 2;
760       gtk_widget_event (priv->scale, (GdkEvent *) e);
761       e->window = button_event->window;
762       gdk_event_free ((GdkEvent *) e);
763     }
764
765   gtk_widget_grab_focus (priv->scale);
766
767   priv->pop_time = time;
768
769   return TRUE;
770 }
771
772 static gboolean
773 gtk_scale_button_press (GtkWidget      *widget,
774                         GdkEventButton *event)
775 {
776   return gtk_scale_popup (widget, (GdkEvent *) event, event->time);
777 }
778
779 static void
780 gtk_scale_button_popup_from_bindings (GtkWidget *widget)
781 {
782   GdkEvent *ev;
783
784   ev = gdk_event_new (GDK_KEY_RELEASE);
785   gtk_scale_popup (widget, ev, GDK_CURRENT_TIME);
786   gdk_event_free (ev);
787 }
788
789 static gboolean
790 gtk_scale_button_key_release (GtkWidget   *widget,
791                               GdkEventKey *event)
792 {
793   return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
794 }
795
796 /* This is called when the grab is broken for
797  * either the dock, or the scale itself */
798 static void
799 gtk_scale_button_grab_notify (GtkScaleButton *button,
800                               gboolean        was_grabbed)
801 {
802   GdkDisplay *display;
803   GtkScaleButtonPrivate *priv;
804
805   if (was_grabbed != FALSE)
806     return;
807
808   priv = button->priv;
809
810   if (gtk_widget_is_ancestor (gtk_grab_get_current (), priv->dock))
811     return;
812
813   display = gtk_widget_get_display (priv->dock);
814   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
815   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
816   gtk_grab_remove (priv->dock);
817
818   /* hide again */
819   gtk_widget_hide (priv->dock);
820   priv->timeout = FALSE;
821 }
822
823 /*
824  * +/- button callbacks.
825  */
826
827 static gboolean
828 cb_button_timeout (gpointer user_data)
829 {
830   GtkScaleButton *button;
831   GtkScaleButtonPrivate *priv;
832   GtkAdjustment *adj;
833   gdouble val;
834   gboolean res = TRUE;
835
836   button = GTK_SCALE_BUTTON (user_data);
837   priv = button->priv;
838
839   if (priv->click_id == 0)
840     return FALSE;
841
842   adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
843
844   val = gtk_scale_button_get_value (button);
845   val += priv->direction;
846   if (val <= adj->lower)
847     {
848       res = FALSE;
849       val = adj->lower;
850     }
851   else if (val > adj->upper)
852     {
853       res = FALSE;
854       val = adj->upper;
855     }
856   gtk_scale_button_set_value (button, val);
857
858   if (!res)
859     {
860       g_source_remove (priv->click_id);
861       priv->click_id = 0;
862     }
863
864   return res;
865 }
866
867 static gboolean
868 cb_button_press (GtkWidget      *widget,
869                  GdkEventButton *event,
870                  gpointer        user_data)
871 {
872   GtkScaleButton *button;
873   GtkScaleButtonPrivate *priv;
874   GtkAdjustment *adj;
875
876   button = GTK_SCALE_BUTTON (user_data);
877   priv = button->priv;
878   adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
879
880   if (priv->click_id != 0)
881     g_source_remove (priv->click_id);
882
883   if (widget == button->plus_button)
884     priv->direction = fabs (adj->page_increment);
885   else
886     priv->direction = - fabs (adj->page_increment);
887
888   priv->click_id = g_timeout_add (priv->click_timeout,
889                                   (GSourceFunc) cb_button_timeout, button);
890   cb_button_timeout (button);
891
892   return TRUE;
893 }
894
895 static gboolean
896 cb_button_release (GtkWidget      *widget,
897                    GdkEventButton *event,
898                    gpointer        user_data)
899 {
900   GtkScaleButton *button;
901   GtkScaleButtonPrivate *priv;
902
903   button = GTK_SCALE_BUTTON (user_data);
904   priv = button->priv;
905
906   if (priv->click_id != 0)
907     {
908       g_source_remove (priv->click_id);
909       priv->click_id = 0;
910     }
911
912   return TRUE;
913 }
914
915 static void
916 cb_dock_grab_notify (GtkWidget *widget,
917                      gboolean   was_grabbed,
918                      gpointer   user_data)
919 {
920   GtkScaleButton *button = (GtkScaleButton *) user_data;
921
922   gtk_scale_button_grab_notify (button, was_grabbed);
923 }
924
925 static gboolean
926 cb_dock_grab_broken_event (GtkWidget *widget,
927                            gboolean   was_grabbed,
928                            gpointer   user_data)
929 {
930   GtkScaleButton *button = (GtkScaleButton *) user_data;
931
932   gtk_scale_button_grab_notify (button, FALSE);
933
934   return FALSE;
935 }
936
937 /*
938  * Scale callbacks.
939  */
940
941 static void
942 gtk_scale_button_release_grab (GtkScaleButton *button,
943                                GdkEventButton *event)
944 {
945   GdkEventButton *e;
946   GdkDisplay *display;
947   GtkScaleButtonPrivate *priv;
948
949   priv = button->priv;
950
951   /* ungrab focus */
952   display = gtk_widget_get_display (GTK_WIDGET (button));
953   gdk_display_keyboard_ungrab (display, event->time);
954   gdk_display_pointer_ungrab (display, event->time);
955   gtk_grab_remove (priv->dock);
956
957   /* hide again */
958   gtk_widget_hide (priv->dock);
959   priv->timeout = FALSE;
960
961   e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
962   e->window = GTK_WIDGET (button)->window;
963   e->type = GDK_BUTTON_RELEASE;
964   gtk_widget_event (GTK_WIDGET (button), (GdkEvent *) e);
965   e->window = event->window;
966   gdk_event_free ((GdkEvent *) e);
967 }
968
969 static gboolean
970 cb_dock_button_press (GtkWidget      *widget,
971                       GdkEventButton *event,
972                       gpointer        user_data)
973 {
974   GtkScaleButton *button = GTK_SCALE_BUTTON (user_data);
975
976   if (event->type == GDK_BUTTON_PRESS)
977     {
978       gtk_scale_button_release_grab (button, event);
979       return TRUE;
980     }
981
982   return FALSE;
983 }
984
985 static void
986 gtk_scale_button_popdown_from_bindings (GtkWidget *widget)
987 {
988   GtkScaleButton *button;
989   GtkScaleButtonPrivate *priv;
990   GdkDisplay *display;
991
992   button = GTK_SCALE_BUTTON (widget);
993   priv = button->priv;
994
995   /* ungrab focus */
996   display = gtk_widget_get_display (widget);
997   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
998   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
999   gtk_grab_remove (priv->dock);
1000
1001   /* hide again */
1002   gtk_widget_hide (priv->dock);
1003   priv->timeout = FALSE;
1004 }
1005
1006 static gboolean
1007 cb_dock_key_release (GtkWidget   *widget,
1008                      GdkEventKey *event,
1009                      gpointer     user_data)
1010 {
1011   if (event->keyval == GDK_Escape)
1012     {
1013       gtk_scale_button_popdown_from_bindings (GTK_WIDGET (user_data));
1014       return TRUE;
1015     }
1016
1017   if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event))
1018     {
1019       /* The popup hasn't managed the event, pass onto the button */
1020       gtk_bindings_activate_event (GTK_OBJECT (user_data), event);
1021     }
1022
1023   return TRUE;
1024 }
1025
1026 static void
1027 cb_scale_grab_notify (GtkWidget *widget,
1028                       gboolean   was_grabbed,
1029                       gpointer   user_data)
1030 {
1031   GtkScaleButton *button = (GtkScaleButton *) user_data;
1032
1033   gtk_scale_button_grab_notify (button, was_grabbed);
1034 }
1035
1036 /*
1037  * Scale stuff.
1038  */
1039
1040 #define GTK_TYPE_SCALE_BUTTON_SCALE \
1041   (gtk_scale_button_scale_get_type ())
1042 #define GTK_SCALE_BUTTON_SCALE(obj) \
1043   (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SCALE_BUTTON_SCALE, \
1044                                GtkScaleButtonScale))
1045
1046 typedef struct _GtkScaleButtonScaleClass {
1047   GtkVScaleClass parent_class;
1048 } GtkScaleButtonScaleClass;
1049
1050 typedef struct _GtkScaleButtonScale {
1051   GtkVScale parent;
1052   GtkScaleButton *button;
1053 } GtkScaleButtonScale;
1054
1055 static GType    gtk_scale_button_scale_get_type   (void);
1056 static void     gtk_scale_button_scale_class_init (GtkScaleButtonScaleClass *klass);
1057 static gboolean gtk_scale_button_scale_press      (GtkWidget                *widget,
1058                                                    GdkEventButton           *event);
1059 static gboolean gtk_scale_button_scale_release    (GtkWidget                *widget,
1060                                                    GdkEventButton           *event);
1061
1062 static GtkVScaleClass *scale_parent_class = NULL;
1063
1064 G_DEFINE_TYPE (GtkScaleButtonScale, gtk_scale_button_scale, GTK_TYPE_VSCALE)
1065
1066 static void
1067 gtk_scale_button_scale_class_init (GtkScaleButtonScaleClass *klass)
1068 {
1069   GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
1070   GtkRangeClass *gtkrange_class = GTK_RANGE_CLASS (klass);
1071
1072   scale_parent_class = g_type_class_peek_parent (klass);
1073
1074   gtkwidget_class->button_press_event = gtk_scale_button_scale_press;
1075   gtkwidget_class->button_release_event = gtk_scale_button_scale_release;
1076   gtkrange_class->value_changed = gtk_scale_button_scale_value_changed;
1077 }
1078
1079 static void
1080 gtk_scale_button_scale_init (GtkScaleButtonScale *scale)
1081 {
1082 }
1083
1084 static GtkWidget *
1085 gtk_scale_button_scale_new (GtkScaleButton *button,
1086                             gdouble         min,
1087                             gdouble         max,
1088                             gdouble         step)
1089 {
1090   GtkScaleButtonScale *scale;
1091   GtkScaleButtonPrivate *priv;
1092   GtkObject *adj;
1093
1094   priv = button->priv;
1095   scale = g_object_new (GTK_TYPE_SCALE_BUTTON_SCALE, NULL);
1096   adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1097   gtk_range_set_adjustment (GTK_RANGE (scale), GTK_ADJUSTMENT (adj));
1098   scale->button = button;
1099
1100   return GTK_WIDGET (scale);
1101 }
1102
1103 static gboolean
1104 gtk_scale_button_scale_press (GtkWidget      *widget,
1105                               GdkEventButton *event)
1106 {
1107   GtkScaleButtonScale *scale;
1108   GtkScaleButtonPrivate *priv;
1109
1110   scale = GTK_SCALE_BUTTON_SCALE (widget);
1111   priv = scale->button->priv;
1112
1113   /* the scale will grab input; if we have input grabbed, all goes
1114    * horribly wrong, so let's not do that. */
1115   gtk_grab_remove (priv->dock);
1116
1117   return GTK_WIDGET_CLASS (scale_parent_class)->button_press_event (widget, event);
1118 }
1119
1120 static gboolean
1121 gtk_scale_button_scale_release (GtkWidget      *widget,
1122                                 GdkEventButton *event)
1123 {
1124   GtkScaleButtonScale *scale;
1125   GtkScaleButtonPrivate *priv;
1126   gboolean res;
1127
1128   scale = GTK_SCALE_BUTTON_SCALE (widget);
1129   priv = scale->button->priv;
1130
1131   if (priv->timeout)
1132     {
1133       /* if we did a quick click, leave the window open; else, hide it */
1134       if (event->time > priv->pop_time + priv->click_timeout)
1135         {
1136           gtk_scale_button_release_grab (scale->button, event);
1137           GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
1138           return TRUE;
1139         }
1140       priv->timeout = FALSE;
1141     }
1142
1143   res = GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
1144
1145   /* the scale will release input; right after that, we *have to* grab
1146    * it back so we can catch out-of-scale clicks and hide the popup,
1147    * so I basically want a g_signal_connect_after_always(), but I can't
1148    * find that, so we do this complex 'first-call-parent-then-do-actual-
1149    * action' thingy... */
1150   gtk_grab_add (priv->dock);
1151
1152   return res;
1153 }
1154
1155 static void
1156 gtk_scale_button_update_icon (GtkScaleButton *button)
1157 {
1158   GtkScaleButtonPrivate *priv;
1159   GtkRange *range;
1160   GtkAdjustment *adj;
1161   gdouble value;
1162   const gchar *name;
1163   guint num_icons;
1164
1165   priv = button->priv;
1166
1167   if (priv->icon_list == NULL || priv->icon_list[0] == NULL)
1168     {
1169       gtk_image_set_from_stock (GTK_IMAGE (priv->image),
1170                                 GTK_STOCK_MISSING_IMAGE,
1171                                 priv->size);
1172       return;
1173     }
1174
1175   for (num_icons = 0; priv->icon_list[num_icons] != NULL; num_icons++) {}
1176
1177   /* The 1-icon special case */
1178   if (num_icons == 1)
1179     {
1180       gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1181                                     priv->icon_list[0],
1182                                     priv->size);
1183       return;
1184     }
1185
1186   range = GTK_RANGE (priv->scale);
1187   adj = gtk_range_get_adjustment (range);
1188   value = gtk_scale_button_get_value (button);
1189
1190   /* The 2-icons special case */
1191   if (num_icons == 2)
1192     {
1193       gdouble limit;
1194       limit = (adj->upper - adj->lower) / 2 + adj->lower;
1195       if (value < limit)
1196         name = priv->icon_list[0];
1197       else
1198         name = priv->icon_list[1];
1199
1200       gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1201                                     name,
1202                                     priv->size);
1203       return;
1204     }
1205
1206   /* With 3 or more icons */
1207   if (value == adj->lower)
1208     {
1209       name = priv->icon_list[0];
1210     }
1211   else if (value == adj->upper)
1212     {
1213       name = priv->icon_list[1];
1214     }
1215   else
1216     {
1217       gdouble step;
1218       guint i;
1219
1220       step = (adj->upper - adj->lower) / (num_icons - 2);
1221       i = (guint) (value - adj->lower) / step + 2;
1222       g_assert (i < num_icons);
1223       name = priv->icon_list[i];
1224     }
1225
1226   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1227                                 name,
1228                                 priv->size);
1229 }
1230
1231 static void
1232 gtk_scale_button_scale_value_changed (GtkRange *range)
1233 {
1234   GtkScaleButtonScale *scale = GTK_SCALE_BUTTON_SCALE (range);
1235   GtkScaleButton *button = scale->button;
1236   gdouble value;
1237
1238   value = gtk_range_get_value (range);
1239
1240   gtk_scale_button_update_icon (button);
1241
1242   g_signal_emit (button, signals[VALUE_CHANGED], 0, value);
1243   g_object_notify (G_OBJECT (button), "value");
1244 }
1245  
1246
1247 #define __GTK_SCALE_BUTTON_C__
1248 #include "gtkaliasdef.c"