]> Pileus Git - ~andy/gtk/blob - gtk/gtkvscale.c
be31ec33788a74ab812e6457ab28eb7ca36ff9d2
[~andy/gtk] / gtk / gtkvscale.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <stdio.h>
28 #include "gtkvscale.h"
29 #include "gtksignal.h"
30 #include "gdk/gdkkeysyms.h"
31 #include "gtkintl.h"
32 #include "gtkbindings.h"
33
34
35 #define SCALE_CLASS(w)  GTK_SCALE_GET_CLASS (w)
36 #define RANGE_CLASS(w)  GTK_RANGE_GET_CLASS (w)
37
38 enum {
39   PROP_0,
40   PROP_ADJUSTMENT
41 };
42
43 static void     gtk_vscale_class_init       (GtkVScaleClass *klass);
44 static void     gtk_vscale_init             (GtkVScale      *vscale);
45 static void     gtk_vscale_set_property     (GObject        *object,
46                                              guint           prop_id,
47                                              const GValue   *value,
48                                              GParamSpec     *pspec);
49 static void     gtk_vscale_get_property     (GObject        *object,
50                                              guint           prop_id,
51                                              GValue         *value,
52                                              GParamSpec     *pspec);
53 static void     gtk_vscale_realize          (GtkWidget      *widget);
54 static void     gtk_vscale_size_request     (GtkWidget      *widget,
55                                              GtkRequisition *requisition);
56 static void     gtk_vscale_size_allocate    (GtkWidget      *widget,
57                                              GtkAllocation  *allocation);
58 static void     gtk_vscale_pos_trough       (GtkVScale      *vscale,
59                                              gint           *x,
60                                              gint           *y,
61                                              gint           *w,
62                                              gint           *h);
63 static void     gtk_vscale_pos_background   (GtkVScale      *vscale,
64                                              gint           *x,
65                                              gint           *y,
66                                              gint           *w,
67                                              gint           *h);
68 static void     gtk_vscale_draw_slider      (GtkRange       *range);
69 static void     gtk_vscale_draw_value       (GtkScale       *scale);
70 static void     gtk_vscale_clear_background (GtkRange       *range);
71
72 GtkType
73 gtk_vscale_get_type (void)
74 {
75   static GtkType vscale_type = 0;
76   
77   if (!vscale_type)
78     {
79       static const GtkTypeInfo vscale_info =
80       {
81         "GtkVScale",
82         sizeof (GtkVScale),
83         sizeof (GtkVScaleClass),
84         (GtkClassInitFunc) gtk_vscale_class_init,
85         (GtkObjectInitFunc) gtk_vscale_init,
86         /* reserved_1 */ NULL,
87         /* reserved_2 */ NULL,
88         (GtkClassInitFunc) NULL,
89       };
90       
91       vscale_type = gtk_type_unique (GTK_TYPE_SCALE, &vscale_info);
92     }
93   
94   return vscale_type;
95 }
96
97 #define add_slider_binding(binding_set, keyval, mask, scroll, trough) \
98   gtk_binding_entry_add_signal (binding_set, keyval, mask,             \
99                                 "move_slider", 2,                      \
100                                 GTK_TYPE_SCROLL_TYPE, scroll,          \
101                                 GTK_TYPE_TROUGH_TYPE, trough)
102
103 static void
104 gtk_vscale_class_init (GtkVScaleClass *class)
105 {
106   GtkObjectClass *object_class;
107   GObjectClass   *gobject_class;
108   GtkWidgetClass *widget_class;
109   GtkRangeClass *range_class;
110   GtkScaleClass *scale_class;
111   GtkBindingSet *binding_set;
112   
113   object_class = (GtkObjectClass*) class;
114   gobject_class = G_OBJECT_CLASS (class);
115   widget_class = (GtkWidgetClass*) class;
116   range_class = (GtkRangeClass*) class;
117   scale_class = (GtkScaleClass*) class;
118   
119   gobject_class->set_property = gtk_vscale_set_property;
120   gobject_class->get_property = gtk_vscale_get_property;
121   
122   widget_class->realize = gtk_vscale_realize;
123   widget_class->size_request = gtk_vscale_size_request;
124   widget_class->size_allocate = gtk_vscale_size_allocate;
125   
126   range_class->slider_update = _gtk_range_default_vslider_update;
127   range_class->trough_click = _gtk_range_default_vtrough_click;
128   range_class->motion = _gtk_range_default_vmotion;
129   range_class->draw_slider = gtk_vscale_draw_slider;
130   range_class->clear_background = gtk_vscale_clear_background;
131   
132   scale_class->draw_value = gtk_vscale_draw_value;
133
134   g_object_class_install_property (gobject_class,
135                                    PROP_ADJUSTMENT,
136                                    g_param_spec_object ("adjustment",
137                                                         _("Adjustment"),
138                                                         _("The GtkAdjustment that determines the values to use for this VScale."),
139                                                         GTK_TYPE_ADJUSTMENT,
140                                                         G_PARAM_READWRITE));
141
142   binding_set = gtk_binding_set_by_class (object_class);
143
144   add_slider_binding (binding_set, GDK_Up, 0,
145                       GTK_SCROLL_STEP_UP, GTK_TROUGH_NONE);
146
147   add_slider_binding (binding_set, GDK_Up, GDK_CONTROL_MASK,
148                       GTK_SCROLL_PAGE_UP, GTK_TROUGH_NONE);
149
150   add_slider_binding (binding_set, GDK_KP_Up, 0,
151                       GTK_SCROLL_STEP_UP, GTK_TROUGH_NONE);
152
153   add_slider_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK,
154                       GTK_SCROLL_PAGE_UP, GTK_TROUGH_NONE);
155
156
157   add_slider_binding (binding_set, GDK_Down, 0,
158                       GTK_SCROLL_STEP_DOWN, GTK_TROUGH_NONE);
159
160   add_slider_binding (binding_set, GDK_Down, GDK_CONTROL_MASK,
161                       GTK_SCROLL_PAGE_DOWN, GTK_TROUGH_NONE);
162
163   add_slider_binding (binding_set, GDK_KP_Down, 0,
164                       GTK_SCROLL_STEP_DOWN, GTK_TROUGH_NONE);
165
166   add_slider_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK,
167                       GTK_SCROLL_PAGE_DOWN, GTK_TROUGH_NONE);
168
169   add_slider_binding (binding_set, GDK_Page_Up, 0,
170                       GTK_SCROLL_PAGE_BACKWARD, GTK_TROUGH_NONE);
171
172   add_slider_binding (binding_set, GDK_KP_Page_Up, 0,
173                       GTK_SCROLL_PAGE_BACKWARD, GTK_TROUGH_NONE);  
174
175   add_slider_binding (binding_set, GDK_Page_Down, 0,
176                       GTK_SCROLL_PAGE_FORWARD, GTK_TROUGH_NONE);
177
178   add_slider_binding (binding_set, GDK_KP_Page_Down, 0,
179                       GTK_SCROLL_PAGE_FORWARD, GTK_TROUGH_NONE);
180
181   add_slider_binding (binding_set, GDK_Home, 0,
182                       GTK_SCROLL_NONE, GTK_TROUGH_START);
183
184   add_slider_binding (binding_set, GDK_KP_Home, 0,
185                       GTK_SCROLL_NONE, GTK_TROUGH_START);
186
187
188   add_slider_binding (binding_set, GDK_End, 0,
189                       GTK_SCROLL_NONE, GTK_TROUGH_END);
190
191   add_slider_binding (binding_set, GDK_KP_End, 0,
192                       GTK_SCROLL_NONE, GTK_TROUGH_END);
193 }
194
195 static void
196 gtk_vscale_set_property (GObject         *object,
197                          guint            prop_id,
198                          const GValue    *value,
199                          GParamSpec      *pspec)
200 {
201   GtkVScale *vscale;
202   
203   vscale = GTK_VSCALE (object);
204   
205   switch (prop_id)
206     {
207     case PROP_ADJUSTMENT:
208       gtk_range_set_adjustment (GTK_RANGE (vscale), g_value_get_object (value));
209       break;
210     default:
211       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
212       break;
213     }
214 }
215
216 static void
217 gtk_vscale_get_property (GObject         *object,
218                          guint            prop_id,
219                          GValue          *value,
220                          GParamSpec      *pspec)
221 {
222   GtkVScale *vscale;
223   
224   vscale = GTK_VSCALE (object);
225   
226   switch (prop_id)
227     {
228     case PROP_ADJUSTMENT:
229       g_value_set_object (value,
230                           G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (vscale))));
231       break;
232     default:
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234       break;
235     }
236 }
237
238 static void
239 gtk_vscale_init (GtkVScale *vscale)
240 {
241   GTK_WIDGET_SET_FLAGS (vscale, GTK_NO_WINDOW);
242 }
243
244 GtkWidget*
245 gtk_vscale_new (GtkAdjustment *adjustment)
246 {
247   GtkWidget *vscale;
248   
249   vscale = gtk_widget_new (GTK_TYPE_VSCALE,
250                            "adjustment", adjustment,
251                            NULL);
252   
253   return vscale;
254 }
255
256
257 static void
258 gtk_vscale_realize (GtkWidget *widget)
259 {
260   GtkRange *range;
261   GdkWindowAttr attributes;
262   gint attributes_mask;
263   gint x, y, w, h;
264   gint slider_width, slider_length;
265   
266   g_return_if_fail (widget != NULL);
267   g_return_if_fail (GTK_IS_VSCALE (widget));
268   
269   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
270   range = GTK_RANGE (widget);
271
272   _gtk_range_get_props (range, &slider_width, NULL, NULL, NULL);
273   gtk_widget_style_get (widget, "slider_length", &slider_length, NULL);
274   
275   widget->window = gtk_widget_get_parent_window (widget);
276   gdk_window_ref (widget->window);
277   
278   gtk_vscale_pos_trough (GTK_VSCALE (widget), &x, &y, &w, &h);
279   
280   attributes.x = x;
281   attributes.y = y;
282   attributes.width = w;
283   attributes.height = h;
284   attributes.wclass = GDK_INPUT_OUTPUT;
285   attributes.window_type = GDK_WINDOW_CHILD;
286          
287   attributes.event_mask = gtk_widget_get_events (widget) | 
288     (GDK_EXPOSURE_MASK |
289      GDK_BUTTON_PRESS_MASK |
290      GDK_BUTTON_RELEASE_MASK |
291      GDK_ENTER_NOTIFY_MASK |
292      GDK_LEAVE_NOTIFY_MASK);
293   attributes.visual = gtk_widget_get_visual (widget);
294   attributes.colormap = gtk_widget_get_colormap (widget);
295   
296   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
297   
298   range->trough = gdk_window_new (widget->window, &attributes, attributes_mask);
299   
300   attributes.width = slider_width;
301   attributes.height = slider_length;
302   attributes.event_mask |= (GDK_BUTTON_MOTION_MASK |
303                             GDK_POINTER_MOTION_HINT_MASK);
304   
305   range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
306   
307   widget->style = gtk_style_attach (widget->style, widget->window);
308   
309   gdk_window_set_user_data (range->trough, widget);
310   gdk_window_set_user_data (range->slider, widget);
311   
312   gtk_style_set_background (widget->style, range->trough, GTK_STATE_ACTIVE);
313   gtk_style_set_background (widget->style, range->slider, GTK_STATE_NORMAL);
314   
315   _gtk_range_slider_update (GTK_RANGE (widget));
316   
317   gdk_window_show (range->slider);
318 }
319
320 static void
321 gtk_vscale_clear_background (GtkRange    *range)
322 {
323   GtkWidget *widget;
324   GtkScale *scale;
325   gint x, y, width, height;
326   
327   g_return_if_fail (range != NULL);
328   g_return_if_fail (GTK_IS_SCALE (range));
329   
330   widget = GTK_WIDGET (range);
331   scale = GTK_SCALE (range);
332   
333   gtk_vscale_pos_background (GTK_VSCALE (widget), &x, &y, &width, &height);
334   
335   gtk_widget_queue_clear_area (GTK_WIDGET (range),
336                                x, y, width, height);
337 }
338
339 static void
340 gtk_vscale_size_request (GtkWidget      *widget,
341                          GtkRequisition *requisition)
342 {
343   GtkScale *scale = GTK_SCALE (widget);
344   gint slider_width, slider_length, trough_border;
345   
346   _gtk_range_get_props (GTK_RANGE (scale),
347                         &slider_width, &trough_border, NULL, NULL);
348   gtk_widget_style_get (widget, "slider_length", &slider_length, NULL);
349     
350   requisition->width = (slider_width + trough_border * 2);
351   requisition->height = (slider_length + trough_border) * 2;
352   
353   if (scale->draw_value)
354     {
355       gint value_width, value_height;
356       gtk_scale_get_value_size (scale, &value_width, &value_height);
357       
358       if ((scale->value_pos == GTK_POS_LEFT) ||
359           (scale->value_pos == GTK_POS_RIGHT))
360         {
361           requisition->width += value_width + SCALE_CLASS (scale)->value_spacing;
362           if (requisition->height < (value_height))
363             requisition->height = value_height;
364         }
365       else if ((scale->value_pos == GTK_POS_TOP) ||
366                (scale->value_pos == GTK_POS_BOTTOM))
367         {
368           if (requisition->width < value_width)
369             requisition->width = value_width;
370           requisition->height += value_height;
371         }
372     }
373 }
374
375 static void
376 gtk_vscale_size_allocate (GtkWidget     *widget,
377                           GtkAllocation *allocation)
378 {
379   GtkRange *range;
380   GtkScale *scale;
381   gint width, height;
382   gint x, y;
383   
384   g_return_if_fail (widget != NULL);
385   g_return_if_fail (GTK_IS_VSCALE (widget));
386   g_return_if_fail (allocation != NULL);
387   
388   widget->allocation = *allocation;
389   if (GTK_WIDGET_REALIZED (widget))
390     {
391       range = GTK_RANGE (widget);
392       scale = GTK_SCALE (widget);
393       
394       gtk_vscale_pos_trough (GTK_VSCALE (widget), &x, &y, &width, &height);
395       
396       gdk_window_move_resize (range->trough, x, y, width, height);
397       _gtk_range_slider_update (GTK_RANGE (widget));
398     }
399 }
400
401 static void
402 gtk_vscale_pos_trough (GtkVScale *vscale,
403                        gint      *x,
404                        gint      *y,
405                        gint      *w,
406                        gint      *h)
407 {
408   GtkWidget *widget = GTK_WIDGET (vscale);
409   GtkScale *scale = GTK_SCALE (vscale);
410   gint value_width, value_height;
411   gint slider_width, trough_border;
412   
413   _gtk_range_get_props (GTK_RANGE (scale),
414                         &slider_width, &trough_border, NULL, NULL);
415     
416   *w = (slider_width + trough_border * 2);
417   *h = widget->allocation.height;
418   
419   if (scale->draw_value)
420     {
421       *x = 0;
422       *y = 0;
423       
424       gtk_scale_get_value_size (scale, &value_width, &value_height);
425
426       switch (scale->value_pos)
427         {
428         case GTK_POS_LEFT:
429           *x = (value_width + SCALE_CLASS (scale)->value_spacing +
430                 (widget->allocation.width - widget->requisition.width) / 2);
431           break;
432         case GTK_POS_RIGHT:
433           *x = (widget->allocation.width - widget->requisition.width) / 2;
434           break;
435         case GTK_POS_TOP:
436           *x = (widget->allocation.width - *w) / 2;
437           *y = value_height;
438           *h -= *y;
439           break;
440         case GTK_POS_BOTTOM:
441           *x = (widget->allocation.width - *w) / 2;
442           *h -= value_height;
443           break;
444         }
445     }
446   else
447     {
448       *x = (widget->allocation.width - *w) / 2;
449       *y = 0;
450     }
451   *y += 1;
452   *h -= 2;
453   
454   *x += widget->allocation.x;
455   *y += widget->allocation.y;
456 }
457
458 static void
459 gtk_vscale_pos_background (GtkVScale *vscale,
460                            gint      *x,
461                            gint      *y,
462                            gint      *w,
463                            gint      *h)
464 {
465   GtkWidget *widget;
466   GtkScale *scale;
467   gint slider_width, trough_border;
468   
469   gint tx, ty, twidth, theight;
470   
471   g_return_if_fail (vscale != NULL);
472   g_return_if_fail (GTK_IS_VSCALE (vscale));
473   g_return_if_fail ((x != NULL) && (y != NULL) && (w != NULL) && (h != NULL));
474   
475   gtk_vscale_pos_trough (vscale, &tx, &ty, &twidth, &theight);
476   
477   widget = GTK_WIDGET (vscale);
478   scale = GTK_SCALE (vscale);
479   
480   *x = widget->allocation.x;
481   *y = widget->allocation.y;
482   *w = widget->allocation.width;
483   *h = widget->allocation.height;
484   
485   switch (scale->value_pos)
486     {
487     case GTK_POS_LEFT:
488       *w -= twidth;
489       break;
490     case GTK_POS_RIGHT:
491       *x += twidth;
492       *w -= twidth;
493       break;
494     case GTK_POS_TOP:
495       *h -= theight;
496       break;
497     case GTK_POS_BOTTOM:
498       *y += theight;
499       *h -= theight;
500       break;
501     }
502   *w = MAX (*w, 0);
503   *h = MAX (*h, 0);
504 }
505
506 static void
507 gtk_vscale_draw_slider (GtkRange *range)
508 {
509   GtkStateType state_type;
510   
511   g_return_if_fail (range != NULL);
512   g_return_if_fail (GTK_IS_VSCALE (range));
513   
514   if (range->slider)
515     {
516       if ((range->in_child == RANGE_CLASS (range)->slider) ||
517           (range->click_child == RANGE_CLASS (range)->slider))
518         state_type = GTK_STATE_PRELIGHT;
519       else
520         state_type = GTK_STATE_NORMAL;
521       
522       gtk_paint_slider (GTK_WIDGET (range)->style, range->slider, state_type, 
523                         GTK_SHADOW_OUT, 
524                         NULL, GTK_WIDGET (range), "vscale",
525                         0, 0, -1, -1, 
526                         GTK_ORIENTATION_VERTICAL); 
527     }
528 }
529
530 static void
531 gtk_vscale_draw_value (GtkScale *scale)
532 {
533   GtkStateType state_type;
534   GtkWidget *widget;
535   gint width, height;
536   gint x, y;
537   
538   g_return_if_fail (scale != NULL);
539   g_return_if_fail (GTK_IS_VSCALE (scale));
540   
541   widget = GTK_WIDGET (scale);
542   
543   if (scale->draw_value)
544     {
545       PangoLayout *layout;
546       PangoRectangle logical_rect;
547       gchar *txt;
548       
549       txt = _gtk_scale_format_value (scale,
550                                      GTK_RANGE (scale)->adjustment->value);
551       
552       layout = gtk_widget_create_pango_layout (widget, txt);
553       g_free (txt);
554       
555       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
556
557       switch (scale->value_pos)
558         {
559         case GTK_POS_LEFT:
560           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, NULL);
561           gdk_window_get_position (GTK_RANGE (scale)->slider, NULL, &y);
562           gdk_window_get_size (GTK_RANGE (scale)->trough, &width, NULL);
563           gdk_window_get_size (GTK_RANGE (scale)->slider, NULL, &height);
564           
565           x -= SCALE_CLASS (scale)->value_spacing + logical_rect.width;
566           y += widget->allocation.y + (height - logical_rect.height) / 2 +
567                                        PANGO_ASCENT (logical_rect);
568           break;
569         case GTK_POS_RIGHT:
570           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, NULL);
571           gdk_window_get_position (GTK_RANGE (scale)->slider, NULL, &y);
572           gdk_window_get_size (GTK_RANGE (scale)->trough, &width, NULL);
573           gdk_window_get_size (GTK_RANGE (scale)->slider, NULL, &height);
574           
575           x += width + SCALE_CLASS (scale)->value_spacing;
576           y += widget->allocation.y + (height - logical_rect.height) / 2 +
577                                        PANGO_ASCENT (logical_rect);
578           break;
579         case GTK_POS_TOP:
580           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
581           gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
582           gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
583           
584           x += (width - logical_rect.width) / 2;
585           y -= PANGO_DESCENT (logical_rect);
586           break;
587         case GTK_POS_BOTTOM:
588           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
589           gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
590           gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
591           
592           x += (width - logical_rect.width) / 2;
593           y += height + PANGO_ASCENT (logical_rect);
594           break;
595         }
596       
597       state_type = GTK_STATE_NORMAL;
598       if (!GTK_WIDGET_IS_SENSITIVE (scale))
599         state_type = GTK_STATE_INSENSITIVE;
600
601       gtk_paint_layout (widget->style,
602                         widget->window,
603                         state_type,
604                         FALSE,
605                         NULL,
606                         widget,
607                         "vscale",
608                         x, y,
609                         layout);      
610
611       g_object_unref (G_OBJECT (layout));
612     }
613 }