]> Pileus Git - ~andy/gtk/blob - gtk/gtkhscrollbar.c
Initial revision
[~andy/gtk] / gtk / gtkhscrollbar.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 Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "gtkhscrollbar.h"
19 #include "gtksignal.h"
20 #include "gdk/gdkkeysyms.h"
21
22
23 #define EPSILON 0.01
24
25 #define RANGE_CLASS(w)  GTK_RANGE_CLASS (GTK_OBJECT (w)->klass)
26
27
28 static void gtk_hscrollbar_class_init       (GtkHScrollbarClass *klass);
29 static void gtk_hscrollbar_init             (GtkHScrollbar      *hscrollbar);
30 static void gtk_hscrollbar_realize          (GtkWidget          *widget);
31 static void gtk_hscrollbar_size_allocate    (GtkWidget          *widget,
32                                              GtkAllocation      *allocation);
33 static void gtk_hscrollbar_draw_step_forw   (GtkRange           *range);
34 static void gtk_hscrollbar_draw_step_back   (GtkRange           *range);
35 static void gtk_hscrollbar_slider_update    (GtkRange           *range);
36 static void gtk_hscrollbar_calc_slider_size (GtkHScrollbar      *hscrollbar);
37 static gint gtk_hscrollbar_trough_keys      (GtkRange *range,
38                                              GdkEventKey *key,
39                                              GtkScrollType *scroll,
40                                              GtkTroughType *pos);
41
42
43 guint
44 gtk_hscrollbar_get_type ()
45 {
46   static guint hscrollbar_type = 0;
47
48   if (!hscrollbar_type)
49     {
50       GtkTypeInfo hscrollbar_info =
51       {
52         "GtkHScrollbar",
53         sizeof (GtkHScrollbar),
54         sizeof (GtkHScrollbarClass),
55         (GtkClassInitFunc) gtk_hscrollbar_class_init,
56         (GtkObjectInitFunc) gtk_hscrollbar_init,
57         (GtkArgFunc) NULL,
58       };
59
60       hscrollbar_type = gtk_type_unique (gtk_scrollbar_get_type (), &hscrollbar_info);
61     }
62
63   return hscrollbar_type;
64 }
65
66 static void
67 gtk_hscrollbar_class_init (GtkHScrollbarClass *class)
68 {
69   GtkWidgetClass *widget_class;
70   GtkRangeClass *range_class;
71
72   widget_class = (GtkWidgetClass*) class;
73   range_class = (GtkRangeClass*) class;
74
75   widget_class->realize = gtk_hscrollbar_realize;
76   widget_class->size_allocate = gtk_hscrollbar_size_allocate;
77
78   range_class->draw_step_forw = gtk_hscrollbar_draw_step_forw;
79   range_class->draw_step_back = gtk_hscrollbar_draw_step_back;
80   range_class->slider_update = gtk_hscrollbar_slider_update;
81   range_class->trough_click = gtk_range_default_htrough_click;
82   range_class->trough_keys = gtk_hscrollbar_trough_keys;
83   range_class->motion = gtk_range_default_hmotion;
84 }
85
86 static void
87 gtk_hscrollbar_init (GtkHScrollbar *hscrollbar)
88 {
89   GtkWidget *widget;
90   GtkRequisition *requisition;
91
92   widget = GTK_WIDGET (hscrollbar);
93   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
94   requisition = &widget->requisition;
95
96   requisition->width = (RANGE_CLASS (widget)->min_slider_size +
97                         RANGE_CLASS (widget)->stepper_size +
98                         RANGE_CLASS (widget)->stepper_slider_spacing +
99                         widget->style->klass->xthickness) * 2;
100   requisition->height = (RANGE_CLASS (widget)->slider_width +
101                          widget->style->klass->ythickness * 2);
102 }
103
104 GtkWidget*
105 gtk_hscrollbar_new (GtkAdjustment *adjustment)
106 {
107   GtkHScrollbar *hscrollbar;
108
109   hscrollbar = gtk_type_new (gtk_hscrollbar_get_type ());
110
111   if (!adjustment)
112     adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
113
114   gtk_range_set_adjustment (GTK_RANGE (hscrollbar), adjustment);
115
116   return GTK_WIDGET (hscrollbar);
117 }
118
119
120 static void
121 gtk_hscrollbar_realize (GtkWidget *widget)
122 {
123   GtkRange *range;
124   GdkWindowAttr attributes;
125   gint attributes_mask;
126
127   g_return_if_fail (widget != NULL);
128   g_return_if_fail (GTK_IS_HSCROLLBAR (widget));
129
130   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
131   range = GTK_RANGE (widget);
132
133   attributes.x = widget->allocation.x;
134   attributes.y = widget->allocation.y + (widget->allocation.height - widget->requisition.height) / 2;
135   attributes.width = widget->allocation.width;
136   attributes.height = widget->requisition.height;
137   attributes.wclass = GDK_INPUT_OUTPUT;
138   attributes.window_type = GDK_WINDOW_CHILD;
139   attributes.visual = gtk_widget_get_visual (widget);
140   attributes.colormap = gtk_widget_get_colormap (widget);
141   attributes.event_mask = gtk_widget_get_events (widget);
142   attributes.event_mask |= (GDK_EXPOSURE_MASK |
143                             GDK_BUTTON_PRESS_MASK |
144                             GDK_BUTTON_RELEASE_MASK |
145                             GDK_ENTER_NOTIFY_MASK |
146                             GDK_LEAVE_NOTIFY_MASK);
147
148   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
149   widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
150   range->trough = widget->window;
151
152   attributes.x = widget->style->klass->xthickness;
153   attributes.y = widget->style->klass->ythickness;
154   attributes.width = RANGE_CLASS (widget)->stepper_size;
155   attributes.height = RANGE_CLASS (widget)->stepper_size;
156
157   range->step_back = gdk_window_new (range->trough, &attributes, attributes_mask);
158
159   attributes.x = (widget->allocation.width -
160                   widget->style->klass->xthickness -
161                   RANGE_CLASS (widget)->stepper_size);
162
163   range->step_forw = gdk_window_new (range->trough, &attributes, attributes_mask);
164
165   attributes.x = 0;
166   attributes.y = widget->style->klass->ythickness;
167   attributes.width = RANGE_CLASS (widget)->min_slider_size;
168   attributes.height = RANGE_CLASS (widget)->slider_width;
169   attributes.event_mask |= (GDK_BUTTON_MOTION_MASK |
170                             GDK_POINTER_MOTION_HINT_MASK);
171
172   range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
173
174   gtk_hscrollbar_calc_slider_size (GTK_HSCROLLBAR (widget));
175   gtk_range_slider_update (GTK_RANGE (widget));
176
177   widget->style = gtk_style_attach (widget->style, widget->window);
178
179   gdk_window_set_user_data (range->trough, widget);
180   gdk_window_set_user_data (range->slider, widget);
181   gdk_window_set_user_data (range->step_forw, widget);
182   gdk_window_set_user_data (range->step_back, widget);
183
184   gtk_style_set_background (widget->style, range->trough, GTK_STATE_ACTIVE);
185   gtk_style_set_background (widget->style, range->slider, GTK_STATE_NORMAL);
186   gtk_style_set_background (widget->style, range->step_forw, GTK_STATE_ACTIVE);
187   gtk_style_set_background (widget->style, range->step_back, GTK_STATE_ACTIVE);
188
189   gdk_window_show (range->slider);
190   gdk_window_show (range->step_forw);
191   gdk_window_show (range->step_back);
192 }
193
194 static void
195 gtk_hscrollbar_size_allocate (GtkWidget     *widget,
196                               GtkAllocation *allocation)
197 {
198   GtkRange *range;
199
200   g_return_if_fail (widget != NULL);
201   g_return_if_fail (GTK_IS_HSCROLLBAR (widget));
202   g_return_if_fail (allocation != NULL);
203
204   widget->allocation = *allocation;
205   if (GTK_WIDGET_REALIZED (widget))
206     {
207       range = GTK_RANGE (widget);
208
209       gdk_window_move_resize (range->trough,
210                               allocation->x,
211                               allocation->y + (allocation->height - widget->requisition.height) / 2,
212                               allocation->width, widget->requisition.height);
213       gdk_window_move_resize (range->step_back,
214                               widget->style->klass->xthickness,
215                               widget->style->klass->ythickness,
216                               RANGE_CLASS (widget)->stepper_size,
217                               widget->requisition.height - widget->style->klass->ythickness * 2);
218       gdk_window_move_resize (range->step_forw,
219                               allocation->width - widget->style->klass->xthickness -
220                               RANGE_CLASS (widget)->stepper_size,
221                               widget->style->klass->ythickness,
222                               RANGE_CLASS (widget)->stepper_size,
223                               widget->requisition.height - widget->style->klass->ythickness * 2);
224       gdk_window_resize (range->slider,
225                          RANGE_CLASS (widget)->min_slider_size,
226                          widget->requisition.height - widget->style->klass->ythickness * 2);
227
228       gtk_range_slider_update (GTK_RANGE (widget));
229     }
230 }
231
232 static void
233 gtk_hscrollbar_draw_step_forw (GtkRange *range)
234 {
235   GtkStateType state_type;
236   GtkShadowType shadow_type;
237
238   g_return_if_fail (range != NULL);
239   g_return_if_fail (GTK_IS_HSCROLLBAR (range));
240
241   if (GTK_WIDGET_DRAWABLE (range))
242     {
243       if (range->in_child == RANGE_CLASS (range)->step_forw)
244         {
245           if (range->click_child == RANGE_CLASS (range)->step_forw)
246             state_type = GTK_STATE_ACTIVE;
247           else
248             state_type = GTK_STATE_PRELIGHT;
249         }
250       else
251         state_type = GTK_STATE_NORMAL;
252
253       if (range->click_child == RANGE_CLASS (range)->step_forw)
254         shadow_type = GTK_SHADOW_IN;
255       else
256         shadow_type = GTK_SHADOW_OUT;
257
258       gtk_draw_arrow (GTK_WIDGET (range)->style, range->step_forw,
259                       state_type, shadow_type, GTK_ARROW_RIGHT,
260                       TRUE, 0, 0, -1, -1);
261     }
262 }
263
264 static void
265 gtk_hscrollbar_draw_step_back (GtkRange *range)
266 {
267   GtkStateType state_type;
268   GtkShadowType shadow_type;
269
270   g_return_if_fail (range != NULL);
271   g_return_if_fail (GTK_IS_HSCROLLBAR (range));
272
273   if (GTK_WIDGET_DRAWABLE (range))
274     {
275       if (range->in_child == RANGE_CLASS (range)->step_back)
276         {
277           if (range->click_child == RANGE_CLASS (range)->step_back)
278             state_type = GTK_STATE_ACTIVE;
279           else
280             state_type = GTK_STATE_PRELIGHT;
281         }
282       else
283         state_type = GTK_STATE_NORMAL;
284
285       if (range->click_child == RANGE_CLASS (range)->step_back)
286         shadow_type = GTK_SHADOW_IN;
287       else
288         shadow_type = GTK_SHADOW_OUT;
289
290       gtk_draw_arrow (GTK_WIDGET (range)->style, range->step_back,
291                       state_type, shadow_type, GTK_ARROW_LEFT,
292                       TRUE, 0, 0, -1, -1);
293     }
294 }
295
296 static void
297 gtk_hscrollbar_slider_update (GtkRange *range)
298 {
299   g_return_if_fail (range != NULL);
300   g_return_if_fail (GTK_IS_HSCROLLBAR (range));
301
302   gtk_hscrollbar_calc_slider_size (GTK_HSCROLLBAR (range));
303   gtk_range_default_hslider_update (range);
304 }
305
306 static void
307 gtk_hscrollbar_calc_slider_size (GtkHScrollbar *hscrollbar)
308 {
309   GtkRange *range;
310   gint step_back_x;
311   gint step_back_width;
312   gint step_forw_x;
313   gint slider_width;
314   gint slider_height;
315   gint left, right;
316   gint width;
317
318   g_return_if_fail (hscrollbar != NULL);
319   g_return_if_fail (GTK_IS_HSCROLLBAR (hscrollbar));
320
321   if (GTK_WIDGET_REALIZED (hscrollbar))
322     {
323       range = GTK_RANGE (hscrollbar);
324
325       gdk_window_get_size (range->step_back, &step_back_width, NULL);
326       gdk_window_get_position (range->step_back, &step_back_x, NULL);
327       gdk_window_get_position (range->step_forw, &step_forw_x, NULL);
328
329       left = (step_back_x +
330               step_back_width +
331               RANGE_CLASS (hscrollbar)->stepper_slider_spacing);
332       right = step_forw_x - RANGE_CLASS (hscrollbar)->stepper_slider_spacing;
333       width = right - left;
334
335       if ((range->adjustment->page_size > 0) &&
336           (range->adjustment->lower != range->adjustment->upper))
337         {
338           if (range->adjustment->page_size >
339               (range->adjustment->upper - range->adjustment->lower))
340             range->adjustment->page_size = range->adjustment->upper - range->adjustment->lower;
341
342           width = (width * range->adjustment->page_size /
343                    (range->adjustment->upper - range->adjustment->lower));
344
345           if (width < RANGE_CLASS (hscrollbar)->min_slider_size)
346             width = RANGE_CLASS (hscrollbar)->min_slider_size;
347         }
348
349       gdk_window_get_size (range->slider, &slider_width, &slider_height);
350
351       if (slider_width != width)
352         gdk_window_resize (range->slider, width, slider_height);
353     }
354 }
355
356 static gint
357 gtk_hscrollbar_trough_keys(GtkRange *range,
358                            GdkEventKey *key,
359                            GtkScrollType *scroll,
360                            GtkTroughType *pos)
361 {
362   gint return_val = FALSE;
363   switch (key->keyval)
364     {
365     case GDK_Left:
366       return_val = TRUE;
367       *scroll = GTK_SCROLL_STEP_BACKWARD;
368       break;
369     case GDK_Right:
370       return_val = TRUE;
371       *scroll = GTK_SCROLL_STEP_FORWARD;
372       break;
373     case GDK_Home:
374       return_val = TRUE;
375       *pos = GTK_TROUGH_START;
376       break;
377     case GDK_End:
378       return_val = TRUE;
379       *pos = GTK_TROUGH_END;
380       break;
381     }
382   return return_val;
383 }