]> Pileus Git - ~andy/gtk/blob - gtk/gtkvscrollbar.c
voc Pennington <hp@redhat.com>
[~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
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   GObjectClass   *gobject_class;
91   GtkWidgetClass *widget_class;
92   GtkRangeClass *range_class;
93   
94   gobject_class = G_OBJECT_CLASS (class);
95   widget_class = (GtkWidgetClass*) class;
96   range_class = (GtkRangeClass*) class;
97   
98   gobject_class->set_property = gtk_vscrollbar_set_property;
99   gobject_class->get_property = gtk_vscrollbar_get_property;
100   
101   widget_class->realize = gtk_vscrollbar_realize;
102   widget_class->size_request = gtk_vscrollbar_size_request;
103   widget_class->size_allocate = gtk_vscrollbar_size_allocate;
104   
105   range_class->draw_step_forw = gtk_vscrollbar_draw_step_forw;
106   range_class->draw_step_back = gtk_vscrollbar_draw_step_back;
107   range_class->slider_update = gtk_vscrollbar_slider_update;
108   range_class->trough_click = _gtk_range_default_vtrough_click;
109   range_class->motion = _gtk_range_default_vmotion;
110
111   g_object_class_install_property (gobject_class,
112                                    PROP_ADJUSTMENT,
113                                    g_param_spec_object ("adjustment",
114                                                         _("Adjustment"),
115                                                         _("The GtkAdjustment that determines the values to use for this scrollbar."),
116                                                         GTK_TYPE_ADJUSTMENT,
117                                                         G_PARAM_READWRITE));
118
119 }
120
121 static void
122 gtk_vscrollbar_set_property (GObject         *object,
123                              guint            prop_id,
124                              const GValue    *value,
125                              GParamSpec      *pspec)
126 {
127   GtkVScrollbar *vscrollbar;
128   
129   vscrollbar = GTK_VSCROLLBAR (object);
130   
131   switch (prop_id)
132     {
133     case PROP_ADJUSTMENT:
134       gtk_range_set_adjustment (GTK_RANGE (vscrollbar), g_value_get_object (value));
135       break;
136     default:
137       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
138       break;
139     }
140 }
141
142 static void
143 gtk_vscrollbar_get_property (GObject         *object,
144                              guint            prop_id,
145                              GValue          *value,
146                              GParamSpec      *pspec)
147 {
148   GtkVScrollbar *vscrollbar;
149   
150   vscrollbar = GTK_VSCROLLBAR (object);
151   
152   switch (prop_id)
153     {
154     case PROP_ADJUSTMENT:
155       g_value_set_object (value, G_OBJECT (vscrollbar));
156       break;
157     default:
158       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
159       break;
160     }
161 }
162
163 static void
164 gtk_vscrollbar_init (GtkVScrollbar *vscrollbar)
165 {
166 }
167
168 GtkWidget*
169 gtk_vscrollbar_new (GtkAdjustment *adjustment)
170 {
171   GtkWidget *vscrollbar;
172   
173   vscrollbar = gtk_widget_new (GTK_TYPE_VSCROLLBAR,
174                                "adjustment", adjustment,
175                                NULL);
176   
177   return vscrollbar;
178 }
179
180
181 static void
182 gtk_vscrollbar_realize (GtkWidget *widget)
183 {
184   GtkRange *range;
185   GdkWindowAttr attributes;
186   gint attributes_mask;
187   gint slider_width;
188   gint stepper_size;
189   gint trough_border;
190   
191   g_return_if_fail (widget != NULL);
192   g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
193   
194   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
195   range = GTK_RANGE (widget);
196   
197   _gtk_range_get_props (range, &slider_width, &trough_border,
198                         &stepper_size, NULL);
199
200   attributes.x = widget->allocation.x + (widget->allocation.width - widget->requisition.width) / 2;
201   attributes.y = widget->allocation.y;
202   attributes.width = widget->requisition.width;
203   attributes.height = widget->allocation.height;
204   attributes.wclass = GDK_INPUT_OUTPUT;
205   attributes.window_type = GDK_WINDOW_CHILD;
206   attributes.visual = gtk_widget_get_visual (widget);
207   attributes.colormap = gtk_widget_get_colormap (widget);
208   attributes.event_mask = gtk_widget_get_events (widget);
209   attributes.event_mask |= (GDK_EXPOSURE_MASK |
210                             GDK_BUTTON_PRESS_MASK |
211                             GDK_BUTTON_RELEASE_MASK |
212                             GDK_ENTER_NOTIFY_MASK |
213                             GDK_LEAVE_NOTIFY_MASK);
214   
215   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
216   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
217   
218   range->trough = widget->window;
219   gdk_window_ref (range->trough);
220   
221   attributes.x = trough_border;
222   attributes.y = trough_border;
223   attributes.width = stepper_size;
224   attributes.height = stepper_size;
225   
226   range->step_back = gdk_window_new (range->trough, &attributes, attributes_mask);
227   
228   attributes.y = (widget->allocation.height -
229                   trough_border -
230                   stepper_size);
231   
232   range->step_forw = gdk_window_new (range->trough, &attributes, attributes_mask);
233   
234   attributes.x = trough_border;
235   attributes.y = 0;
236   attributes.width = slider_width;
237   attributes.height = RANGE_CLASS (widget)->min_slider_size;
238   attributes.event_mask |= (GDK_BUTTON_MOTION_MASK |
239                             GDK_POINTER_MOTION_HINT_MASK);
240   
241   range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
242   
243   gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (widget));
244   _gtk_range_slider_update (GTK_RANGE (widget));
245   
246   widget->style = gtk_style_attach (widget->style, widget->window);
247   
248   gdk_window_set_user_data (range->trough, widget);
249   gdk_window_set_user_data (range->slider, widget);
250   gdk_window_set_user_data (range->step_forw, widget);
251   gdk_window_set_user_data (range->step_back, widget);
252   
253   gtk_style_set_background (widget->style, range->trough, GTK_STATE_ACTIVE);
254   gtk_style_set_background (widget->style, range->slider, GTK_STATE_NORMAL);
255   gtk_style_set_background (widget->style, range->step_forw, GTK_STATE_ACTIVE);
256   gtk_style_set_background (widget->style, range->step_back, GTK_STATE_ACTIVE);
257   
258   gdk_window_show (range->slider);
259   gdk_window_show (range->step_forw);
260   gdk_window_show (range->step_back);
261 }
262
263 static void
264 gtk_vscrollbar_size_request (GtkWidget      *widget,
265                              GtkRequisition *requisition)
266 {
267   gint slider_width;
268   gint trough_border;
269   gint stepper_size;
270   gint stepper_spacing;
271   
272   GtkRange *range = GTK_RANGE (widget);
273
274   _gtk_range_get_props (range, &slider_width, &trough_border,
275                         &stepper_size, &stepper_spacing);
276   
277   requisition->width = (slider_width +
278                         trough_border * 2);
279   requisition->height = (RANGE_CLASS (widget)->min_slider_size +
280                          stepper_size +
281                          stepper_spacing +
282                          trough_border) * 2;
283 }
284
285 static void
286 gtk_vscrollbar_size_allocate (GtkWidget     *widget,
287                               GtkAllocation *allocation)
288 {
289   GtkRange *range;
290   gint trough_border;
291   gint stepper_size;
292   
293   g_return_if_fail (widget != NULL);
294   g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
295   g_return_if_fail (allocation != NULL);
296   
297   widget->allocation = *allocation;
298   if (GTK_WIDGET_REALIZED (widget))
299     {
300       range = GTK_RANGE (widget);
301       
302       _gtk_range_get_props (range, NULL, &trough_border, 
303                             &stepper_size, NULL);
304   
305       gdk_window_move_resize (range->trough,
306                               allocation->x + (allocation->width - widget->requisition.width) / 2,
307                               allocation->y,
308                               widget->requisition.width, allocation->height);
309       gdk_window_move_resize (range->step_back,
310                               trough_border,
311                               trough_border,
312                               widget->requisition.width - trough_border * 2,
313                               stepper_size);
314       gdk_window_move_resize (range->step_forw,
315                               trough_border,
316                               allocation->height - trough_border -
317                               stepper_size,
318                               widget->requisition.width - trough_border * 2,
319                               stepper_size);
320       
321       _gtk_range_slider_update (GTK_RANGE (widget));
322     }
323 }
324
325 static void
326 gtk_vscrollbar_draw_step_forw (GtkRange *range)
327 {
328   GtkStateType state_type;
329   GtkShadowType shadow_type;
330   
331   g_return_if_fail (range != NULL);
332   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
333   
334   if (GTK_WIDGET_DRAWABLE (range))
335     {
336       if (range->in_child == RANGE_CLASS (range)->step_forw)
337         {
338           if (range->click_child == RANGE_CLASS (range)->step_forw)
339             state_type = GTK_STATE_ACTIVE;
340           else
341             state_type = GTK_STATE_PRELIGHT;
342         }
343       else
344         state_type = GTK_STATE_NORMAL;
345       
346       if (range->click_child == RANGE_CLASS (range)->step_forw)
347         shadow_type = GTK_SHADOW_IN;
348       else
349         shadow_type = GTK_SHADOW_OUT;
350       
351       gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_forw,
352                        state_type, shadow_type, 
353                        NULL, GTK_WIDGET (range), "vscrollbar",
354                        GTK_ARROW_DOWN,
355                        TRUE, 0, 0, -1, -1);
356     }
357 }
358
359 static void
360 gtk_vscrollbar_draw_step_back (GtkRange *range)
361 {
362   GtkStateType state_type;
363   GtkShadowType shadow_type;
364   
365   g_return_if_fail (range != NULL);
366   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
367   
368   if (GTK_WIDGET_DRAWABLE (range))
369     {
370       if (range->in_child == RANGE_CLASS (range)->step_back)
371         {
372           if (range->click_child == RANGE_CLASS (range)->step_back)
373             state_type = GTK_STATE_ACTIVE;
374           else
375             state_type = GTK_STATE_PRELIGHT;
376         }
377       else
378         state_type = GTK_STATE_NORMAL;
379       
380       if (range->click_child == RANGE_CLASS (range)->step_back)
381         shadow_type = GTK_SHADOW_IN;
382       else
383         shadow_type = GTK_SHADOW_OUT;
384       
385       gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_back,
386                        state_type, shadow_type, 
387                        NULL, GTK_WIDGET (range), "vscrollbar",
388                        GTK_ARROW_UP,
389                        TRUE, 0, 0, -1, -1);
390     }
391 }
392
393 static void
394 gtk_vscrollbar_slider_update (GtkRange *range)
395 {
396   g_return_if_fail (range != NULL);
397   g_return_if_fail (GTK_IS_VSCROLLBAR (range));
398   
399   gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (range));
400   _gtk_range_default_vslider_update (range);
401 }
402
403 static void
404 gtk_vscrollbar_calc_slider_size (GtkVScrollbar *vscrollbar)
405 {
406   GtkRange *range;
407   gint step_back_y;
408   gint step_back_height;
409   gint step_forw_y;
410   gint stepper_spacing;
411   gint slider_width;
412   gint slider_height;
413   gint top, bottom;
414   gint height;
415   
416   g_return_if_fail (vscrollbar != NULL);
417   g_return_if_fail (GTK_IS_VSCROLLBAR (vscrollbar));
418   
419   if (GTK_WIDGET_REALIZED (vscrollbar))
420     {
421       range = GTK_RANGE (vscrollbar);
422       
423       _gtk_range_get_props (range, NULL, NULL, NULL, &stepper_spacing);
424       
425       gdk_window_get_size (range->step_back, NULL, &step_back_height);
426       gdk_window_get_position (range->step_back, NULL, &step_back_y);
427       gdk_window_get_position (range->step_forw, NULL, &step_forw_y);
428       
429       top = (step_back_y +
430              step_back_height +
431              stepper_spacing);
432       bottom = step_forw_y - stepper_spacing;
433       height = bottom - top;
434       
435       if ((range->adjustment->page_size > 0) &&
436           (range->adjustment->lower != range->adjustment->upper))
437         {
438           if (range->adjustment->page_size >
439               (range->adjustment->upper - range->adjustment->lower))
440             range->adjustment->page_size = range->adjustment->upper - range->adjustment->lower;
441           
442           height = (height * range->adjustment->page_size /
443                     (range->adjustment->upper - range->adjustment->lower));
444           
445           if (height < RANGE_CLASS (vscrollbar)->min_slider_size)
446             height = RANGE_CLASS (vscrollbar)->min_slider_size;
447         }
448       
449       gdk_window_get_size (range->slider, &slider_width, &slider_height);
450       
451       if (slider_height != height)
452         {
453           gdk_window_resize (range->slider, slider_width, height);
454           gdk_window_invalidate_rect (range->slider, NULL, FALSE);
455         }
456     }
457 }