]> Pileus Git - ~andy/gtk/blob - gtk/gtkvscale.c
cacf96426bd8797b745f878a9197b07ebeb7f59f
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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 #include <stdio.h>
20 #include "gtkvscale.h"
21 #include "gtksignal.h"
22 #include "gdk/gdkkeysyms.h"
23
24
25 #define SCALE_CLASS(w)  GTK_SCALE_CLASS (GTK_OBJECT (w)->klass)
26 #define RANGE_CLASS(w)  GTK_RANGE_CLASS (GTK_OBJECT (w)->klass)
27
28 enum {
29   ARG_0,
30   ARG_ADJUSTMENT
31 };
32
33 static void gtk_vscale_class_init    (GtkVScaleClass *klass);
34 static void gtk_vscale_init          (GtkVScale      *vscale);
35 static void gtk_vscale_set_arg       (GtkObject      *object,
36                                       GtkArg         *arg,
37                                       guint           arg_id);
38 static void gtk_vscale_get_arg       (GtkObject      *object,
39                                       GtkArg         *arg,
40                                       guint           arg_id);
41 static void gtk_vscale_realize       (GtkWidget      *widget);
42 static void gtk_vscale_size_request  (GtkWidget      *widget,
43                                       GtkRequisition *requisition);
44 static void gtk_vscale_size_allocate (GtkWidget      *widget,
45                                       GtkAllocation  *allocation);
46 static void gtk_vscale_pos_trough    (GtkVScale      *vscale,
47                                       gint           *x,
48                                       gint           *y,
49                                       gint           *w,
50                                       gint           *h);
51 static void gtk_vscale_pos_background (GtkVScale     *vscale,
52                                        gint          *x,
53                                        gint          *y,
54                                        gint          *w,
55                                        gint          *h);
56 static void gtk_vscale_draw_slider   (GtkRange       *range);
57 static void gtk_vscale_draw_value    (GtkScale       *scale);
58 static void gtk_vscale_draw          (GtkWidget      *widget,
59                                       GdkRectangle   *area);
60 static gint gtk_vscale_trough_keys   (GtkRange *range,
61                                       GdkEventKey *key,
62                                       GtkScrollType *scroll,
63                                       GtkTroughType *pos);
64 static void gtk_vscale_clear_background (GtkRange    *range);
65
66 GtkType
67 gtk_vscale_get_type (void)
68 {
69   static GtkType vscale_type = 0;
70   
71   if (!vscale_type)
72     {
73       static const GtkTypeInfo vscale_info =
74       {
75         "GtkVScale",
76         sizeof (GtkVScale),
77         sizeof (GtkVScaleClass),
78         (GtkClassInitFunc) gtk_vscale_class_init,
79         (GtkObjectInitFunc) gtk_vscale_init,
80         /* reserved_1 */ NULL,
81         /* reserved_2 */ NULL,
82         (GtkClassInitFunc) NULL,
83       };
84       
85       vscale_type = gtk_type_unique (GTK_TYPE_SCALE, &vscale_info);
86     }
87   
88   return vscale_type;
89 }
90
91 static void
92 gtk_vscale_class_init (GtkVScaleClass *class)
93 {
94   GtkObjectClass *object_class;
95   GtkWidgetClass *widget_class;
96   GtkRangeClass *range_class;
97   GtkScaleClass *scale_class;
98   
99   object_class = (GtkObjectClass*) class;
100   widget_class = (GtkWidgetClass*) class;
101   range_class = (GtkRangeClass*) class;
102   scale_class = (GtkScaleClass*) class;
103   
104   gtk_object_add_arg_type ("GtkVScale::adjustment",
105                            GTK_TYPE_ADJUSTMENT,
106                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
107                            ARG_ADJUSTMENT);
108   
109   object_class->set_arg = gtk_vscale_set_arg;
110   object_class->get_arg = gtk_vscale_get_arg;
111   
112   widget_class->realize = gtk_vscale_realize;
113   widget_class->size_request = gtk_vscale_size_request;
114   widget_class->size_allocate = gtk_vscale_size_allocate;
115   widget_class->draw = gtk_vscale_draw;
116   
117   range_class->slider_update = gtk_range_default_vslider_update;
118   range_class->trough_click = gtk_range_default_vtrough_click;
119   range_class->motion = gtk_range_default_vmotion;
120   range_class->draw_slider = gtk_vscale_draw_slider;
121   range_class->trough_keys = gtk_vscale_trough_keys;
122   range_class->clear_background = gtk_vscale_clear_background;
123   
124   scale_class->draw_value = gtk_vscale_draw_value;
125 }
126
127 static void
128 gtk_vscale_set_arg (GtkObject          *object,
129                     GtkArg             *arg,
130                     guint               arg_id)
131 {
132   GtkVScale *vscale;
133   
134   vscale = GTK_VSCALE (object);
135   
136   switch (arg_id)
137     {
138     case ARG_ADJUSTMENT:
139       gtk_range_set_adjustment (GTK_RANGE (vscale), GTK_VALUE_POINTER (*arg));
140       break;
141     default:
142       break;
143     }
144 }
145
146 static void
147 gtk_vscale_get_arg (GtkObject          *object,
148                     GtkArg             *arg,
149                     guint               arg_id)
150 {
151   GtkVScale *vscale;
152   
153   vscale = GTK_VSCALE (object);
154   
155   switch (arg_id)
156     {
157     case ARG_ADJUSTMENT:
158       GTK_VALUE_POINTER (*arg) = GTK_RANGE (vscale);
159       break;
160     default:
161       arg->type = GTK_TYPE_INVALID;
162       break;
163     }
164 }
165
166 static void
167 gtk_vscale_init (GtkVScale *vscale)
168 {
169   GTK_WIDGET_SET_FLAGS (vscale, GTK_NO_WINDOW);
170 }
171
172 GtkWidget*
173 gtk_vscale_new (GtkAdjustment *adjustment)
174 {
175   GtkWidget *vscale;
176   
177   vscale = gtk_widget_new (GTK_TYPE_VSCALE,
178                            "adjustment", adjustment,
179                            NULL);
180   
181   return vscale;
182 }
183
184
185 static void
186 gtk_vscale_realize (GtkWidget *widget)
187 {
188   GtkRange *range;
189   GdkWindowAttr attributes;
190   gint attributes_mask;
191   gint x, y, w, h;
192   
193   g_return_if_fail (widget != NULL);
194   g_return_if_fail (GTK_IS_VSCALE (widget));
195   
196   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
197   range = GTK_RANGE (widget);
198   
199   widget->window = gtk_widget_get_parent_window (widget);
200   gdk_window_ref (widget->window);
201   
202   gtk_vscale_pos_trough (GTK_VSCALE (widget), &x, &y, &w, &h);
203   
204   attributes.x = x;
205   attributes.y = y;
206   attributes.width = w;
207   attributes.height = h;
208   attributes.wclass = GDK_INPUT_OUTPUT;
209   attributes.window_type = GDK_WINDOW_CHILD;
210          
211   attributes.event_mask = gtk_widget_get_events (widget) | 
212     (GDK_EXPOSURE_MASK |
213      GDK_BUTTON_PRESS_MASK |
214      GDK_BUTTON_RELEASE_MASK |
215      GDK_ENTER_NOTIFY_MASK |
216      GDK_LEAVE_NOTIFY_MASK);
217   attributes.visual = gtk_widget_get_visual (widget);
218   attributes.colormap = gtk_widget_get_colormap (widget);
219   
220   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
221   
222   range->trough = gdk_window_new (widget->window, &attributes, attributes_mask);
223   
224   attributes.width = RANGE_CLASS (range)->slider_width;
225   attributes.height = SCALE_CLASS (range)->slider_length;
226   attributes.event_mask |= (GDK_BUTTON_MOTION_MASK |
227                             GDK_POINTER_MOTION_HINT_MASK);
228   
229   range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
230   
231   widget->style = gtk_style_attach (widget->style, widget->window);
232   
233   gdk_window_set_user_data (range->trough, widget);
234   gdk_window_set_user_data (range->slider, widget);
235   
236   gtk_style_set_background (widget->style, range->trough, GTK_STATE_ACTIVE);
237   gtk_style_set_background (widget->style, range->slider, GTK_STATE_NORMAL);
238   
239   gtk_range_slider_update (GTK_RANGE (widget));
240   
241   gdk_window_show (range->slider);
242 }
243
244 static void
245 gtk_vscale_draw (GtkWidget    *widget,
246                  GdkRectangle *area)
247 {
248   GtkRange *range;
249   GdkRectangle tmp_area;
250   GdkRectangle child_area;
251   gint x, y, width, height;
252   
253   g_return_if_fail (widget != NULL);
254   g_return_if_fail (GTK_IS_RANGE (widget));
255   g_return_if_fail (area != NULL);
256   
257   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
258     {
259       range = GTK_RANGE (widget);
260       
261       gtk_vscale_pos_background (GTK_VSCALE (widget), &x, &y, &width, &height);
262       
263       tmp_area.x = x;
264       tmp_area.y = y;
265       tmp_area.width = width;
266       tmp_area.height = height;
267       
268       if (gdk_rectangle_intersect (area, &tmp_area, &child_area))
269         gtk_range_draw_background (range);
270       
271       gtk_vscale_pos_trough (GTK_VSCALE (widget), &x, &y, &width, &height);
272       
273       tmp_area.x = x;
274       tmp_area.y = y;
275       tmp_area.width = width;
276       tmp_area.height = height;
277       
278       if (gdk_rectangle_intersect (area, &tmp_area, &child_area))
279         {
280           gtk_range_draw_trough (range);
281           gtk_range_draw_slider (range);
282           gtk_range_draw_step_forw (range);
283           gtk_range_draw_step_back (range);
284         }
285     }
286 }
287
288 static void
289 gtk_vscale_clear_background (GtkRange    *range)
290 {
291   GtkWidget *widget;
292   GtkScale *scale;
293   gint x, y, width, height;
294   
295   g_return_if_fail (range != NULL);
296   g_return_if_fail (GTK_IS_SCALE (range));
297   
298   widget = GTK_WIDGET (range);
299   scale = GTK_SCALE (range);
300   
301   gtk_vscale_pos_background (GTK_VSCALE (widget), &x, &y, &width, &height);
302   
303   gtk_widget_queue_clear_area (GTK_WIDGET (range),
304                                x, y, width, height);
305 }
306
307 static void
308 gtk_vscale_size_request (GtkWidget      *widget,
309                          GtkRequisition *requisition)
310 {
311   GtkScale *scale;
312   gint value_width;
313   
314   g_return_if_fail (widget != NULL);
315   g_return_if_fail (GTK_IS_VSCALE (widget));
316   g_return_if_fail (requisition != NULL);
317   
318   scale = GTK_SCALE (widget);
319   
320   requisition->width = (RANGE_CLASS (scale)->slider_width +
321                         widget->style->klass->ythickness * 2);
322   requisition->height = (SCALE_CLASS (scale)->slider_length +
323                          widget->style->klass->xthickness) * 2;
324   
325   if (scale->draw_value)
326     {
327       value_width = gtk_scale_get_value_width (scale);
328       
329       if ((scale->value_pos == GTK_POS_LEFT) ||
330           (scale->value_pos == GTK_POS_RIGHT))
331         {
332           requisition->width += value_width + SCALE_CLASS (scale)->value_spacing;
333           if (requisition->height < (widget->style->font->ascent + widget->style->font->descent))
334             requisition->height = widget->style->font->ascent + widget->style->font->descent;
335         }
336       else if ((scale->value_pos == GTK_POS_TOP) ||
337                (scale->value_pos == GTK_POS_BOTTOM))
338         {
339           if (requisition->width < value_width)
340             requisition->width = value_width;
341           requisition->height += widget->style->font->ascent + widget->style->font->descent;
342         }
343     }
344 }
345
346 static void
347 gtk_vscale_size_allocate (GtkWidget     *widget,
348                           GtkAllocation *allocation)
349 {
350   GtkRange *range;
351   GtkScale *scale;
352   gint width, height;
353   gint x, y;
354   
355   g_return_if_fail (widget != NULL);
356   g_return_if_fail (GTK_IS_VSCALE (widget));
357   g_return_if_fail (allocation != NULL);
358   
359   widget->allocation = *allocation;
360   if (GTK_WIDGET_REALIZED (widget))
361     {
362       range = GTK_RANGE (widget);
363       scale = GTK_SCALE (widget);
364       
365       gtk_vscale_pos_trough (GTK_VSCALE (widget), &x, &y, &width, &height);
366       
367       gdk_window_move_resize (range->trough, x, y, width, height);
368       gtk_range_slider_update (GTK_RANGE (widget));
369     }
370 }
371
372 static void
373 gtk_vscale_pos_trough (GtkVScale *vscale,
374                        gint      *x,
375                        gint      *y,
376                        gint      *w,
377                        gint      *h)
378 {
379   GtkWidget *widget;
380   GtkScale *scale;
381   
382   g_return_if_fail (vscale != NULL);
383   g_return_if_fail (GTK_IS_VSCALE (vscale));
384   g_return_if_fail ((x != NULL) && (y != NULL) && (w != NULL) && (h != NULL));
385   
386   widget = GTK_WIDGET (vscale);
387   scale = GTK_SCALE (vscale);
388   
389   *w = (RANGE_CLASS (scale)->slider_width +
390         widget->style->klass->xthickness * 2);
391   *h = widget->allocation.height;
392   
393   if (scale->draw_value)
394     {
395       *x = 0;
396       *y = 0;
397       
398       switch (scale->value_pos)
399         {
400         case GTK_POS_LEFT:
401           *x = (gtk_scale_get_value_width (scale) +
402                 (widget->allocation.width - widget->requisition.width) / 2);
403           break;
404         case GTK_POS_RIGHT:
405           *x = (widget->allocation.width - widget->requisition.width) / 2;
406           break;
407         case GTK_POS_TOP:
408           *x = (widget->allocation.width - *w) / 2;
409           *y = widget->style->font->ascent + widget->style->font->descent;
410           *h -= *y;
411           break;
412         case GTK_POS_BOTTOM:
413           *x = (widget->allocation.width - *w) / 2;
414           *h -= widget->style->font->ascent + widget->style->font->descent;
415           break;
416         }
417     }
418   else
419     {
420       *x = (widget->allocation.width - *w) / 2;
421       *y = 0;
422     }
423   *y += 1;
424   *h -= 2;
425   
426   *x += widget->allocation.x;
427   *y += widget->allocation.y;
428 }
429
430 static void
431 gtk_vscale_pos_background (GtkVScale *vscale,
432                            gint      *x,
433                            gint      *y,
434                            gint      *w,
435                            gint      *h)
436 {
437   GtkWidget *widget;
438   GtkScale *scale;
439   
440   gint tx, ty, twidth, theight;
441   
442   g_return_if_fail (vscale != NULL);
443   g_return_if_fail (GTK_IS_VSCALE (vscale));
444   g_return_if_fail ((x != NULL) && (y != NULL) && (w != NULL) && (h != NULL));
445   
446   gtk_vscale_pos_trough (vscale, &tx, &ty, &twidth, &theight);
447   
448   widget = GTK_WIDGET (vscale);
449   scale = GTK_SCALE (vscale);
450   
451   *x = widget->allocation.x;
452   *y = widget->allocation.y;
453   *w = widget->allocation.width;
454   *h = widget->allocation.height;
455   
456   switch (scale->value_pos)
457     {
458     case GTK_POS_LEFT:
459       *w -= twidth;
460       break;
461     case GTK_POS_RIGHT:
462       *x = tx;
463       *w -= twidth;
464       break;
465     case GTK_POS_TOP:
466       *h -= theight;
467       break;
468     case GTK_POS_BOTTOM:
469       *y = ty;
470       *h -= theight;
471       break;
472     }
473 }
474
475 static void
476 gtk_vscale_draw_slider (GtkRange *range)
477 {
478   GtkStateType state_type;
479   
480   g_return_if_fail (range != NULL);
481   g_return_if_fail (GTK_IS_VSCALE (range));
482   
483   if (range->slider)
484     {
485       if ((range->in_child == RANGE_CLASS (range)->slider) ||
486           (range->click_child == RANGE_CLASS (range)->slider))
487         state_type = GTK_STATE_PRELIGHT;
488       else
489         state_type = GTK_STATE_NORMAL;
490       
491       gtk_paint_slider (GTK_WIDGET (range)->style, range->slider, state_type, 
492                         GTK_SHADOW_OUT, 
493                         NULL, GTK_WIDGET (range), "vscale",
494                         0, 0, -1, -1, 
495                         GTK_ORIENTATION_VERTICAL); 
496     }
497 }
498
499 static void
500 gtk_vscale_draw_value (GtkScale *scale)
501 {
502   GtkStateType state_type;
503   GtkWidget *widget;
504   gchar buffer[32];
505   gint text_width;
506   gint width, height;
507   gint x, y;
508   
509   g_return_if_fail (scale != NULL);
510   g_return_if_fail (GTK_IS_VSCALE (scale));
511   
512   widget = GTK_WIDGET (scale);
513   
514   if (scale->draw_value)
515     {
516       sprintf (buffer, "%0.*f", GTK_RANGE (scale)->digits, GTK_RANGE (scale)->adjustment->value);
517       text_width = gdk_string_measure (GTK_WIDGET (scale)->style->font, buffer);
518       
519       switch (scale->value_pos)
520         {
521         case GTK_POS_LEFT:
522           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, NULL);
523           gdk_window_get_position (GTK_RANGE (scale)->slider, NULL, &y);
524           gdk_window_get_size (GTK_RANGE (scale)->trough, &width, NULL);
525           gdk_window_get_size (GTK_RANGE (scale)->slider, NULL, &height);
526           
527           x -= SCALE_CLASS (scale)->value_spacing + text_width;
528           y += widget->allocation.y + ((height -
529                                         (GTK_WIDGET (scale)->style->font->ascent +
530                                          GTK_WIDGET (scale)->style->font->descent)) / 2 +
531                                        GTK_WIDGET (scale)->style->font->ascent);
532           break;
533         case GTK_POS_RIGHT:
534           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, NULL);
535           gdk_window_get_position (GTK_RANGE (scale)->slider, NULL, &y);
536           gdk_window_get_size (GTK_RANGE (scale)->trough, &width, NULL);
537           gdk_window_get_size (GTK_RANGE (scale)->slider, NULL, &height);
538           
539           x += width + SCALE_CLASS (scale)->value_spacing;
540           y += widget->allocation.y + ((height -
541                                         (GTK_WIDGET (scale)->style->font->ascent +
542                                          GTK_WIDGET (scale)->style->font->descent)) / 2 +
543                                        GTK_WIDGET (scale)->style->font->ascent);
544           break;
545         case GTK_POS_TOP:
546           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
547           gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
548           gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
549           
550           x += (width - text_width) / 2;
551           y -= GTK_WIDGET (scale)->style->font->descent;
552           break;
553         case GTK_POS_BOTTOM:
554           gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
555           gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
556           gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
557           
558           x += (width - text_width) / 2;
559           y += height + GTK_WIDGET (scale)->style->font->ascent;
560           break;
561         }
562       
563       state_type = GTK_STATE_NORMAL;
564       if (!GTK_WIDGET_IS_SENSITIVE (scale))
565         state_type = GTK_STATE_INSENSITIVE;
566       
567       gtk_paint_string (GTK_WIDGET (scale)->style,
568                         GTK_WIDGET (scale)->window,
569                         state_type, 
570                         NULL, GTK_WIDGET (scale), "vscale",
571                         x, y, buffer);
572     }
573 }
574
575 static gint
576 gtk_vscale_trough_keys (GtkRange *range,
577                         GdkEventKey *key,
578                         GtkScrollType *scroll,
579                         GtkTroughType *pos)
580 {
581   gint return_val = FALSE;
582   switch (key->keyval)
583     {
584     case GDK_Up:
585       return_val = TRUE;
586       *scroll = GTK_SCROLL_STEP_BACKWARD;
587       break;
588     case GDK_Down:
589       return_val = TRUE;
590       *scroll = GTK_SCROLL_STEP_FORWARD;
591       break;
592     case GDK_Page_Up:
593       return_val = TRUE;
594       *scroll = GTK_SCROLL_PAGE_BACKWARD;
595       break;
596     case GDK_Page_Down:
597       return_val = TRUE;
598       *scroll = GTK_SCROLL_PAGE_FORWARD;
599       break;
600     case GDK_Home:
601       return_val = TRUE;
602       *pos = GTK_TROUGH_START;
603       break;
604     case GDK_End:
605       return_val = TRUE;
606       *pos = GTK_TROUGH_END;
607       break;
608     }
609   return return_val;
610 }