]> Pileus Git - ~andy/gtk/blob - gtk/gtkscale.c
Bug 553765 – Add orientation API to GtkRange
[~andy/gtk] / gtk / gtkscale.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 2001 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26  */
27
28 #include "config.h"
29
30 #include <math.h>
31 #include <stdlib.h>
32
33 #include "gdk/gdkkeysyms.h"
34 #include "gtkscale.h"
35 #include "gtkmarshalers.h"
36 #include "gtkbindings.h"
37 #include "gtkprivate.h"
38 #include "gtkintl.h"
39 #include "gtkalias.h"
40
41
42 #define MAX_DIGITS      (64)    /* don't change this,
43                                  * a) you don't need to and
44                                  * b) you might cause buffer owerflows in
45                                  *    unrelated code portions otherwise
46                                  */
47
48 #define GTK_SCALE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCALE, GtkScalePrivate))
49
50 typedef struct _GtkScalePrivate GtkScalePrivate;
51
52 struct _GtkScalePrivate
53 {
54   PangoLayout *layout;
55 };
56
57 enum {
58   PROP_0,
59   PROP_DIGITS,
60   PROP_DRAW_VALUE,
61   PROP_VALUE_POS
62 };
63
64 enum {
65   FORMAT_VALUE,
66   LAST_SIGNAL
67 };
68
69 static guint signals[LAST_SIGNAL];
70
71 static void     gtk_scale_set_property            (GObject        *object,
72                                                    guint           prop_id,
73                                                    const GValue   *value,
74                                                    GParamSpec     *pspec);
75 static void     gtk_scale_get_property            (GObject        *object,
76                                                    guint           prop_id,
77                                                    GValue         *value,
78                                                    GParamSpec     *pspec);
79 static void     gtk_scale_style_set               (GtkWidget      *widget,
80                                                    GtkStyle       *previous);
81 static void     gtk_scale_get_range_border        (GtkRange       *range,
82                                                    GtkBorder      *border);
83 static void     gtk_scale_finalize                (GObject        *object);
84 static void     gtk_scale_screen_changed          (GtkWidget      *widget,
85                                                    GdkScreen      *old_screen);
86 static gboolean gtk_scale_expose                  (GtkWidget      *widget,
87                                                    GdkEventExpose *event);
88 static void     gtk_scale_real_get_layout_offsets (GtkScale       *scale,
89                                                    gint           *x,
90                                                    gint           *y);
91
92 G_DEFINE_TYPE (GtkScale, gtk_scale, GTK_TYPE_RANGE)
93
94 static gboolean
95 single_string_accumulator (GSignalInvocationHint *ihint,
96                            GValue                *return_accu,
97                            const GValue          *handler_return,
98                            gpointer               dummy)
99 {
100   gboolean continue_emission;
101   const gchar *str;
102   
103   str = g_value_get_string (handler_return);
104   g_value_set_string (return_accu, str);
105   continue_emission = str == NULL;
106   
107   return continue_emission;
108 }
109
110
111 #define add_slider_binding(binding_set, keyval, mask, scroll)              \
112   gtk_binding_entry_add_signal (binding_set, keyval, mask,                 \
113                                 I_("move-slider"), 1, \
114                                 GTK_TYPE_SCROLL_TYPE, scroll)
115
116 static void
117 gtk_scale_class_init (GtkScaleClass *class)
118 {
119   GObjectClass   *gobject_class;
120   GtkWidgetClass *widget_class;
121   GtkRangeClass  *range_class;
122   GtkBindingSet  *binding_set;
123   
124   gobject_class = G_OBJECT_CLASS (class);
125   range_class = (GtkRangeClass*) class;
126   widget_class = (GtkWidgetClass*) class;
127   
128   gobject_class->set_property = gtk_scale_set_property;
129   gobject_class->get_property = gtk_scale_get_property;
130   gobject_class->finalize = gtk_scale_finalize;
131
132   widget_class->style_set = gtk_scale_style_set;
133   widget_class->screen_changed = gtk_scale_screen_changed;
134   widget_class->expose_event = gtk_scale_expose;
135
136   range_class->slider_detail = "Xscale";
137   range_class->get_range_border = gtk_scale_get_range_border;
138
139   class->get_layout_offsets = gtk_scale_real_get_layout_offsets;
140
141   signals[FORMAT_VALUE] =
142     g_signal_new (I_("format-value"),
143                   G_TYPE_FROM_CLASS (gobject_class),
144                   G_SIGNAL_RUN_LAST,
145                   G_STRUCT_OFFSET (GtkScaleClass, format_value),
146                   single_string_accumulator, NULL,
147                   _gtk_marshal_STRING__DOUBLE,
148                   G_TYPE_STRING, 1,
149                   G_TYPE_DOUBLE);
150
151   g_object_class_install_property (gobject_class,
152                                    PROP_DIGITS,
153                                    g_param_spec_int ("digits",
154                                                      P_("Digits"),
155                                                      P_("The number of decimal places that are displayed in the value"),
156                                                      -1,
157                                                      MAX_DIGITS,
158                                                      1,
159                                                      GTK_PARAM_READWRITE));
160   
161   g_object_class_install_property (gobject_class,
162                                    PROP_DRAW_VALUE,
163                                    g_param_spec_boolean ("draw-value",
164                                                          P_("Draw Value"),
165                                                          P_("Whether the current value is displayed as a string next to the slider"),
166                                                          TRUE,
167                                                          GTK_PARAM_READWRITE));
168   
169   g_object_class_install_property (gobject_class,
170                                    PROP_VALUE_POS,
171                                    g_param_spec_enum ("value-pos",
172                                                       P_("Value Position"),
173                                                       P_("The position in which the current value is displayed"),
174                                                       GTK_TYPE_POSITION_TYPE,
175                                                       GTK_POS_TOP,
176                                                       GTK_PARAM_READWRITE));
177
178   gtk_widget_class_install_style_property (widget_class,
179                                            g_param_spec_int ("slider-length",
180                                                              P_("Slider Length"),
181                                                              P_("Length of scale's slider"),
182                                                              0,
183                                                              G_MAXINT,
184                                                              31,
185                                                              GTK_PARAM_READABLE));
186
187   gtk_widget_class_install_style_property (widget_class,
188                                            g_param_spec_int ("value-spacing",
189                                                              P_("Value spacing"),
190                                                              P_("Space between value text and the slider/trough area"),
191                                                              0,
192                                                              G_MAXINT,
193                                                              2,
194                                                              GTK_PARAM_READABLE));
195   
196   /* All bindings (even arrow keys) are on both h/v scale, because
197    * blind users etc. don't care about scale orientation.
198    */
199   
200   binding_set = gtk_binding_set_by_class (class);
201
202   add_slider_binding (binding_set, GDK_Left, 0,
203                       GTK_SCROLL_STEP_LEFT);
204
205   add_slider_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
206                       GTK_SCROLL_PAGE_LEFT);
207
208   add_slider_binding (binding_set, GDK_KP_Left, 0,
209                       GTK_SCROLL_STEP_LEFT);
210
211   add_slider_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
212                       GTK_SCROLL_PAGE_LEFT);
213
214   add_slider_binding (binding_set, GDK_Right, 0,
215                       GTK_SCROLL_STEP_RIGHT);
216
217   add_slider_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
218                       GTK_SCROLL_PAGE_RIGHT);
219
220   add_slider_binding (binding_set, GDK_KP_Right, 0,
221                       GTK_SCROLL_STEP_RIGHT);
222
223   add_slider_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
224                       GTK_SCROLL_PAGE_RIGHT);
225
226   add_slider_binding (binding_set, GDK_Up, 0,
227                       GTK_SCROLL_STEP_UP);
228
229   add_slider_binding (binding_set, GDK_Up, GDK_CONTROL_MASK,
230                       GTK_SCROLL_PAGE_UP);
231
232   add_slider_binding (binding_set, GDK_KP_Up, 0,
233                       GTK_SCROLL_STEP_UP);
234
235   add_slider_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK,
236                       GTK_SCROLL_PAGE_UP);
237
238   add_slider_binding (binding_set, GDK_Down, 0,
239                       GTK_SCROLL_STEP_DOWN);
240
241   add_slider_binding (binding_set, GDK_Down, GDK_CONTROL_MASK,
242                       GTK_SCROLL_PAGE_DOWN);
243
244   add_slider_binding (binding_set, GDK_KP_Down, 0,
245                       GTK_SCROLL_STEP_DOWN);
246
247   add_slider_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK,
248                       GTK_SCROLL_PAGE_DOWN);
249    
250   add_slider_binding (binding_set, GDK_Page_Up, GDK_CONTROL_MASK,
251                       GTK_SCROLL_PAGE_LEFT);
252
253   add_slider_binding (binding_set, GDK_KP_Page_Up, GDK_CONTROL_MASK,
254                       GTK_SCROLL_PAGE_LEFT);  
255
256   add_slider_binding (binding_set, GDK_Page_Up, 0,
257                       GTK_SCROLL_PAGE_UP);
258
259   add_slider_binding (binding_set, GDK_KP_Page_Up, 0,
260                       GTK_SCROLL_PAGE_UP);
261   
262   add_slider_binding (binding_set, GDK_Page_Down, GDK_CONTROL_MASK,
263                       GTK_SCROLL_PAGE_RIGHT);
264
265   add_slider_binding (binding_set, GDK_KP_Page_Down, GDK_CONTROL_MASK,
266                       GTK_SCROLL_PAGE_RIGHT);
267
268   add_slider_binding (binding_set, GDK_Page_Down, 0,
269                       GTK_SCROLL_PAGE_DOWN);
270
271   add_slider_binding (binding_set, GDK_KP_Page_Down, 0,
272                       GTK_SCROLL_PAGE_DOWN);
273
274   /* Logical bindings (vs. visual bindings above) */
275
276   add_slider_binding (binding_set, GDK_plus, 0,
277                       GTK_SCROLL_STEP_FORWARD);  
278
279   add_slider_binding (binding_set, GDK_minus, 0,
280                       GTK_SCROLL_STEP_BACKWARD);  
281
282   add_slider_binding (binding_set, GDK_plus, GDK_CONTROL_MASK,
283                       GTK_SCROLL_PAGE_FORWARD);  
284
285   add_slider_binding (binding_set, GDK_minus, GDK_CONTROL_MASK,
286                       GTK_SCROLL_PAGE_BACKWARD);
287
288
289   add_slider_binding (binding_set, GDK_KP_Add, 0,
290                       GTK_SCROLL_STEP_FORWARD);  
291
292   add_slider_binding (binding_set, GDK_KP_Subtract, 0,
293                       GTK_SCROLL_STEP_BACKWARD);  
294
295   add_slider_binding (binding_set, GDK_KP_Add, GDK_CONTROL_MASK,
296                       GTK_SCROLL_PAGE_FORWARD);  
297
298   add_slider_binding (binding_set, GDK_KP_Subtract, GDK_CONTROL_MASK,
299                       GTK_SCROLL_PAGE_BACKWARD);
300   
301   
302   add_slider_binding (binding_set, GDK_Home, 0,
303                       GTK_SCROLL_START);
304
305   add_slider_binding (binding_set, GDK_KP_Home, 0,
306                       GTK_SCROLL_START);
307
308   add_slider_binding (binding_set, GDK_End, 0,
309                       GTK_SCROLL_END);
310
311   add_slider_binding (binding_set, GDK_KP_End, 0,
312                       GTK_SCROLL_END);
313
314   g_type_class_add_private (gobject_class, sizeof (GtkScalePrivate));
315 }
316
317 static void
318 gtk_scale_orientation_notify (GtkRange         *range,
319                               const GParamSpec *pspec)
320 {
321   range->flippable = (range->orientation == GTK_ORIENTATION_HORIZONTAL);
322 }
323
324 static void
325 gtk_scale_init (GtkScale *scale)
326 {
327   GtkRange *range = GTK_RANGE (scale);
328
329   GTK_WIDGET_SET_FLAGS (scale, GTK_CAN_FOCUS);
330
331   range->slider_size_fixed = TRUE;
332   range->has_stepper_a = FALSE;
333   range->has_stepper_b = FALSE;
334   range->has_stepper_c = FALSE;
335   range->has_stepper_d = FALSE;
336
337   scale->draw_value = TRUE;
338   scale->value_pos = GTK_POS_TOP;
339   scale->digits = 1;
340   range->round_digits = scale->digits;
341
342   gtk_scale_orientation_notify (range, NULL);
343   g_signal_connect (scale, "notify::orientation",
344                     G_CALLBACK (gtk_scale_orientation_notify),
345                     NULL);
346 }
347
348 static void
349 gtk_scale_set_property (GObject      *object,
350                         guint         prop_id,
351                         const GValue *value,
352                         GParamSpec   *pspec)
353 {
354   GtkScale *scale;
355
356   scale = GTK_SCALE (object);
357
358   switch (prop_id)
359     {
360     case PROP_DIGITS:
361       gtk_scale_set_digits (scale, g_value_get_int (value));
362       break;
363     case PROP_DRAW_VALUE:
364       gtk_scale_set_draw_value (scale, g_value_get_boolean (value));
365       break;
366     case PROP_VALUE_POS:
367       gtk_scale_set_value_pos (scale, g_value_get_enum (value));
368       break;
369     default:
370       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371       break;
372     }
373 }
374
375 static void
376 gtk_scale_get_property (GObject      *object,
377                         guint         prop_id,
378                         GValue       *value,
379                         GParamSpec   *pspec)
380 {
381   GtkScale *scale;
382
383   scale = GTK_SCALE (object);
384
385   switch (prop_id)
386     {
387     case PROP_DIGITS:
388       g_value_set_int (value, scale->digits);
389       break;
390     case PROP_DRAW_VALUE:
391       g_value_set_boolean (value, scale->draw_value);
392       break;
393     case PROP_VALUE_POS:
394       g_value_set_enum (value, scale->value_pos);
395       break;
396     default:
397       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
398       break;
399     }
400 }
401
402 /**
403  * gtk_scale_new:
404  * @orientation: the scale's orientation.
405  * @adjustment: the #GtkAdjustment which sets the range of the scale, or
406  *              %NULL to create a new adjustment.
407  *
408  * Creates a new #GtkScale.
409  *
410  * Return value: a new #GtkScale
411  *
412  * Since: 2.16
413  **/
414 GtkWidget *
415 gtk_scale_new (GtkOrientation  orientation,
416                GtkAdjustment  *adjustment)
417 {
418   g_return_val_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment),
419                         NULL);
420
421   return g_object_new (GTK_TYPE_SCALE,
422                        "orientation", orientation,
423                        "adjustment",  adjustment,
424                        NULL);
425 }
426
427 /**
428  * gtk_scale_new_with_range:
429  * @orientation: the scale's orientation.
430  * @min: minimum value
431  * @max: maximum value
432  * @step: step increment (tick size) used with keyboard shortcuts
433  *
434  * Creates a new scale widget with the given orientation that lets the
435  * user input a number between @min and @max (including @min and @max)
436  * with the increment @step.  @step must be nonzero; it's the distance
437  * the slider moves when using the arrow keys to adjust the scale
438  * value.
439  *
440  * Note that the way in which the precision is derived works best if @step
441  * is a power of ten. If the resulting precision is not suitable for your
442  * needs, use gtk_scale_set_digits() to correct it.
443  *
444  * Return value: a new #GtkScale
445  *
446  * Since: 2.16
447  **/
448 GtkWidget *
449 gtk_scale_new_with_range (GtkOrientation orientation,
450                           gdouble        min,
451                           gdouble        max,
452                           gdouble        step)
453 {
454   GtkObject *adj;
455   gint digits;
456
457   g_return_val_if_fail (min < max, NULL);
458   g_return_val_if_fail (step != 0.0, NULL);
459
460   adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
461
462   if (fabs (step) >= 1.0 || step == 0.0)
463     {
464       digits = 0;
465     }
466   else
467     {
468       digits = abs ((gint) floor (log10 (fabs (step))));
469       if (digits > 5)
470         digits = 5;
471     }
472
473   return g_object_new (GTK_TYPE_SCALE,
474                        "orientation", orientation,
475                        "adjustment",  adj,
476                        "digits",      digits,
477                        NULL);
478 }
479
480 void
481 gtk_scale_set_digits (GtkScale *scale,
482                       gint      digits)
483 {
484   GtkRange *range;
485   
486   g_return_if_fail (GTK_IS_SCALE (scale));
487
488   range = GTK_RANGE (scale);
489   
490   digits = CLAMP (digits, -1, MAX_DIGITS);
491
492   if (scale->digits != digits)
493     {
494       scale->digits = digits;
495       if (scale->draw_value)
496         range->round_digits = digits;
497       
498       _gtk_scale_clear_layout (scale);
499       gtk_widget_queue_resize (GTK_WIDGET (scale));
500
501       g_object_notify (G_OBJECT (scale), "digits");
502     }
503 }
504
505 gint
506 gtk_scale_get_digits (GtkScale *scale)
507 {
508   g_return_val_if_fail (GTK_IS_SCALE (scale), -1);
509
510   return scale->digits;
511 }
512
513 void
514 gtk_scale_set_draw_value (GtkScale *scale,
515                           gboolean  draw_value)
516 {
517   g_return_if_fail (GTK_IS_SCALE (scale));
518
519   draw_value = draw_value != FALSE;
520
521   if (scale->draw_value != draw_value)
522     {
523       scale->draw_value = draw_value;
524       if (draw_value)
525         GTK_RANGE (scale)->round_digits = scale->digits;
526       else
527         GTK_RANGE (scale)->round_digits = -1;
528
529       _gtk_scale_clear_layout (scale);
530
531       gtk_widget_queue_resize (GTK_WIDGET (scale));
532
533       g_object_notify (G_OBJECT (scale), "draw-value");
534     }
535 }
536
537 gboolean
538 gtk_scale_get_draw_value (GtkScale *scale)
539 {
540   g_return_val_if_fail (GTK_IS_SCALE (scale), FALSE);
541
542   return scale->draw_value;
543 }
544
545 void
546 gtk_scale_set_value_pos (GtkScale        *scale,
547                          GtkPositionType  pos)
548 {
549   g_return_if_fail (GTK_IS_SCALE (scale));
550
551   if (scale->value_pos != pos)
552     {
553       scale->value_pos = pos;
554
555       _gtk_scale_clear_layout (scale);
556       if (GTK_WIDGET_VISIBLE (scale) && GTK_WIDGET_MAPPED (scale))
557         gtk_widget_queue_resize (GTK_WIDGET (scale));
558
559       g_object_notify (G_OBJECT (scale), "value-pos");
560     }
561 }
562
563 GtkPositionType
564 gtk_scale_get_value_pos (GtkScale *scale)
565 {
566   g_return_val_if_fail (GTK_IS_SCALE (scale), 0);
567
568   return scale->value_pos;
569 }
570
571 static void
572 gtk_scale_get_range_border (GtkRange  *range,
573                             GtkBorder *border)
574 {
575   GtkWidget *widget;
576   GtkScale *scale;
577   gint w, h;
578   
579   widget = GTK_WIDGET (range);
580   scale = GTK_SCALE (range);
581
582   _gtk_scale_get_value_size (scale, &w, &h);
583
584   border->left = 0;
585   border->right = 0;
586   border->top = 0;
587   border->bottom = 0;
588
589   if (scale->draw_value)
590     {
591       gint value_spacing;
592       gtk_widget_style_get (widget, "value-spacing", &value_spacing, NULL);
593
594       switch (scale->value_pos)
595         {
596         case GTK_POS_LEFT:
597           border->left += w + value_spacing;
598           break;
599         case GTK_POS_RIGHT:
600           border->right += w + value_spacing;
601           break;
602         case GTK_POS_TOP:
603           border->top += h + value_spacing;
604           break;
605         case GTK_POS_BOTTOM:
606           border->bottom += h + value_spacing;
607           break;
608         }
609     }
610 }
611
612 /* FIXME this could actually be static at the moment. */
613 void
614 _gtk_scale_get_value_size (GtkScale *scale,
615                            gint     *width,
616                            gint     *height)
617 {
618   GtkRange *range;
619
620   g_return_if_fail (GTK_IS_SCALE (scale));
621
622   if (scale->draw_value)
623     {
624       PangoLayout *layout;
625       PangoRectangle logical_rect;
626       gchar *txt;
627       
628       range = GTK_RANGE (scale);
629
630       layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale), NULL);
631
632       txt = _gtk_scale_format_value (scale, range->adjustment->lower);
633       pango_layout_set_text (layout, txt, -1);
634       g_free (txt);
635       
636       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
637
638       if (width)
639         *width = logical_rect.width;
640       if (height)
641         *height = logical_rect.height;
642
643       txt = _gtk_scale_format_value (scale, range->adjustment->upper);
644       pango_layout_set_text (layout, txt, -1);
645       g_free (txt);
646       
647       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
648
649       if (width)
650         *width = MAX (*width, logical_rect.width);
651       if (height)
652         *height = MAX (*height, logical_rect.height);
653
654       g_object_unref (layout);
655     }
656   else
657     {
658       if (width)
659         *width = 0;
660       if (height)
661         *height = 0;
662     }
663
664 }
665
666 static void
667 gtk_scale_style_set (GtkWidget *widget,
668                      GtkStyle  *previous)
669 {
670   gint slider_length;
671   GtkRange *range;
672
673   range = GTK_RANGE (widget);
674   
675   gtk_widget_style_get (widget,
676                         "slider-length", &slider_length,
677                         NULL);
678   
679   range->min_slider_size = slider_length;
680   
681   _gtk_scale_clear_layout (GTK_SCALE (widget));
682
683   GTK_WIDGET_CLASS (gtk_scale_parent_class)->style_set (widget, previous);
684 }
685
686 static void
687 gtk_scale_screen_changed (GtkWidget *widget,
688                           GdkScreen *old_screen)
689 {
690   _gtk_scale_clear_layout (GTK_SCALE (widget));
691 }
692
693 static gboolean
694 gtk_scale_expose (GtkWidget      *widget,
695                   GdkEventExpose *event)
696 {
697   GtkScale *scale = GTK_SCALE (widget);
698
699   /* We need to chain up _first_ so the various geometry members of
700    * GtkRange struct are updated.
701    */
702   GTK_WIDGET_CLASS (gtk_scale_parent_class)->expose_event (widget, event);
703
704   if (scale->draw_value)
705     {
706       GtkRange *range = GTK_RANGE (scale);
707       PangoLayout *layout;
708       gint x, y;
709       GtkStateType state_type;
710
711       layout = gtk_scale_get_layout (scale);
712       gtk_scale_get_layout_offsets (scale, &x, &y);
713
714       state_type = GTK_STATE_NORMAL;
715       if (!GTK_WIDGET_IS_SENSITIVE (scale))
716         state_type = GTK_STATE_INSENSITIVE;
717
718       gtk_paint_layout (widget->style,
719                         widget->window,
720                         state_type,
721                         FALSE,
722                         NULL,
723                         widget,
724                         range->orientation == GTK_ORIENTATION_HORIZONTAL ?
725                         "hscale" : "vscale",
726                         x, y,
727                         layout);
728
729     }
730
731   return FALSE;
732 }
733
734 static void
735 gtk_scale_real_get_layout_offsets (GtkScale *scale,
736                                    gint     *x,
737                                    gint     *y)
738 {
739   GtkWidget *widget = GTK_WIDGET (scale);
740   GtkRange *range = GTK_RANGE (widget);
741   PangoLayout *layout = gtk_scale_get_layout (scale);
742   PangoRectangle logical_rect;
743   gint value_spacing;
744
745   if (!layout)
746     {
747       *x = 0;
748       *y = 0;
749
750       return;
751     }
752
753   gtk_widget_style_get (widget, "value-spacing", &value_spacing, NULL);
754
755   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
756
757   if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
758     {
759       switch (scale->value_pos)
760         {
761         case GTK_POS_LEFT:
762           *x = range->range_rect.x - value_spacing - logical_rect.width;
763           *y = range->range_rect.y + (range->range_rect.height - logical_rect.height) / 2;
764           break;
765
766         case GTK_POS_RIGHT:
767           *x = range->range_rect.x + range->range_rect.width + value_spacing;
768           *y = range->range_rect.y + (range->range_rect.height - logical_rect.height) / 2;
769           break;
770
771         case GTK_POS_TOP:
772           *x = range->slider_start +
773             (range->slider_end - range->slider_start - logical_rect.width) / 2;
774           *x = CLAMP (*x, 0, widget->allocation.width - logical_rect.width);
775           *y = range->range_rect.y - logical_rect.height - value_spacing;
776           break;
777
778         case GTK_POS_BOTTOM:
779           *x = range->slider_start +
780             (range->slider_end - range->slider_start - logical_rect.width) / 2;
781           *x = CLAMP (*x, 0, widget->allocation.width - logical_rect.width);
782           *y = range->range_rect.y + range->range_rect.height + value_spacing;
783           break;
784
785         default:
786           g_return_if_reached ();
787           break;
788         }
789     }
790   else
791     {
792       switch (scale->value_pos)
793         {
794         case GTK_POS_LEFT:
795           *x = range->range_rect.x - logical_rect.width - value_spacing;
796           *y = range->slider_start + (range->slider_end - range->slider_start - logical_rect.height) / 2;
797           *y = CLAMP (*y, 0, widget->allocation.height - logical_rect.height);
798           break;
799
800         case GTK_POS_RIGHT:
801           *x = range->range_rect.x + range->range_rect.width + value_spacing;
802           *y = range->slider_start + (range->slider_end - range->slider_start - logical_rect.height) / 2;
803           *y = CLAMP (*y, 0, widget->allocation.height - logical_rect.height);
804           break;
805
806         case GTK_POS_TOP:
807           *x = range->range_rect.x + (range->range_rect.width - logical_rect.width) / 2;
808           *y = range->range_rect.y - logical_rect.height - value_spacing;
809           break;
810
811         case GTK_POS_BOTTOM:
812           *x = range->range_rect.x + (range->range_rect.width - logical_rect.width) / 2;
813           *y = range->range_rect.y + range->range_rect.height + value_spacing;
814           break;
815
816         default:
817           g_return_if_reached ();
818         }
819     }
820
821   *x += widget->allocation.x;
822   *y += widget->allocation.y;
823 }
824
825 /**
826  * _gtk_scale_format_value:
827  * @scale: a #GtkScale
828  * @value: adjustment value
829  * 
830  * Emits #GtkScale::format-value signal to format the value, 
831  * if no user signal handlers, falls back to a default format.
832  * 
833  * Return value: formatted value
834  **/
835 gchar*
836 _gtk_scale_format_value (GtkScale *scale,
837                          gdouble   value)
838 {
839   gchar *fmt = NULL;
840
841   g_signal_emit (scale,
842                  signals[FORMAT_VALUE],
843                  0,
844                  value,
845                  &fmt);
846
847   if (fmt)
848     return fmt;
849   else
850     /* insert a LRM, to prevent -20 to come out as 20- in RTL locales */
851     return g_strdup_printf ("\342\200\216%0.*f", scale->digits, value);
852 }
853
854 static void
855 gtk_scale_finalize (GObject *object)
856 {
857   GtkScale *scale = GTK_SCALE (object);
858
859   _gtk_scale_clear_layout (scale);
860
861   G_OBJECT_CLASS (gtk_scale_parent_class)->finalize (object);
862 }
863
864 /**
865  * gtk_scale_get_layout:
866  * @scale: A #GtkScale
867  *
868  * Gets the #PangoLayout used to display the scale. 
869  * The returned object is owned by the scale so does 
870  * not need to be freed by the caller. 
871  *
872  * Return value: the #PangoLayout for this scale, or %NULL 
873  *    if the #GtkScale:draw-value property is %FALSE.
874  *   
875  * Since: 2.4
876  **/
877 PangoLayout *
878 gtk_scale_get_layout (GtkScale *scale)
879 {
880   GtkScalePrivate *priv = GTK_SCALE_GET_PRIVATE (scale);
881   gchar *txt;
882
883   g_return_val_if_fail (GTK_IS_SCALE (scale), NULL);
884
885   if (!priv->layout)
886     {
887       if (scale->draw_value)
888         priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale), NULL);
889     }
890
891   if (scale->draw_value) 
892     {
893       txt = _gtk_scale_format_value (scale,
894                                      GTK_RANGE (scale)->adjustment->value);
895       pango_layout_set_text (priv->layout, txt, -1);
896       g_free (txt);
897     }
898
899   return priv->layout;
900 }
901
902 /**
903  * gtk_scale_get_layout_offsets:
904  * @scale: a #GtkScale
905  * @x: location to store X offset of layout, or %NULL
906  * @y: location to store Y offset of layout, or %NULL
907  *
908  * Obtains the coordinates where the scale will draw the 
909  * #PangoLayout representing the text in the scale. Remember
910  * when using the #PangoLayout function you need to convert to
911  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE. 
912  *
913  * If the #GtkScale:draw-value property is %FALSE, the return 
914  * values are undefined.
915  *
916  * Since: 2.4
917  **/
918 void 
919 gtk_scale_get_layout_offsets (GtkScale *scale,
920                               gint     *x,
921                               gint     *y)
922 {
923   gint local_x = 0; 
924   gint local_y = 0;
925
926   g_return_if_fail (GTK_IS_SCALE (scale));
927
928   if (GTK_SCALE_GET_CLASS (scale)->get_layout_offsets)
929     (GTK_SCALE_GET_CLASS (scale)->get_layout_offsets) (scale, &local_x, &local_y);
930
931   if (x)
932     *x = local_x;
933   
934   if (y)
935     *y = local_y;
936 }
937
938 void
939 _gtk_scale_clear_layout (GtkScale *scale)
940 {
941   GtkScalePrivate *priv = GTK_SCALE_GET_PRIVATE (scale);
942
943   g_return_if_fail (GTK_IS_SCALE (scale));
944
945   if (priv->layout)
946     {
947       g_object_unref (priv->layout);
948       priv->layout = NULL;
949     }
950 }
951
952 #define __GTK_SCALE_C__
953 #include "gtkaliasdef.c"