]> Pileus Git - ~andy/gtk/blob - gtk/gtkscale.c
fixes to compile with G_DISABLE_COMPAT
[~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 <math.h>
29 #include "gtkintl.h"
30 #include "gtkscale.h"
31 #include "gtkmarshal.h"
32 #include "gdk/gdkkeysyms.h"
33 #include "gtkbindings.h"
34
35 enum {
36   PROP_0,
37   PROP_DIGITS,
38   PROP_DRAW_VALUE,
39   PROP_VALUE_POS
40 };
41
42 enum {
43   FORMAT_VALUE,
44   LAST_SIGNAL
45 };
46
47 static guint signals[LAST_SIGNAL];
48 static GtkRangeClass *parent_class = NULL;
49
50 static void gtk_scale_class_init       (GtkScaleClass *klass);
51 static void gtk_scale_init             (GtkScale      *scale);
52 static void gtk_scale_set_property     (GObject       *object,
53                                         guint          prop_id,
54                                         const GValue  *value,
55                                         GParamSpec    *pspec);
56 static void gtk_scale_get_property     (GObject       *object,
57                                         guint          prop_id,
58                                         GValue        *value,
59                                         GParamSpec    *pspec);
60 static void gtk_scale_style_set        (GtkWidget     *widget,
61                                         GtkStyle      *previous);
62 static void gtk_scale_get_range_border (GtkRange      *range,
63                                         GtkBorder     *border);
64
65 GtkType
66 gtk_scale_get_type (void)
67 {
68   static GtkType scale_type = 0;
69
70   if (!scale_type)
71     {
72       static const GtkTypeInfo scale_info =
73       {
74         "GtkScale",
75         sizeof (GtkScale),
76         sizeof (GtkScaleClass),
77         (GtkClassInitFunc) gtk_scale_class_init,
78         (GtkObjectInitFunc) gtk_scale_init,
79         /* reserved_1 */ NULL,
80         /* reserved_2 */ NULL,
81         (GtkClassInitFunc) NULL,
82       };
83
84       scale_type = gtk_type_unique (GTK_TYPE_RANGE, &scale_info);
85     }
86
87   return scale_type;
88 }
89
90 gboolean
91 single_string_accumulator (GSignalInvocationHint *ihint,
92                            GValue                *return_accu,
93                            const GValue          *handler_return,
94                            gpointer               dummy)
95 {
96   gboolean continue_emission;
97   gchar *str;
98   
99   str = g_value_get_string (handler_return);
100   g_value_set_string (return_accu, str);
101   continue_emission = str == NULL;
102   
103   return continue_emission;
104 }
105
106
107 #define add_slider_binding(binding_set, keyval, mask, scroll)          \
108   gtk_binding_entry_add_signal (binding_set, keyval, mask,             \
109                                 "move_slider", 1,                      \
110                                 GTK_TYPE_SCROLL_TYPE, scroll)
111
112 static void
113 gtk_scale_class_init (GtkScaleClass *class)
114 {
115   GObjectClass   *gobject_class;
116   GtkObjectClass *object_class;
117   GtkWidgetClass *widget_class;
118   GtkRangeClass *range_class;
119   GtkBindingSet *binding_set;
120   
121   gobject_class = G_OBJECT_CLASS (class);
122   object_class = (GtkObjectClass*) class;
123   range_class = (GtkRangeClass*) class;
124   widget_class = (GtkWidgetClass*) class;
125   
126   parent_class = gtk_type_class (GTK_TYPE_RANGE);
127   
128   gobject_class->set_property = gtk_scale_set_property;
129   gobject_class->get_property = gtk_scale_get_property;
130
131   widget_class->style_set = gtk_scale_style_set;
132
133   range_class->get_range_border = gtk_scale_get_range_border;
134   
135   signals[FORMAT_VALUE] =
136     g_signal_new ("format_value",
137                   G_TYPE_FROM_CLASS (object_class),
138                   G_SIGNAL_RUN_LAST,
139                   G_STRUCT_OFFSET (GtkScaleClass, format_value),
140                   single_string_accumulator, NULL,
141                   gtk_marshal_STRING__DOUBLE,
142                   G_TYPE_STRING, 1,
143                   G_TYPE_DOUBLE);
144
145   g_object_class_install_property (gobject_class,
146                                    PROP_DIGITS,
147                                    g_param_spec_int ("digits",
148                                                      _("Digits"),
149                                                      _("The number of decimal places that are displayed in the value"),
150                                                      0,
151                                                      G_MAXINT,
152                                                      0,
153                                                      G_PARAM_READWRITE));
154   
155   g_object_class_install_property (gobject_class,
156                                    PROP_DRAW_VALUE,
157                                    g_param_spec_boolean ("draw_value",
158                                                          _("Draw Value"),
159                                                          _("Whether the current value is displayed as a string next to the slider"),
160                                                          FALSE,
161                                                          G_PARAM_READWRITE));
162   
163   g_object_class_install_property (gobject_class,
164                                    PROP_VALUE_POS,
165                                    g_param_spec_enum ("value_pos",
166                                                       _("Value Position"),
167                                                       _("The position in which the current value is displayed"),
168                                                       GTK_TYPE_POSITION_TYPE,
169                                                       GTK_POS_LEFT,
170                                                       G_PARAM_READWRITE));
171
172   gtk_widget_class_install_style_property (widget_class,
173                                            g_param_spec_int ("slider_length",
174                                                              _("Slider Length"),
175                                                              _("Length of scale's slider"),
176                                                              0,
177                                                              G_MAXINT,
178                                                              31,
179                                                              G_PARAM_READABLE));
180
181   gtk_widget_class_install_style_property (widget_class,
182                                            g_param_spec_int ("value_spacing",
183                                                              _("Value spacing"),
184                                                              _("Space between value text and the slider/trough area"),
185                                                              0,
186                                                              G_MAXINT,
187                                                              2,
188                                                              G_PARAM_READABLE));
189   
190   /* All bindings (even arrow keys) are on both h/v scale, because
191    * blind users etc. don't care about scale orientation.
192    */
193   
194   binding_set = gtk_binding_set_by_class (class);
195
196   add_slider_binding (binding_set, GDK_Left, 0,
197                       GTK_SCROLL_STEP_LEFT);
198
199   add_slider_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
200                       GTK_SCROLL_PAGE_LEFT);
201
202   add_slider_binding (binding_set, GDK_KP_Left, 0,
203                       GTK_SCROLL_STEP_LEFT);
204
205   add_slider_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
206                       GTK_SCROLL_PAGE_LEFT);
207
208   add_slider_binding (binding_set, GDK_Right, 0,
209                       GTK_SCROLL_STEP_RIGHT);
210
211   add_slider_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
212                       GTK_SCROLL_PAGE_RIGHT);
213
214   add_slider_binding (binding_set, GDK_KP_Right, 0,
215                       GTK_SCROLL_STEP_RIGHT);
216
217   add_slider_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
218                       GTK_SCROLL_PAGE_RIGHT);
219
220   add_slider_binding (binding_set, GDK_Up, 0,
221                       GTK_SCROLL_STEP_UP);
222
223   add_slider_binding (binding_set, GDK_Up, GDK_CONTROL_MASK,
224                       GTK_SCROLL_PAGE_UP);
225
226   add_slider_binding (binding_set, GDK_KP_Up, 0,
227                       GTK_SCROLL_STEP_UP);
228
229   add_slider_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK,
230                       GTK_SCROLL_PAGE_UP);
231
232   add_slider_binding (binding_set, GDK_Down, 0,
233                       GTK_SCROLL_STEP_DOWN);
234
235   add_slider_binding (binding_set, GDK_Down, GDK_CONTROL_MASK,
236                       GTK_SCROLL_PAGE_DOWN);
237
238   add_slider_binding (binding_set, GDK_KP_Down, 0,
239                       GTK_SCROLL_STEP_DOWN);
240
241   add_slider_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK,
242                       GTK_SCROLL_PAGE_DOWN);
243    
244   add_slider_binding (binding_set, GDK_Page_Up, 0,
245                       GTK_SCROLL_PAGE_LEFT);
246
247   add_slider_binding (binding_set, GDK_KP_Page_Up, 0,
248                       GTK_SCROLL_PAGE_LEFT);  
249
250   add_slider_binding (binding_set, GDK_Page_Up, 0,
251                       GTK_SCROLL_PAGE_UP);
252
253   add_slider_binding (binding_set, GDK_KP_Page_Up, 0,
254                       GTK_SCROLL_PAGE_UP);
255   
256   add_slider_binding (binding_set, GDK_Page_Down, 0,
257                       GTK_SCROLL_PAGE_RIGHT);
258
259   add_slider_binding (binding_set, GDK_KP_Page_Down, 0,
260                       GTK_SCROLL_PAGE_RIGHT);
261
262   add_slider_binding (binding_set, GDK_Page_Down, 0,
263                       GTK_SCROLL_PAGE_DOWN);
264
265   add_slider_binding (binding_set, GDK_KP_Page_Down, 0,
266                       GTK_SCROLL_PAGE_DOWN);
267
268   /* Logical bindings (vs. visual bindings above) */
269
270   add_slider_binding (binding_set, GDK_plus, 0,
271                       GTK_SCROLL_STEP_FORWARD);  
272
273   add_slider_binding (binding_set, GDK_minus, 0,
274                       GTK_SCROLL_STEP_BACKWARD);  
275
276   add_slider_binding (binding_set, GDK_plus, GDK_CONTROL_MASK,
277                       GTK_SCROLL_PAGE_FORWARD);  
278
279   add_slider_binding (binding_set, GDK_minus, GDK_CONTROL_MASK,
280                       GTK_SCROLL_PAGE_BACKWARD);
281
282
283   add_slider_binding (binding_set, GDK_KP_Add, 0,
284                       GTK_SCROLL_STEP_FORWARD);  
285
286   add_slider_binding (binding_set, GDK_KP_Subtract, 0,
287                       GTK_SCROLL_STEP_BACKWARD);  
288
289   add_slider_binding (binding_set, GDK_KP_Add, GDK_CONTROL_MASK,
290                       GTK_SCROLL_PAGE_FORWARD);  
291
292   add_slider_binding (binding_set, GDK_KP_Subtract, GDK_CONTROL_MASK,
293                       GTK_SCROLL_PAGE_BACKWARD);
294   
295   
296   add_slider_binding (binding_set, GDK_Home, 0,
297                       GTK_SCROLL_START);
298
299   add_slider_binding (binding_set, GDK_KP_Home, 0,
300                       GTK_SCROLL_START);
301
302   add_slider_binding (binding_set, GDK_End, 0,
303                       GTK_SCROLL_END);
304
305   add_slider_binding (binding_set, GDK_KP_End, 0,
306                       GTK_SCROLL_END);
307 }
308
309 static void
310 gtk_scale_set_property (GObject      *object,
311                         guint         prop_id,
312                         const GValue *value,
313                         GParamSpec   *pspec)
314 {
315   GtkScale *scale;
316
317   scale = GTK_SCALE (object);
318
319   switch (prop_id)
320     {
321     case PROP_DIGITS:
322       gtk_scale_set_digits (scale, g_value_get_int (value));
323       break;
324     case PROP_DRAW_VALUE:
325       gtk_scale_set_draw_value (scale, g_value_get_boolean (value));
326       break;
327     case PROP_VALUE_POS:
328       gtk_scale_set_value_pos (scale, g_value_get_enum (value));
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332       break;
333     }
334 }
335
336 static void
337 gtk_scale_get_property (GObject      *object,
338                         guint         prop_id,
339                         GValue       *value,
340                         GParamSpec   *pspec)
341 {
342   GtkScale *scale;
343
344   scale = GTK_SCALE (object);
345
346   switch (prop_id)
347     {
348     case PROP_DIGITS:
349       g_value_set_int (value, scale->digits);
350       break;
351     case PROP_DRAW_VALUE:
352       g_value_set_boolean (value, scale->draw_value);
353       break;
354     case PROP_VALUE_POS:
355       g_value_set_enum (value, scale->value_pos);
356       break;
357     default:
358       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
359       break;
360     }
361 }
362
363 static void
364 gtk_scale_init (GtkScale *scale)
365 {
366   GtkRange *range;
367
368   range = GTK_RANGE (scale);
369   
370   GTK_WIDGET_SET_FLAGS (scale, GTK_CAN_FOCUS);
371
372   range->slider_size_fixed = TRUE;
373   range->has_stepper_a = FALSE;
374   range->has_stepper_b = FALSE;
375   range->has_stepper_c = FALSE;
376   range->has_stepper_d = FALSE;
377   
378   scale->draw_value = TRUE;
379   scale->value_pos = GTK_POS_TOP;
380   scale->digits = 1;
381   range->round_digits = scale->digits;
382 }
383
384 void
385 gtk_scale_set_digits (GtkScale *scale,
386                       gint      digits)
387 {
388   GtkRange *range;
389   
390   g_return_if_fail (GTK_IS_SCALE (scale));
391
392   range = GTK_RANGE (scale);
393   
394   digits = CLAMP (digits, -1, 16);
395
396   if (scale->digits != digits)
397     {
398       scale->digits = digits;
399       range->round_digits = digits;
400       
401       gtk_widget_queue_resize (GTK_WIDGET (scale));
402
403       g_object_notify (G_OBJECT (scale), "digits");
404     }
405 }
406
407 gint
408 gtk_scale_get_digits (GtkScale *scale)
409 {
410   g_return_val_if_fail (GTK_IS_SCALE (scale), -1);
411
412   return scale->digits;
413 }
414
415 void
416 gtk_scale_set_draw_value (GtkScale *scale,
417                           gboolean  draw_value)
418 {
419   g_return_if_fail (scale != NULL);
420   g_return_if_fail (GTK_IS_SCALE (scale));
421
422   draw_value = draw_value != FALSE;
423
424   if (scale->draw_value != draw_value)
425     {
426       scale->draw_value = draw_value;
427
428       gtk_widget_queue_resize (GTK_WIDGET (scale));
429
430       g_object_notify (G_OBJECT (scale), "draw_value");
431     }
432 }
433
434 gboolean
435 gtk_scale_get_draw_value (GtkScale *scale)
436 {
437   g_return_val_if_fail (GTK_IS_SCALE (scale), FALSE);
438
439   return scale->draw_value;
440 }
441
442 void
443 gtk_scale_set_value_pos (GtkScale        *scale,
444                          GtkPositionType  pos)
445 {
446   g_return_if_fail (GTK_IS_SCALE (scale));
447
448   if (scale->value_pos != pos)
449     {
450       scale->value_pos = pos;
451
452       if (GTK_WIDGET_VISIBLE (scale) && GTK_WIDGET_MAPPED (scale))
453         gtk_widget_queue_resize (GTK_WIDGET (scale));
454
455       g_object_notify (G_OBJECT (scale), "value_pos");
456     }
457 }
458
459 GtkPositionType
460 gtk_scale_get_value_pos (GtkScale *scale)
461 {
462   g_return_val_if_fail (GTK_IS_SCALE (scale), 0);
463
464   return scale->value_pos;
465 }
466
467 static void
468 gtk_scale_get_range_border (GtkRange  *range,
469                             GtkBorder *border)
470 {
471   GtkWidget *widget;
472   GtkScale *scale;
473   gint w, h;
474   
475   widget = GTK_WIDGET (range);
476   scale = GTK_SCALE (range);
477
478   _gtk_scale_get_value_size (scale, &w, &h);
479
480   border->left = 0;
481   border->right = 0;
482   border->top = 0;
483   border->bottom = 0;
484
485   if (scale->draw_value)
486     {
487       gint value_spacing;
488       gtk_widget_style_get (widget, "value_spacing", &value_spacing, NULL);
489
490       switch (scale->value_pos)
491         {
492         case GTK_POS_LEFT:
493           border->left += w + value_spacing;
494           break;
495         case GTK_POS_RIGHT:
496           border->right += w + value_spacing;
497           break;
498         case GTK_POS_TOP:
499           border->top += h + value_spacing;
500           break;
501         case GTK_POS_BOTTOM:
502           border->bottom += h + value_spacing;
503           break;
504         }
505     }
506 }
507
508 /* FIXME this could actually be static at the moment. */
509 void
510 _gtk_scale_get_value_size (GtkScale *scale,
511                            gint     *width,
512                            gint     *height)
513 {
514   GtkRange *range;
515
516   g_return_if_fail (scale != NULL);
517   g_return_if_fail (GTK_IS_SCALE (scale));
518
519   if (scale->draw_value)
520     {
521       PangoLayout *layout;
522       PangoRectangle logical_rect;
523       gchar *txt;
524       
525       range = GTK_RANGE (scale);
526
527       layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale), NULL);
528
529       txt = _gtk_scale_format_value (scale, range->adjustment->lower);
530       pango_layout_set_text (layout, txt, -1);
531       g_free (txt);
532       
533       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
534
535       if (width)
536         *width = logical_rect.width;
537       if (height)
538         *height = logical_rect.height;
539
540       txt = _gtk_scale_format_value (scale, range->adjustment->upper);
541       pango_layout_set_text (layout, txt, -1);
542       g_free (txt);
543       
544       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
545
546       if (width)
547         *width = MAX (*width, logical_rect.width);
548       if (height)
549         *height = MAX (*height, logical_rect.height);
550
551       g_object_unref (G_OBJECT (layout));
552     }
553   else
554     {
555       if (width)
556         *width = 0;
557       if (height)
558         *height = 0;
559     }
560
561 }
562
563 static void
564 gtk_scale_style_set (GtkWidget *widget,
565                      GtkStyle  *previous)
566 {
567   gint slider_length;
568   GtkRange *range;
569
570   range = GTK_RANGE (widget);
571   
572   gtk_widget_style_get (widget,
573                         "slider_length", &slider_length,
574                         NULL);
575   
576   range->min_slider_size = slider_length;
577   
578   (* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous);
579 }
580
581
582 /**
583  * _gtk_scale_format_value:
584  * @scale: a #GtkScale
585  * @value: adjustment value
586  * 
587  * Emits "format_value" signal to format the value, if no user
588  * signal handlers, falls back to a default format.
589  * 
590  * Return value: formatted value
591  **/
592 gchar*
593 _gtk_scale_format_value (GtkScale *scale,
594                          gdouble   value)
595 {
596   gchar *fmt = NULL;
597
598   g_signal_emit (G_OBJECT (scale),
599                  signals[FORMAT_VALUE],
600                  0,
601                  value,
602                  &fmt);
603
604   if (fmt)
605     return fmt;
606   else
607     return g_strdup_printf ("%0.*f", scale->digits,
608                             value);
609 }