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