]> Pileus Git - ~andy/gtk/blob - gtk/gtkscale.c
to simplify parsing for exported functions: - made implementation
[~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 static 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   const 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 (GTK_IS_SCALE (scale));
420
421   draw_value = draw_value != FALSE;
422
423   if (scale->draw_value != draw_value)
424     {
425       scale->draw_value = draw_value;
426
427       gtk_widget_queue_resize (GTK_WIDGET (scale));
428
429       g_object_notify (G_OBJECT (scale), "draw_value");
430     }
431 }
432
433 gboolean
434 gtk_scale_get_draw_value (GtkScale *scale)
435 {
436   g_return_val_if_fail (GTK_IS_SCALE (scale), FALSE);
437
438   return scale->draw_value;
439 }
440
441 void
442 gtk_scale_set_value_pos (GtkScale        *scale,
443                          GtkPositionType  pos)
444 {
445   g_return_if_fail (GTK_IS_SCALE (scale));
446
447   if (scale->value_pos != pos)
448     {
449       scale->value_pos = pos;
450
451       if (GTK_WIDGET_VISIBLE (scale) && GTK_WIDGET_MAPPED (scale))
452         gtk_widget_queue_resize (GTK_WIDGET (scale));
453
454       g_object_notify (G_OBJECT (scale), "value_pos");
455     }
456 }
457
458 GtkPositionType
459 gtk_scale_get_value_pos (GtkScale *scale)
460 {
461   g_return_val_if_fail (GTK_IS_SCALE (scale), 0);
462
463   return scale->value_pos;
464 }
465
466 static void
467 gtk_scale_get_range_border (GtkRange  *range,
468                             GtkBorder *border)
469 {
470   GtkWidget *widget;
471   GtkScale *scale;
472   gint w, h;
473   
474   widget = GTK_WIDGET (range);
475   scale = GTK_SCALE (range);
476
477   _gtk_scale_get_value_size (scale, &w, &h);
478
479   border->left = 0;
480   border->right = 0;
481   border->top = 0;
482   border->bottom = 0;
483
484   if (scale->draw_value)
485     {
486       gint value_spacing;
487       gtk_widget_style_get (widget, "value_spacing", &value_spacing, NULL);
488
489       switch (scale->value_pos)
490         {
491         case GTK_POS_LEFT:
492           border->left += w + value_spacing;
493           break;
494         case GTK_POS_RIGHT:
495           border->right += w + value_spacing;
496           break;
497         case GTK_POS_TOP:
498           border->top += h + value_spacing;
499           break;
500         case GTK_POS_BOTTOM:
501           border->bottom += h + value_spacing;
502           break;
503         }
504     }
505 }
506
507 /* FIXME this could actually be static at the moment. */
508 void
509 _gtk_scale_get_value_size (GtkScale *scale,
510                            gint     *width,
511                            gint     *height)
512 {
513   GtkRange *range;
514
515   g_return_if_fail (GTK_IS_SCALE (scale));
516
517   if (scale->draw_value)
518     {
519       PangoLayout *layout;
520       PangoRectangle logical_rect;
521       gchar *txt;
522       
523       range = GTK_RANGE (scale);
524
525       layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale), NULL);
526
527       txt = _gtk_scale_format_value (scale, range->adjustment->lower);
528       pango_layout_set_text (layout, txt, -1);
529       g_free (txt);
530       
531       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
532
533       if (width)
534         *width = logical_rect.width;
535       if (height)
536         *height = logical_rect.height;
537
538       txt = _gtk_scale_format_value (scale, range->adjustment->upper);
539       pango_layout_set_text (layout, txt, -1);
540       g_free (txt);
541       
542       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
543
544       if (width)
545         *width = MAX (*width, logical_rect.width);
546       if (height)
547         *height = MAX (*height, logical_rect.height);
548
549       g_object_unref (G_OBJECT (layout));
550     }
551   else
552     {
553       if (width)
554         *width = 0;
555       if (height)
556         *height = 0;
557     }
558
559 }
560
561 static void
562 gtk_scale_style_set (GtkWidget *widget,
563                      GtkStyle  *previous)
564 {
565   gint slider_length;
566   GtkRange *range;
567
568   range = GTK_RANGE (widget);
569   
570   gtk_widget_style_get (widget,
571                         "slider_length", &slider_length,
572                         NULL);
573   
574   range->min_slider_size = slider_length;
575   
576   (* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous);
577 }
578
579
580 /**
581  * _gtk_scale_format_value:
582  * @scale: a #GtkScale
583  * @value: adjustment value
584  * 
585  * Emits "format_value" signal to format the value, if no user
586  * signal handlers, falls back to a default format.
587  * 
588  * Return value: formatted value
589  **/
590 gchar*
591 _gtk_scale_format_value (GtkScale *scale,
592                          gdouble   value)
593 {
594   gchar *fmt = NULL;
595
596   g_signal_emit (G_OBJECT (scale),
597                  signals[FORMAT_VALUE],
598                  0,
599                  value,
600                  &fmt);
601
602   if (fmt)
603     return fmt;
604   else
605     return g_strdup_printf ("%0.*f", scale->digits,
606                             value);
607 }