]> Pileus Git - ~andy/gtk/blob - gtk/gtkvscrollbar.c
4f9df1022af07048766b0693e698292062acf91d
[~andy/gtk] / gtk / gtkvscrollbar.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 "gtkvscrollbar.h"
28 #include "gtksignal.h"
29 #include "gdk/gdkkeysyms.h"
30 #include "gtkintl.h"
31
32
33 #define EPSILON 0.01
34
35 #define RANGE_CLASS(w)  GTK_RANGE_GET_CLASS (w)
36
37 enum {
38   PROP_0,
39   PROP_ADJUSTMENT
40 };
41
42 static void     gtk_vscrollbar_class_init       (GtkVScrollbarClass *klass);
43 static void     gtk_vscrollbar_init             (GtkVScrollbar      *vscrollbar);
44 static void     gtk_vscrollbar_set_property     (GObject            *object,
45                                                  guint               prop_id,
46                                                  const GValue       *value,
47                                                  GParamSpec         *pspec);
48 static void     gtk_vscrollbar_get_property     (GObject            *object,
49                                                  guint               prop_id,
50                                                  GValue             *value,
51                                                  GParamSpec         *pspec);
52 static void     gtk_vscrollbar_realize          (GtkWidget          *widget);
53 static void     gtk_vscrollbar_size_request     (GtkWidget          *widget,
54                                                  GtkRequisition     *requisition);
55 static void     gtk_vscrollbar_size_allocate    (GtkWidget          *widget,
56                                                  GtkAllocation      *allocation);
57 static void     gtk_vscrollbar_draw_step_forw   (GtkRange           *range);
58 static void     gtk_vscrollbar_draw_step_back   (GtkRange           *range);
59 static void     gtk_vscrollbar_slider_update    (GtkRange           *range);
60 static void     gtk_vscrollbar_calc_slider_size (GtkVScrollbar      *vscrollbar);
61 static gboolean gtk_vscrollbar_trough_keys      (GtkRange           *range,
62                                                  GdkEventKey        *key,
63                                                  GtkScrollType      *scroll,
64                                                  GtkTroughType      *pos);
65
66 GtkType
67 gtk_vscrollbar_get_type (void)
68 {
69   static GtkType vscrollbar_type = 0;
70   
71   if (!vscrollbar_type)
72     {
73       static const GtkTypeInfo vscrollbar_info =
74       {
75         "GtkVScrollbar",
76         sizeof (GtkVScrollbar),
77         sizeof (GtkVScrollbarClass),
78         (GtkClassInitFunc) gtk_vscrollbar_class_init,
79         (GtkObjectInitFunc) gtk_vscrollbar_init,
80         /* reserved_1 */ NULL,
81         /* reserved_2 */ NULL,
82         (GtkClassInitFunc) NULL,
83       };
84       
85       vscrollbar_type = gtk_type_unique (GTK_TYPE_SCROLLBAR, &vscrollbar_info);
86     }
87   
88   return vscrollbar_type;
89 }
90
91 static void
92 gtk_vscrollbar_class_init (GtkVScrollbarClass *class)
93 {
94   GObjectClass   *gobject_class;
95   GtkWidgetClass *widget_class;
96   GtkRangeClass *range_class;
97   
98   gobject_class = G_OBJECT_CLASS (class);
99   widget_class = (GtkWidgetClass*) class;
100   range_class = (GtkRangeClass*) class;
101   
102   gobject_class->set_property = gtk_vscrollbar_set_property;
103   gobject_class->get_property = gtk_vscrollbar_get_property;
104   
105   widget_class->realize = gtk_vscrollbar_realize;
106   widget_class->size_request = gtk_vscrollbar_size_request;
107   widget_class->size_allocate = gtk_vscrollbar_size_allocate;
108   
109   range_class->draw_step_forw = gtk_vscrollbar_draw_step_forw;
110   range_class->draw_step_back = gtk_vscrollbar_draw_step_back;
111   range_class->slider_update = gtk_vscrollbar_slider_update;
112   range_class->trough_click = _gtk_range_default_vtrough_click;
113   range_class->trough_keys = gtk_vscrollbar_trough_keys;
114   range_class->motion = _gtk_range_default_vmotion;
115
116   g_object_class_install_property (gobject_class,
117                                    PROP_ADJUSTMENT,
118                                    g_param_spec_object ("adjustment",
119                                                         _("Adjustment"),
120                                                         _("The GtkAdjustment that determines the values to use for this scrollbar."),
121                                                         GTK_TYPE_ADJUSTMENT,
122                                                         G_PARAM_READWRITE));
123
124 }
125
126 static void
127 gtk_vscrollbar_set_property (GObject         *object,
128                              guint            prop_id,
129                              const GValue    *value,
130                              GParamSpec      *pspec)
131 {
132   GtkVScrollbar *vscrollbar;
133   
134   vscrollbar = GTK_VSCROLLBAR (object);
135   
136   switch (prop_id)
137     {
138     case PROP_ADJUSTMENT:
139       gtk_range_set_adjustment (GTK_RANGE (vscrollbar), g_value_get_object (value));
140       break;
141     default:
142       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
143       break;
144     }
145 }
146
147 static void
148 gtk_vscrollbar_get_property (GObject         *object,
149                              guint            prop_id,
150                              GValue          *value,
151                              GParamSpec      *pspec)
152 {
153   GtkVScrollbar *vscrollbar;
154   
155   vscrollbar = GTK_VSCROLLBAR (object);
156   
157   switch (prop_id)
158     {
159     case PROP_ADJUSTMENT:
160       g_value_set_object (value, G_OBJECT (vscrollbar));
161       break;
162     default:
163       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
164       break;
165     }
166 }
167
168 static void
169 gtk_vscrollbar_init (GtkVScrollbar *vscrollbar)
170 {
171 }
172
173 GtkWidget*
174 gtk_vscrollbar_new (GtkAdjustment *adjustment)
175 {
176   GtkWidget *vscrollbar;
177   
178   vscrollbar = gtk_widget_new (GTK_TYPE_VSCROLLBAR,
179                                "adjustment", adjustment,
180                                NULL);
181   
182   return vscrollbar;
183 }
184
185
186 static void
187 gtk_vscrollbar_realize (GtkWidget *widget)
188 {
189   GtkRange *range;
190   GdkWindowAttr attributes;
191   gint attributes_mask;
192   gint slider_width;
193   gint stepper_size;
194   gint trough_border;
195   
196   g_return_if_fail (widget != NULL);
197   g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
198   
199   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
200   range = GTK_RANGE (widget);
201   
202   _gtk_range_get_props (range, &slider_width, &trough_border,
203                         &stepper_size, NULL);
204
205   attributes.x = widget->allocation.x + (widget->allocation.width - widget->requisition.width) / 2;
206   attributes.y = widget->allocation.y;
207   attributes.width = widget->requisition.width;
208   attributes.height = widget->allocation.height;
209   attributes.wclass = GDK_INPUT_OUTPUT;
210   attributes.window_type = GDK_WINDOW_CHILD;
211   attributes.visual = gtk_widget_get_visual (widget);
212   attributes.colormap = gtk_widget_get_colormap (widget);
213   attributes.event_mask = gtk_widget_get_events (widget);
214   attributes.event_mask |= (GDK_EXPOSURE_MASK |
215                             GDK_BUTTON_PRESS_MASK |
216                             GDK_BUTTON_RELEASE_MASK |
217                             GDK_ENTER_NOTIFY_MASK |
218                             GDK_LEAVE_NOTIFY_MASK);
219   
220   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
221   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
222   
223   range->trough = widget->window;
224   gdk_window_ref (range->trough);
225   
226   attributes.x = trough_border;
227   attributes.y = trough_border;
228   attributes.width = stepper_size;
229   attributes.height = stepper_size;
230   
231   range->step_back = gdk_window_new (range->trough, &attributes, attributes_mask);
232   
233   attributes.y = (widget->allocation.height -
234                   trough_border -
235                   stepper_size);
236   
237   range->step_forw = gdk_window_new (range->trough, &attributes, attributes_mask);
238   
239   attributes.x = trough_border;
240   attributes.y = 0;
241   attributes.width = slider_width;
242   attributes.height = RANGE_CLASS (widget)->min_slider_size;
243   attributes.event_mask |= (GDK_BUTTON_MOTION_MASK |
244                             GDK_POINTER_MOTION_HINT_MASK);
245   
246   range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
247   
248   gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (widget));
249   _gtk_range_slider_update (GTK_RANGE (widget));
250   
251   widget->style = gtk_style_attach (widget->style, widget->window);
252   
253   gdk_window_set_user_data (range->trough, widget);
254   gdk_window_set_user_data (range->slider, widget);
255   gdk_window_set_user_data (range->step_forw, widget);
256   gdk_window_set_user_data (range->step_back, widget);
257   
258   gtk_style_set_background (widget->style, range->trough, GTK_STATE_ACTIVE);
259   gtk_style_set_background (widget->style, range->slider, GTK_STATE_NORMAL);
260   gtk_style_set_background (widget->style, range->step_forw, GTK_STATE_ACTIVE);
261   gtk_style_set_background (widget->style, range->step_back, GTK_STATE_ACTIVE);
262   
263   gdk_window_show (range->slider);
264   gdk_window_show (range->step_forw);
265   gdk_window_show (range->step_back);
266 }
267
268 static void
269 gtk_vscrollbar_size_request (GtkWidget      *widget,
270                              GtkRequisition *requisition)
271 {
272   gint slider_width;
273   gint trough_border;
274   gint stepper_size;
275   gint stepper_spacing;
276   
277   GtkRange *range = GTK_RANGE (widget);
278
279   _gtk_range_get_props (range, &slider_width, &trough_border,
280                         &stepper_size, &stepper_spacing);
281   
282   requisition->width = (slider_width +
283                         trough_border * 2);
284   requisition->height = (RANGE_CLASS (widget)->min_slider_size +
285                          stepper_size +
286                          stepper_spacing +
287                          trough_border) * 2;
288 }
289
290 static void
291 gtk_vscrollbar_size_allocate (GtkWidget     *widget,
292                               GtkAllocation *allocation)
293 {
294   GtkRange *range;
295   gint trough_border;
296   gint stepper_size;
297   
298   g_return_if_fail (widget != NULL);
299   g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
300   g_return_if_fail (allocation != NULL);
301   
302   widget->allocation = *allocation;
303   if (GTK_WIDGET_REALIZED (widget))
304     {
305       range = GTK_RANGE (widget);
306       
307       _gtk_range_get_props (range, NULL, &trough_border, 
308                             &stepper_size, NULL);
309   
310       gdk_window_move_resize (range->trough,
311                               allocation->x + (allocation->width - widget->requisition.width) / 2,
312                               allocation->y,
313                               widget->requisition.width, allocation->height);
314       gdk_window_move_resize (range->step_back,
315                               trough_border,
316                               trough_border,
317                               widget->requisition.width - trough_border * 2,
318                               stepper_size);
319       gdk_window_move_resize (range->step_forw,
320                               trough_border,
321                               allocation->height - trough_border -
322                               stepper_size,
323                               widget->requisition.width - trough_border * 2,
324                               stepper_size);
325       
326       _gtk_range_slider_update (GTK_RANGE (widget));
327     }
328 }
329
330 static void
331 gtk_vscrollbar_draw_step_forw (GtkRange *range)
332 {
333   GtkStateType state_type;
334   GtkShadowType shadow_type;
335   
336   g_return_if_fail (range != NULL);
337   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
338   
339   if (GTK_WIDGET_DRAWABLE (range))
340     {
341       if (range->in_child == RANGE_CLASS (range)->step_forw)
342         {
343           if (range->click_child == RANGE_CLASS (range)->step_forw)
344             state_type = GTK_STATE_ACTIVE;
345           else
346             state_type = GTK_STATE_PRELIGHT;
347         }
348       else
349         state_type = GTK_STATE_NORMAL;
350       
351       if (range->click_child == RANGE_CLASS (range)->step_forw)
352         shadow_type = GTK_SHADOW_IN;
353       else
354         shadow_type = GTK_SHADOW_OUT;
355       
356       gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_forw,
357                        state_type, shadow_type, 
358                        NULL, GTK_WIDGET (range), "vscrollbar",
359                        GTK_ARROW_DOWN,
360                        TRUE, 0, 0, -1, -1);
361     }
362 }
363
364 static void
365 gtk_vscrollbar_draw_step_back (GtkRange *range)
366 {
367   GtkStateType state_type;
368   GtkShadowType shadow_type;
369   
370   g_return_if_fail (range != NULL);
371   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
372   
373   if (GTK_WIDGET_DRAWABLE (range))
374     {
375       if (range->in_child == RANGE_CLASS (range)->step_back)
376         {
377           if (range->click_child == RANGE_CLASS (range)->step_back)
378             state_type = GTK_STATE_ACTIVE;
379           else
380             state_type = GTK_STATE_PRELIGHT;
381         }
382       else
383         state_type = GTK_STATE_NORMAL;
384       
385       if (range->click_child == RANGE_CLASS (range)->step_back)
386         shadow_type = GTK_SHADOW_IN;
387       else
388         shadow_type = GTK_SHADOW_OUT;
389       
390       gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_back,
391                        state_type, shadow_type, 
392                        NULL, GTK_WIDGET (range), "vscrollbar",
393                        GTK_ARROW_UP,
394                        TRUE, 0, 0, -1, -1);
395     }
396 }
397
398 static void
399 gtk_vscrollbar_slider_update (GtkRange *range)
400 {
401   g_return_if_fail (range != NULL);
402   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
403   
404   gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (range));
405   _gtk_range_default_vslider_update (range);
406 }
407
408 static void
409 gtk_vscrollbar_calc_slider_size (GtkVScrollbar *vscrollbar)
410 {
411   GtkRange *range;
412   gint step_back_y;
413   gint step_back_height;
414   gint step_forw_y;
415   gint stepper_spacing;
416   gint slider_width;
417   gint slider_height;
418   gint top, bottom;
419   gint height;
420   
421   g_return_if_fail (vscrollbar != NULL);
422   g_return_if_fail (GTK_IS_VSCROLLBAR (vscrollbar));
423   
424   if (GTK_WIDGET_REALIZED (vscrollbar))
425     {
426       range = GTK_RANGE (vscrollbar);
427       
428       _gtk_range_get_props (range, NULL, NULL, NULL, &stepper_spacing);
429       
430       gdk_window_get_size (range->step_back, NULL, &step_back_height);
431       gdk_window_get_position (range->step_back, NULL, &step_back_y);
432       gdk_window_get_position (range->step_forw, NULL, &step_forw_y);
433       
434       top = (step_back_y +
435              step_back_height +
436              stepper_spacing);
437       bottom = step_forw_y - stepper_spacing;
438       height = bottom - top;
439       
440       if ((range->adjustment->page_size > 0) &&
441           (range->adjustment->lower != range->adjustment->upper))
442         {
443           if (range->adjustment->page_size >
444               (range->adjustment->upper - range->adjustment->lower))
445             range->adjustment->page_size = range->adjustment->upper - range->adjustment->lower;
446           
447           height = (height * range->adjustment->page_size /
448                     (range->adjustment->upper - range->adjustment->lower));
449           
450           if (height < RANGE_CLASS (vscrollbar)->min_slider_size)
451             height = RANGE_CLASS (vscrollbar)->min_slider_size;
452         }
453       
454       gdk_window_get_size (range->slider, &slider_width, &slider_height);
455       
456       if (slider_height != height)
457         {
458           gdk_window_resize (range->slider, slider_width, height);
459           gdk_window_invalidate_rect (range->slider, NULL, FALSE);
460         }
461     }
462 }
463
464 static gboolean
465 gtk_vscrollbar_trough_keys (GtkRange *range,
466                             GdkEventKey *key,
467                             GtkScrollType *scroll,
468                             GtkTroughType *pos)
469 {
470   gint return_val = FALSE;
471   switch (key->keyval)
472     {
473     case GDK_Up:
474       return_val = TRUE;
475       *scroll = GTK_SCROLL_STEP_UP;
476       break;
477     case GDK_Down:
478       return_val = TRUE;
479       *scroll = GTK_SCROLL_STEP_DOWN;
480       break;
481     case GDK_Page_Up:
482       return_val = TRUE;
483       if (key->state & GDK_CONTROL_MASK)
484         *pos = GTK_TROUGH_START;
485       else
486         *scroll = GTK_SCROLL_PAGE_BACKWARD;
487       break;
488     case GDK_Page_Down:
489       return_val = TRUE;
490       if (key->state & GDK_CONTROL_MASK)
491         *pos = GTK_TROUGH_END;
492       else
493         *scroll = GTK_SCROLL_PAGE_FORWARD;
494       break;
495     }
496   return return_val;
497 }