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