]> Pileus Git - ~andy/gtk/blob - gtk/gtkvscrollbar.c
applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[~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
31
32 #define EPSILON 0.01
33
34 #define RANGE_CLASS(w)  GTK_RANGE_GET_CLASS (w)
35
36 enum {
37   ARG_0,
38   ARG_ADJUSTMENT
39 };
40
41 static void gtk_vscrollbar_class_init       (GtkVScrollbarClass *klass);
42 static void gtk_vscrollbar_init             (GtkVScrollbar      *vscrollbar);
43 static void gtk_vscrollbar_set_arg          (GtkObject          *object,
44                                              GtkArg             *arg,
45                                              guint               arg_id);
46 static void gtk_vscrollbar_get_arg          (GtkObject          *object,
47                                              GtkArg             *arg,
48                                              guint               arg_id);
49 static void gtk_vscrollbar_realize          (GtkWidget          *widget);
50 static void gtk_vscrollbar_size_allocate    (GtkWidget          *widget,
51                                              GtkAllocation      *allocation);
52 static void gtk_vscrollbar_draw_step_forw   (GtkRange           *range);
53 static void gtk_vscrollbar_draw_step_back   (GtkRange           *range);
54 static void gtk_vscrollbar_slider_update    (GtkRange           *range);
55 static void gtk_vscrollbar_calc_slider_size (GtkVScrollbar      *vscrollbar);
56 static gint gtk_vscrollbar_trough_keys      (GtkRange *range,
57                                              GdkEventKey *key,
58                                              GtkScrollType *scroll,
59                                              GtkTroughType *pos);
60
61
62 GtkType
63 gtk_vscrollbar_get_type (void)
64 {
65   static GtkType vscrollbar_type = 0;
66   
67   if (!vscrollbar_type)
68     {
69       static const GtkTypeInfo vscrollbar_info =
70       {
71         "GtkVScrollbar",
72         sizeof (GtkVScrollbar),
73         sizeof (GtkVScrollbarClass),
74         (GtkClassInitFunc) gtk_vscrollbar_class_init,
75         (GtkObjectInitFunc) gtk_vscrollbar_init,
76         /* reserved_1 */ NULL,
77         /* reserved_2 */ NULL,
78         (GtkClassInitFunc) NULL,
79       };
80       
81       vscrollbar_type = gtk_type_unique (GTK_TYPE_SCROLLBAR, &vscrollbar_info);
82     }
83   
84   return vscrollbar_type;
85 }
86
87 static void
88 gtk_vscrollbar_class_init (GtkVScrollbarClass *class)
89 {
90   GtkObjectClass *object_class;
91   GtkWidgetClass *widget_class;
92   GtkRangeClass *range_class;
93   
94   object_class = (GtkObjectClass*) class;
95   widget_class = (GtkWidgetClass*) class;
96   range_class = (GtkRangeClass*) class;
97   
98   gtk_object_add_arg_type ("GtkVScrollbar::adjustment",
99                            GTK_TYPE_ADJUSTMENT,
100                            GTK_ARG_READWRITE,
101                            ARG_ADJUSTMENT);
102   
103   object_class->set_arg = gtk_vscrollbar_set_arg;
104   object_class->get_arg = gtk_vscrollbar_get_arg;
105   
106   widget_class->realize = gtk_vscrollbar_realize;
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
117 static void
118 gtk_vscrollbar_set_arg (GtkObject          *object,
119                         GtkArg             *arg,
120                         guint               arg_id)
121 {
122   GtkVScrollbar *vscrollbar;
123   
124   vscrollbar = GTK_VSCROLLBAR (object);
125   
126   switch (arg_id)
127     {
128     case ARG_ADJUSTMENT:
129       gtk_range_set_adjustment (GTK_RANGE (vscrollbar), GTK_VALUE_POINTER (*arg));
130       break;
131     default:
132       break;
133     }
134 }
135
136 static void
137 gtk_vscrollbar_get_arg (GtkObject          *object,
138                         GtkArg             *arg,
139                         guint               arg_id)
140 {
141   GtkVScrollbar *vscrollbar;
142   
143   vscrollbar = GTK_VSCROLLBAR (object);
144   
145   switch (arg_id)
146     {
147     case ARG_ADJUSTMENT:
148       GTK_VALUE_POINTER (*arg) = GTK_RANGE (vscrollbar);
149       break;
150     default:
151       arg->type = GTK_TYPE_INVALID;
152       break;
153     }
154 }
155
156 static void
157 gtk_vscrollbar_init (GtkVScrollbar *vscrollbar)
158 {
159   GtkWidget *widget;
160   GtkRequisition *requisition;
161   
162   widget = GTK_WIDGET (vscrollbar);
163   requisition = &widget->requisition;
164   
165   requisition->width = (RANGE_CLASS (widget)->slider_width +
166                         widget->style->xthickness * 2);
167   requisition->height = (RANGE_CLASS (widget)->min_slider_size +
168                          RANGE_CLASS (widget)->stepper_size +
169                          RANGE_CLASS (widget)->stepper_slider_spacing +
170                          widget->style->ythickness) * 2;
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   
193   g_return_if_fail (widget != NULL);
194   g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
195   
196   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
197   range = GTK_RANGE (widget);
198   
199   attributes.x = widget->allocation.x + (widget->allocation.width - widget->requisition.width) / 2;
200   attributes.y = widget->allocation.y;
201   attributes.width = widget->requisition.width;
202   attributes.height = widget->allocation.height;
203   attributes.wclass = GDK_INPUT_OUTPUT;
204   attributes.window_type = GDK_WINDOW_CHILD;
205   attributes.visual = gtk_widget_get_visual (widget);
206   attributes.colormap = gtk_widget_get_colormap (widget);
207   attributes.event_mask = gtk_widget_get_events (widget);
208   attributes.event_mask |= (GDK_EXPOSURE_MASK |
209                             GDK_BUTTON_PRESS_MASK |
210                             GDK_BUTTON_RELEASE_MASK |
211                             GDK_ENTER_NOTIFY_MASK |
212                             GDK_LEAVE_NOTIFY_MASK);
213   
214   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
215   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
216   
217   range->trough = widget->window;
218   gdk_window_ref (range->trough);
219   
220   attributes.x = widget->style->xthickness;
221   attributes.y = widget->style->ythickness;
222   attributes.width = RANGE_CLASS (widget)->stepper_size;
223   attributes.height = RANGE_CLASS (widget)->stepper_size;
224   
225   range->step_back = gdk_window_new (range->trough, &attributes, attributes_mask);
226   
227   attributes.y = (widget->allocation.height -
228                   widget->style->ythickness -
229                   RANGE_CLASS (widget)->stepper_size);
230   
231   range->step_forw = gdk_window_new (range->trough, &attributes, attributes_mask);
232   
233   attributes.x = widget->style->ythickness;
234   attributes.y = 0;
235   attributes.width = RANGE_CLASS (widget)->slider_width;
236   attributes.height = RANGE_CLASS (widget)->min_slider_size;
237   attributes.event_mask |= (GDK_BUTTON_MOTION_MASK |
238                             GDK_POINTER_MOTION_HINT_MASK);
239   
240   range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
241   
242   gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (widget));
243   gtk_range_slider_update (GTK_RANGE (widget));
244   
245   widget->style = gtk_style_attach (widget->style, widget->window);
246   
247   gdk_window_set_user_data (range->trough, widget);
248   gdk_window_set_user_data (range->slider, widget);
249   gdk_window_set_user_data (range->step_forw, widget);
250   gdk_window_set_user_data (range->step_back, widget);
251   
252   gtk_style_set_background (widget->style, range->trough, GTK_STATE_ACTIVE);
253   gtk_style_set_background (widget->style, range->slider, GTK_STATE_NORMAL);
254   gtk_style_set_background (widget->style, range->step_forw, GTK_STATE_ACTIVE);
255   gtk_style_set_background (widget->style, range->step_back, GTK_STATE_ACTIVE);
256   
257   gdk_window_show (range->slider);
258   gdk_window_show (range->step_forw);
259   gdk_window_show (range->step_back);
260 }
261
262 static void
263 gtk_vscrollbar_size_allocate (GtkWidget     *widget,
264                               GtkAllocation *allocation)
265 {
266   GtkRange *range;
267   
268   g_return_if_fail (widget != NULL);
269   g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
270   g_return_if_fail (allocation != NULL);
271   
272   widget->allocation = *allocation;
273   if (GTK_WIDGET_REALIZED (widget))
274     {
275       range = GTK_RANGE (widget);
276       
277       gdk_window_move_resize (range->trough,
278                               allocation->x + (allocation->width - widget->requisition.width) / 2,
279                               allocation->y,
280                               widget->requisition.width, allocation->height);
281       gdk_window_move_resize (range->step_back,
282                               widget->style->xthickness,
283                               widget->style->ythickness,
284                               widget->requisition.width - widget->style->xthickness * 2,
285                               RANGE_CLASS (widget)->stepper_size);
286       gdk_window_move_resize (range->step_forw,
287                               widget->style->xthickness,
288                               allocation->height - widget->style->ythickness -
289                               RANGE_CLASS (widget)->stepper_size,
290                               widget->requisition.width - widget->style->xthickness * 2,
291                               RANGE_CLASS (widget)->stepper_size);
292       
293       gtk_range_slider_update (GTK_RANGE (widget));
294     }
295 }
296
297 static void
298 gtk_vscrollbar_draw_step_forw (GtkRange *range)
299 {
300   GtkStateType state_type;
301   GtkShadowType shadow_type;
302   
303   g_return_if_fail (range != NULL);
304   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
305   
306   if (GTK_WIDGET_DRAWABLE (range))
307     {
308       if (range->in_child == RANGE_CLASS (range)->step_forw)
309         {
310           if (range->click_child == RANGE_CLASS (range)->step_forw)
311             state_type = GTK_STATE_ACTIVE;
312           else
313             state_type = GTK_STATE_PRELIGHT;
314         }
315       else
316         state_type = GTK_STATE_NORMAL;
317       
318       if (range->click_child == RANGE_CLASS (range)->step_forw)
319         shadow_type = GTK_SHADOW_IN;
320       else
321         shadow_type = GTK_SHADOW_OUT;
322       
323       gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_forw,
324                        state_type, shadow_type, 
325                        NULL, GTK_WIDGET (range), "vscrollbar",
326                        GTK_ARROW_DOWN,
327                        TRUE, 0, 0, -1, -1);
328     }
329 }
330
331 static void
332 gtk_vscrollbar_draw_step_back (GtkRange *range)
333 {
334   GtkStateType state_type;
335   GtkShadowType shadow_type;
336   
337   g_return_if_fail (range != NULL);
338   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
339   
340   if (GTK_WIDGET_DRAWABLE (range))
341     {
342       if (range->in_child == RANGE_CLASS (range)->step_back)
343         {
344           if (range->click_child == RANGE_CLASS (range)->step_back)
345             state_type = GTK_STATE_ACTIVE;
346           else
347             state_type = GTK_STATE_PRELIGHT;
348         }
349       else
350         state_type = GTK_STATE_NORMAL;
351       
352       if (range->click_child == RANGE_CLASS (range)->step_back)
353         shadow_type = GTK_SHADOW_IN;
354       else
355         shadow_type = GTK_SHADOW_OUT;
356       
357       gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_back,
358                        state_type, shadow_type, 
359                        NULL, GTK_WIDGET (range), "vscrollbar",
360                        GTK_ARROW_UP,
361                        TRUE, 0, 0, -1, -1);
362     }
363 }
364
365 static void
366 gtk_vscrollbar_slider_update (GtkRange *range)
367 {
368   g_return_if_fail (range != NULL);
369   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
370   
371   gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (range));
372   gtk_range_default_vslider_update (range);
373 }
374
375 static void
376 gtk_vscrollbar_calc_slider_size (GtkVScrollbar *vscrollbar)
377 {
378   GtkRange *range;
379   gint step_back_y;
380   gint step_back_height;
381   gint step_forw_y;
382   gint slider_width;
383   gint slider_height;
384   gint top, bottom;
385   gint height;
386   
387   g_return_if_fail (vscrollbar != NULL);
388   g_return_if_fail (GTK_IS_VSCROLLBAR (vscrollbar));
389   
390   if (GTK_WIDGET_REALIZED (vscrollbar))
391     {
392       range = GTK_RANGE (vscrollbar);
393       
394       gdk_window_get_size (range->step_back, NULL, &step_back_height);
395       gdk_window_get_position (range->step_back, NULL, &step_back_y);
396       gdk_window_get_position (range->step_forw, NULL, &step_forw_y);
397       
398       top = (step_back_y +
399              step_back_height +
400              RANGE_CLASS (vscrollbar)->stepper_slider_spacing);
401       bottom = step_forw_y - RANGE_CLASS (vscrollbar)->stepper_slider_spacing;
402       height = bottom - top;
403       
404       if ((range->adjustment->page_size > 0) &&
405           (range->adjustment->lower != range->adjustment->upper))
406         {
407           if (range->adjustment->page_size >
408               (range->adjustment->upper - range->adjustment->lower))
409             range->adjustment->page_size = range->adjustment->upper - range->adjustment->lower;
410           
411           height = (height * range->adjustment->page_size /
412                     (range->adjustment->upper - range->adjustment->lower));
413           
414           if (height < RANGE_CLASS (vscrollbar)->min_slider_size)
415             height = RANGE_CLASS (vscrollbar)->min_slider_size;
416         }
417       
418       gdk_window_get_size (range->slider, &slider_width, &slider_height);
419       
420       if (slider_height != height)
421         {
422           gdk_window_resize (range->slider, slider_width, height);
423           gdk_window_invalidate_rect (range->slider, NULL, FALSE);
424         }
425     }
426 }
427
428 static gint
429 gtk_vscrollbar_trough_keys(GtkRange *range,
430                            GdkEventKey *key,
431                            GtkScrollType *scroll,
432                            GtkTroughType *pos)
433 {
434   gint return_val = FALSE;
435   switch (key->keyval)
436     {
437     case GDK_Up:
438       return_val = TRUE;
439       *scroll = GTK_SCROLL_STEP_BACKWARD;
440       break;
441     case GDK_Down:
442       return_val = TRUE;
443       *scroll = GTK_SCROLL_STEP_FORWARD;
444       break;
445     case GDK_Page_Up:
446       return_val = TRUE;
447       if (key->state & GDK_CONTROL_MASK)
448         *pos = GTK_TROUGH_START;
449       else
450         *scroll = GTK_SCROLL_PAGE_BACKWARD;
451       break;
452     case GDK_Page_Down:
453       return_val = TRUE;
454       if (key->state & GDK_CONTROL_MASK)
455         *pos = GTK_TROUGH_END;
456       else
457         *scroll = GTK_SCROLL_PAGE_FORWARD;
458       break;
459     }
460   return return_val;
461 }