1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "gdraw.h"
31 #include "ggadgetP.h"
32 #include "gresource.h"
33 #include "ustring.h"
34 
35 static GBox scrollbar_box = GBOX_EMPTY; /* Don't initialize here */
36 static GBox thumb_box = GBOX_EMPTY; /* Don't initialize here */
37 int _GScrollBar_Width = 13;			/* in points */
38 int _GScrollBar_StartTime=300, _GScrollBar_RepeatTime=200;
39 static int gscrollbar_inited = false;
40 
41 static GGadget *GScrollBarCreateInitialized(struct gwindow *base, GGadgetData *gd,void *data);
42 static struct scrollbarinit sbinit = { 0, 40, 20, 10 };
43 static GGadgetCreateData scrollbar_gcd[] = {
44     { GScrollBarCreateInitialized, { { 0, 0, 100, 13 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) &sbinit }, gg_visible, NULL, NULL }, NULL, NULL },
45     { GScrollBarCreateInitialized, { { 0, 0, 100, 13 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) &sbinit }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL }
46 };
47 static GGadgetCreateData *sarray[] = { GCD_Glue, &scrollbar_gcd[0], GCD_Glue, &scrollbar_gcd[1], GCD_Glue, NULL, NULL };
48 static GGadgetCreateData scrollbarbox =
49     { GHVGroupCreate, { { 2, 2, 0, 0 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) sarray }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL };
50 static GResInfo gthumb_ri;
51 static GResInfo gscrollbar_ri = {
52     &gthumb_ri, &ggadget_ri,&gthumb_ri, NULL,
53     &scrollbar_box,
54     NULL,
55     &scrollbarbox,
56     NULL,
57     N_("ScrollBar"),
58     N_("Scroll Bar"),
59     "GScrollBar",
60     "Gdraw",
61     false,
62     box_foreground_border_outer|omf_border_type|omf_border_width|
63 	omf_padding|omf_main_background,
64     NULL,
65     GBOX_EMPTY,
66     NULL,
67     NULL,
68     NULL
69 };
70 static GResInfo gthumb_ri = {
71     NULL, &ggadget_ri,&gscrollbar_ri, NULL,
72     &thumb_box,
73     NULL,
74     &scrollbarbox,
75     NULL,
76     N_("SB Thumb"),
77     N_("Scroll Bar Thumb"),
78     "GScrollBarThumb",
79     "Gdraw",
80     true,
81     omf_main_background|omf_border_width|omf_padding,
82     NULL,
83     GBOX_EMPTY,
84     NULL,
85     NULL,
86     NULL
87 };
88 
GScrollBarChanged(GScrollBar * gsb,enum sb sbtype,int32 pos)89 static void GScrollBarChanged(GScrollBar *gsb, enum sb sbtype, int32 pos) {
90     GEvent e;
91     int active_len;
92 
93     if ( gsb->g.vert ) {
94 	active_len = gsb->g.inner.height;
95     } else {
96 	active_len = gsb->g.inner.width;
97     }
98     if ( active_len<=0 )
99 	active_len = 1;
100 
101     e.type = et_controlevent;
102     e.w = gsb->g.base;
103     e.u.control.subtype = et_scrollbarchange;
104     e.u.control.g = &gsb->g;
105     e.u.control.u.sb.type = sbtype;
106     e.u.control.u.sb.pos = (pos-gsb->thumboff)*(gsb->sb_max-gsb->sb_min)/active_len +
107 	    gsb->sb_min;
108     if ( e.u.control.u.sb.pos > gsb->sb_max-gsb->sb_mustshow )
109 	e.u.control.u.sb.pos = gsb->sb_max-gsb->sb_mustshow;
110     if ( e.u.control.u.sb.pos < gsb->sb_min )
111 	e.u.control.u.sb.pos = gsb->sb_min;
112 
113     if ( gsb->g.handle_controlevent!=NULL )
114 	(gsb->g.handle_controlevent)(&gsb->g,&e);
115     else
116 	GDrawPostEvent(&e);
117 }
118 
draw_thumb(GWindow pixmap,GScrollBar * gsb)119 static void draw_thumb(GWindow pixmap, GScrollBar *gsb) {
120     GRect thumbrect, thumbinner, old;
121     int lw, skip, i;
122 
123     GDrawPushClip(pixmap,&gsb->g.inner,&old);
124     thumbrect = gsb->g.inner;
125     if ( gsb->g.vert ) {
126 	thumbrect.y = gsb->g.inner.y+gsb->thumbpos;
127 	thumbrect.height = gsb->thumbsize;
128     } else {
129 	thumbrect.x = gsb->g.inner.x+gsb->thumbpos;
130 	thumbrect.width = gsb->thumbsize;
131     }
132     thumbinner.x = thumbrect.x+gsb->thumbborder;
133     thumbinner.y = thumbrect.y+gsb->thumbborder;
134     thumbinner.width = thumbrect.width-2*gsb->thumbborder;
135     thumbinner.height = thumbrect.height-2*gsb->thumbborder;
136 
137     GBoxDrawBackground(pixmap,&thumbrect,gsb->thumbbox, gsb->g.state,false);
138     GBoxDrawBorder(pixmap,&thumbrect,gsb->thumbbox,gsb->g.state,false);
139 
140     lw = GDrawPointsToPixels(gsb->g.base,1);
141     skip = GDrawPointsToPixels(gsb->g.base,3);
142     GDrawSetLineWidth(pixmap,lw);
143     if ( gsb->g.vert ) {
144 	for ( i = thumbinner.y + skip; i<thumbinner.y+thumbinner.height-skip;
145 		i += skip+2*lw ) {
146 	    GDrawDrawLine(pixmap,thumbinner.x+lw, i, thumbinner.x+thumbinner.width-2*lw, i,
147 		    gsb->thumbbox->border_brightest );
148 	    GDrawDrawLine(pixmap,thumbinner.x+lw, i+lw, thumbinner.x+thumbinner.width-2*lw, i+lw,
149 		    gsb->thumbbox->border_darkest );
150 	}
151     } else {
152 	for ( i = thumbinner.x + skip; i<thumbinner.x+thumbinner.width-skip;
153 		i += skip+2*lw ) {
154 	    GDrawDrawLine(pixmap,i, thumbinner.y+lw, i, thumbinner.y+thumbinner.height-2*lw,
155 		    gsb->thumbbox->border_brightest );
156 	    GDrawDrawLine(pixmap,i+lw, thumbinner.y+lw, i+lw, thumbinner.y+thumbinner.height-2*lw,
157 		    gsb->thumbbox->border_darkest );
158 	}
159     }
160     GDrawPopClip(pixmap,&old);
161 }
162 
draw_arrow(GWindow pixmap,GScrollBar * gsb,int which)163 static void draw_arrow(GWindow pixmap, GScrollBar *gsb, int which) {
164     GPoint pts[5];
165     int point = GDrawPointsToPixels(gsb->g.base,1);
166     int cnt = 4;
167     Color fill = gsb->thumbbox->main_foreground;
168 
169     if ( fill == COLOR_DEFAULT )
170 	fill = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
171 
172     switch ( which ) {
173       case 0:		/* Horizontal left arrow */
174 	pts[0].y = (gsb->g.r.y+(gsb->g.r.height-1)/2);
175 		pts[0].x = gsb->g.r.x + 2*point;
176 	pts[1].y = gsb->g.r.y + point;
177 		pts[1].x = pts[0].x + (gsb->g.r.height-1)/2-point ;
178 	pts[2].y = gsb->g.r.y+gsb->g.r.height-1 - point;
179 		pts[2].x = pts[1].x;
180 	pts[3] = pts[0];
181 	if ( !(gsb->g.inner.height&1 )) {
182 	    ++pts[3].y;
183 	    pts[4] = pts[0];
184 	    cnt = 5;
185 	}
186 	GDrawFillPoly(pixmap,pts,cnt,fill);
187 	GDrawDrawLine(pixmap,pts[0].x,pts[0].y,pts[1].x,pts[1].y,
188 		gsb->thumbbox->border_brightest);
189 	GDrawDrawLine(pixmap,pts[2].x,pts[2].y,pts[3].x,pts[3].y,
190 		gsb->thumbbox->border_darker);
191 	GDrawDrawLine(pixmap,pts[1].x,pts[1].y,pts[2].x,pts[2].y,
192 		gsb->thumbbox->border_darkest);
193       break;
194       case 1:		/* Vertical up arrow */
195 	pts[0].x = (gsb->g.r.x+(gsb->g.r.width-1)/2);
196 		pts[0].y = gsb->g.r.y + 2*point;
197 	pts[1].x = gsb->g.r.x + point;
198 		pts[1].y = pts[0].y + (gsb->g.r.width-1)/2-point ;
199 	pts[2].x = gsb->g.r.x+gsb->g.r.width-1 - point;
200 		pts[2].y = pts[1].y;
201 	pts[3] = pts[0];
202 	if ( !(gsb->g.inner.width&1 )) {
203 	    ++pts[3].x;
204 	    pts[4] = pts[0];
205 	    cnt = 5;
206 	}
207 	GDrawFillPoly(pixmap,pts,cnt,fill);
208 	GDrawDrawLine(pixmap,pts[0].x,pts[0].y,pts[1].x,pts[1].y,
209 		gsb->thumbbox->border_brightest);
210 	GDrawDrawLine(pixmap,pts[2].x,pts[2].y,pts[3].x,pts[3].y,
211 		gsb->thumbbox->border_darker);
212 	GDrawDrawLine(pixmap,pts[1].x,pts[1].y,pts[2].x,pts[2].y,
213 		gsb->thumbbox->border_darkest);
214       break;
215       case 2:		/* Horizontal right arrow */
216 	pts[0].y = (gsb->g.r.y+(gsb->g.r.height-1)/2);
217 		pts[0].x = gsb->g.r.x + gsb->g.r.width-1 - 2*point;
218 	pts[1].y = gsb->g.r.y + point;
219 		pts[1].x = pts[0].x - ((gsb->g.r.height-1)/2-point);
220 	pts[2].y = gsb->g.r.y+gsb->g.r.height-1 - point;
221 		pts[2].x = pts[1].x;
222 	pts[3] = pts[0];
223 	if ( !(gsb->g.inner.height&1 )) {
224 	    ++pts[3].y;
225 	    pts[4] = pts[0];
226 	    cnt = 5;
227 	}
228 	GDrawFillPoly(pixmap,pts,cnt,fill);
229 	GDrawDrawLine(pixmap,pts[0].x,pts[0].y,pts[1].x,pts[1].y,
230 		gsb->thumbbox->border_darkest);
231 	GDrawDrawLine(pixmap,pts[2].x,pts[2].y,pts[3].x,pts[3].y,
232 		gsb->thumbbox->border_darker);
233 	GDrawDrawLine(pixmap,pts[1].x,pts[1].y,pts[2].x,pts[2].y,
234 		gsb->thumbbox->border_brightest);
235       break;
236       case 3:		/* Vertical down arrow */
237 	pts[0].x = (gsb->g.r.x+(gsb->g.r.width-1)/2);
238 		pts[0].y = gsb->g.r.y + gsb->g.r.height-1 - 2*point;
239 	pts[1].x = gsb->g.r.x + point;
240 		pts[1].y = pts[0].y - ((gsb->g.r.width-1)/2-point);
241 	pts[2].x = gsb->g.r.x+gsb->g.r.width-1 - point;
242 		pts[2].y = pts[1].y;
243 	pts[3] = pts[0];
244 	if ( !(gsb->g.inner.width&1 )) {
245 	    ++pts[3].x;
246 	    pts[4] = pts[0];
247 	    cnt = 5;
248 	}
249 	GDrawFillPoly(pixmap,pts,cnt,fill);
250 	GDrawDrawLine(pixmap,pts[0].x,pts[0].y,pts[1].x,pts[1].y,
251 		gsb->thumbbox->border_darkest);
252 	GDrawDrawLine(pixmap,pts[2].x,pts[2].y,pts[3].x,pts[3].y,
253 		gsb->thumbbox->border_darker);
254 	GDrawDrawLine(pixmap,pts[1].x,pts[1].y,pts[2].x,pts[2].y,
255 		gsb->thumbbox->border_brightest);
256       break;
257     }
258 }
259 
260 static void GScrollBarFit(GScrollBar *gsb);
261 
gscrollbar_expose(GWindow pixmap,GGadget * g,GEvent * event)262 static int gscrollbar_expose(GWindow pixmap, GGadget *g, GEvent *event) {
263     GScrollBar *gsb = (GScrollBar *) g;
264     GBox box = *(g->box);
265     GRect old1;
266     GRect r;
267     int ar;
268 
269     if ( g->state == gs_invisible )
270 return( false );
271 
272     /* In case border was changed in resource editor, */
273     /* the scrollbar thumb inside must be refitted. */
274     GScrollBarFit(gsb);
275 
276     GDrawPushClip(pixmap,&g->r,&old1);
277 
278     r = g->r;
279     ar = gsb->arrowsize - gsb->sbborder;
280     if ( gsb->g.vert ) { r.y += ar ; r.height -= 2*ar; }
281     else { r.x += ar; r.width -= 2*ar; }
282 
283     /* Mimick old border behavior to retain compatibility with older themes, */
284     /* but match border shape with that of background. */
285     box.flags = box_foreground_border_outer;
286     box.border_width = 0;
287     GBoxDrawBackground(pixmap,&g->r,g->box,g->state,false);
288     GBoxDrawBackground(pixmap,&r,g->box,gs_pressedactive,false);
289     GBoxDrawBorder(pixmap,&g->r,&box,g->state,false);
290     GBoxDrawBorder(pixmap,&r,g->box,g->state,false);
291 
292     draw_thumb(pixmap,gsb); /* sets line width for arrows too */
293     draw_arrow(pixmap,gsb,gsb->g.vert);
294     draw_arrow(pixmap,gsb,gsb->g.vert|2);
295 
296     GDrawPopClip(pixmap,&old1);
297 return( true );
298 }
299 
gscrollbar_mouse(GGadget * g,GEvent * event)300 static int gscrollbar_mouse(GGadget *g, GEvent *event) {
301     GScrollBar *gsb = (GScrollBar *) g;
302     int active_pos, active_len;
303 
304     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
305 return( false );
306     if ( event->type == et_crossing )
307 return( false );
308 
309     if ( gsb->g.vert ) {
310 	active_pos = event->u.mouse.y-g->inner.y;
311 	active_len = g->inner.height;
312     } else {
313 	active_pos = event->u.mouse.x-g->inner.x;
314 	active_len = g->inner.width;
315     }
316 
317     if ( (event->type==et_mouseup || event->type==et_mousedown) &&
318 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
319 	/* X treats scroll wheels as though they send events from buttons 4 and 5 */
320 	/* Badly configured wacom mice send "p5 r5 p4 r4" or "p4 r4 p5 r5" */
321 	/*  properly configured mice just send "p4 r4" or "p5 r5" */
322 	/* And apple's mouse with a scrollwheel sends buttons 6&7 for horizontal*/
323 	/*  scrolling */
324 	/* Convention is that shift-vertical scroll=horizontal scroll */
325 	/*                    control-vertical scroll=minimize/maximize */
326 	if ( event->type==et_mousedown ) {
327 	    GDrawCancelTimer(gsb->pressed); gsb->pressed = NULL;
328 	    int isv = event->u.mouse.button<=5;
329 	    if ( event->u.mouse.state&ksm_shift ) isv = !isv;
330 	    if ( !isv && g->vert )
331 return( false );	/* Allow horizontal scrolling with normal scroll but not vice versa */
332 	    else if ( event->u.mouse.state&ksm_control )
333 return( false );
334 	    if ( event->u.mouse.button==5 || event->u.mouse.button==7 ) {
335 		GScrollBarChanged(gsb,et_sb_down,0);
336 	    } else if ( event->u.mouse.button==4 || event->u.mouse.button==6 ) {
337 		GScrollBarChanged(gsb,et_sb_up,0);
338 	    }
339 	}
340 return( true );
341     }
342 
343     if ( event->type == et_mousedown && GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
344 	GDrawCancelTimer(gsb->pressed); gsb->pressed = NULL;
345 	if ( event->u.mouse.button!=1 ) {
346 	    gsb->thumbpressed = true;
347 	    gsb->thumboff = 0;
348 	    active_pos = event->u.mouse.y-g->inner.y;
349 	    GScrollBarChanged(gsb,et_sb_thumb,active_pos);
350 	} else if ( active_pos >= gsb->thumbpos &&
351 		active_pos < gsb->thumbpos+gsb->thumbsize ) {
352 	    gsb->thumbpressed = true;
353 	    gsb->thumboff = active_pos-gsb->thumbpos;
354 	} else if ( active_pos < gsb->thumbpos &&
355 		(event->u.mouse.state&(ksm_control|ksm_meta)) ) {
356 	    gsb->thumbpressed = true;
357 	    gsb->thumboff = active_pos;
358 	    GScrollBarChanged(gsb,et_sb_top,0);
359 	} else if ( active_pos >= gsb->thumbpos+gsb->thumbsize &&
360 		(event->u.mouse.state&(ksm_control|ksm_meta)) ) {
361 	    gsb->thumbpressed = true;
362 	    gsb->thumboff = active_pos-active_len+gsb->thumbsize;
363 	    GScrollBarChanged(gsb,et_sb_bottom,0);
364 	} else {
365 	    if ( active_pos<0 )
366 		gsb->repeatcmd = et_sb_up;
367 	    else if ( active_pos >= active_len )
368 		gsb->repeatcmd = et_sb_down;
369 	    else if ( active_pos < gsb->thumbpos )
370 		gsb->repeatcmd = et_sb_uppage;
371 	    else /* if ( active_pos > gsb->thumbpos+gsb->thumbsize )*/
372 		gsb->repeatcmd = et_sb_downpage;
373 	    GScrollBarChanged(gsb,gsb->repeatcmd,0);
374 	    gsb->pressed = GDrawRequestTimer(g->base,_GScrollBar_StartTime,_GScrollBar_RepeatTime,NULL);
375 	}
376     } else if ( event->type == et_mousemove && gsb->thumbpressed ) {
377 	GDrawSkipMouseMoveEvents(gsb->g.base,event);
378 	if ( gsb->g.vert ) {
379 	    active_pos = event->u.mouse.y-g->inner.y;
380 	} else {
381 	    active_pos = event->u.mouse.x-g->inner.x;
382 	}
383 	GScrollBarChanged(gsb,et_sb_thumb,active_pos);
384     } else if ( event->type == et_mouseup && (gsb->thumbpressed || gsb->pressed)) {
385 	if ( gsb->thumbpressed )
386 	    GScrollBarChanged(gsb,et_sb_thumbrelease,active_pos);
387 	GDrawCancelTimer(gsb->pressed); gsb->pressed = NULL;
388 	gsb->thumbpressed = false;
389     } else if ( event->type == et_mousemove && !gsb->pressed &&
390 	    g->popup_msg!=NULL && GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
391 	GGadgetPreparePopup(g->base,g->popup_msg);
392 return( true );
393     } else
394 return( false );
395 
396 return( true );
397 }
398 
gscrollbar_timer(GGadget * g,GEvent * event)399 static int gscrollbar_timer(GGadget *g, GEvent *event) {
400     GScrollBar *gsb = (GScrollBar *) g;
401 
402     if ( event->u.timer.timer == gsb->pressed ) {
403 	GScrollBarChanged(gsb,gsb->repeatcmd,0);
404 return( true );
405     }
406 return( false );
407 }
408 
gscrollbar_destroy(GGadget * g)409 static void gscrollbar_destroy(GGadget *g) {
410     GScrollBar *gsb = (GScrollBar *) g;
411 
412     if ( gsb==NULL )
413 return;
414     GDrawCancelTimer(gsb->pressed);
415     _ggadget_destroy(g);
416 }
417 
gscrollbar_get_desired_size(GGadget * g,GRect * outer,GRect * inner)418 static void gscrollbar_get_desired_size(GGadget *g, GRect *outer, GRect *inner) {
419     int bp = GBoxBorderWidth(g->base,g->box);
420     GScrollBar *gsb = (GScrollBar *) g;
421     int width, height;
422     int minheight, sbw;
423 
424     sbw = GDrawPointsToPixels(gsb->g.base,_GScrollBar_Width);
425     minheight = 2*(gsb->thumbborder+gsb->arrowsize) + GDrawPointsToPixels(gsb->g.base,2);
426     if ( g->vert ) {
427 	width = sbw;
428 	height = minheight;
429     } else {
430 	width = minheight;
431 	height = sbw;
432     }
433 
434     if ( inner!=NULL ) {
435 	inner->x = inner->y = 0;
436 	inner->width = width; inner->height = height;
437     }
438     if ( outer!=NULL ) {
439 	outer->x = outer->y = 0;
440 	outer->width = width+2*bp; outer->height = height+2*bp;
441     }
442 }
443 
444 struct gfuncs gscrollbar_funcs = {
445     0,
446     sizeof(struct gfuncs),
447 
448     gscrollbar_expose,
449     gscrollbar_mouse,
450     NULL,
451     NULL,
452     NULL,
453     gscrollbar_timer,
454     NULL,
455 
456     _ggadget_redraw,
457     _ggadget_move,
458     _ggadget_resize,
459     _ggadget_setvisible,
460     _ggadget_setenabled,
461     _ggadget_getsize,
462     _ggadget_getinnersize,
463 
464     gscrollbar_destroy,
465 
466     NULL,
467     NULL,
468     NULL,
469     NULL,
470     NULL,
471 
472     NULL,
473     NULL,
474 
475     NULL,
476     NULL,
477     NULL,
478     NULL,
479     NULL,
480     NULL,
481     NULL,
482     NULL,
483     NULL,
484     NULL,
485     NULL,
486 
487     gscrollbar_get_desired_size,
488     NULL,
489     NULL,
490     NULL
491 };
492 
GScrollBarInit()493 static void GScrollBarInit() {
494     _GGadgetCopyDefaultBox(&scrollbar_box);
495     _GGadgetCopyDefaultBox(&thumb_box);
496     scrollbar_box.border_type = bt_lowered;
497     scrollbar_box.border_width = 1;
498     scrollbar_box.padding = 0;
499     scrollbar_box.flags |= box_foreground_border_outer;
500     scrollbar_box.main_background = GDrawColorBrighten(scrollbar_box.main_background, 0x10);
501     thumb_box.main_background = GDrawColorDarken(thumb_box.main_background,0x8);
502     thumb_box.border_width = 1;
503     thumb_box.padding = 0;
504     _GGadgetInitDefaultBox("GScrollBar.",&scrollbar_box,NULL);
505     _GGadgetInitDefaultBox("GScrollBarThumb.",&thumb_box,NULL);
506     _GScrollBar_Width = GResourceFindInt("GScrollBar.Width",_GScrollBar_Width);
507     _GScrollBar_StartTime = GResourceFindInt("GScrollBar.StartupTime",_GScrollBar_StartTime);
508     _GScrollBar_RepeatTime = GResourceFindInt("GScrollBar.RepeatTime",_GScrollBar_RepeatTime);
509     gscrollbar_inited = true;
510 }
511 
GScrollBarFit(GScrollBar * gsb)512 static void GScrollBarFit(GScrollBar *gsb) {
513     int minheight;
514 
515     gsb->sbborder = GBoxBorderWidth(gsb->g.base,gsb->g.box);
516     gsb->thumbborder = GBoxBorderWidth(gsb->g.base,gsb->thumbbox);
517     /* FIXME: workaround for incorrect calculation. */
518     if ( gsb->thumbborder > 5 ) gsb->thumbborder = 5;
519     gsb->arrowsize = gsb->sbborder +
520 	    2*GDrawPointsToPixels(gsb->g.base,2) +
521 	    GDrawPointsToPixels(gsb->g.base,_GScrollBar_Width)/2-
522 	    2*GDrawPointsToPixels(gsb->g.base,1);
523     minheight = 2*(gsb->thumbborder+gsb->arrowsize) + GDrawPointsToPixels(gsb->g.base,2);
524 
525     if ( gsb->g.vert ) {
526 	gsb->g.r.width = GDrawPointsToPixels(gsb->g.base,_GScrollBar_Width);
527 	if ( gsb->g.r.height< minheight )
528 	    gsb->g.r.height = minheight;
529 	gsb->g.inner.x = gsb->g.r.x+gsb->sbborder;
530 	gsb->g.inner.width = gsb->g.r.width - 2*gsb->sbborder;
531 	gsb->g.inner.y = gsb->g.r.y+gsb->arrowsize;
532 	gsb->g.inner.height = gsb->g.r.height - 2*gsb->arrowsize;
533     } else {
534 	gsb->g.r.height = GDrawPointsToPixels(gsb->g.base,_GScrollBar_Width);
535 	if ( gsb->g.r.width< minheight )
536 	    gsb->g.r.width = minheight;
537 	gsb->g.inner.x = gsb->g.r.x+gsb->arrowsize;
538 	gsb->g.inner.width = gsb->g.r.width - 2*gsb->arrowsize;
539 	gsb->g.inner.y = gsb->g.r.y+gsb->sbborder;
540 	gsb->g.inner.height = gsb->g.r.height - 2*gsb->sbborder;
541     }
542 }
543 
_GScrollBarCreate(GScrollBar * gsb,struct gwindow * base,GGadgetData * gd,void * data,GBox * def)544 static GScrollBar *_GScrollBarCreate(GScrollBar *gsb, struct gwindow *base, GGadgetData *gd,void *data, GBox *def) {
545 
546     if ( !gscrollbar_inited )
547 	GScrollBarInit();
548     gsb->g.funcs = &gscrollbar_funcs;
549     gd->flags |= gg_pos_use0;
550     _GGadget_Create(&gsb->g,base,gd,data,def);
551 
552     gsb->g.takes_input = true;
553     if ( gd->flags & gg_sb_vert )
554 	gsb->g.vert = true;
555     gsb->thumbbox = &thumb_box;
556 
557     GScrollBarFit(gsb);
558     if ( gd->u.sbinit!=NULL )
559 	GScrollBarSetMustShow(&gsb->g,
560 		gd->u.sbinit->sb_min,
561 		gd->u.sbinit->sb_max,
562 		gd->u.sbinit->sb_pagesize,
563 		gd->u.sbinit->sb_pos);
564 
565     if ( gd->flags & gg_group_end )
566 	_GGadgetCloseGroup(&gsb->g);
567 return( gsb );
568 }
569 
GScrollBarCreateInitialized(struct gwindow * base,GGadgetData * gd,void * data)570 static GGadget *GScrollBarCreateInitialized(struct gwindow *base, GGadgetData *gd,void *data) {
571     GScrollBar *gsb = _GScrollBarCreate(calloc(1,sizeof(GScrollBar)),base,gd,data,&scrollbar_box);
572 
573 return( &gsb->g );
574 }
575 
GScrollBarCreate(struct gwindow * base,GGadgetData * gd,void * data)576 GGadget *GScrollBarCreate(struct gwindow *base, GGadgetData *gd,void *data) {
577     GScrollBar *gsb;
578     struct scrollbarinit *hold = gd->u.sbinit;
579 
580     gd->u.sbinit = NULL;
581     gsb = _GScrollBarCreate(calloc(1,sizeof(GScrollBar)),base,gd,data,&scrollbar_box);
582     gd->u.sbinit = hold;
583 
584 return( &gsb->g );
585 }
586 
GScrollBarGetPos(GGadget * g)587 int32 GScrollBarGetPos(GGadget *g) {
588 return( ((GScrollBar *) g)->sb_pos );
589 }
590 
GScrollBarAddToPos(GGadget * g,int32 pos)591 int32 GScrollBarAddToPos(GGadget *g,int32 pos) {
592     return GScrollBarSetPos( g, GScrollBarGetPos(g) + pos );
593 }
594 
595 
GScrollBarSetPos(GGadget * g,int32 pos)596 int32 GScrollBarSetPos(GGadget *g,int32 pos) {
597     GScrollBar *gsb = (GScrollBar *) g;
598 
599     if ( pos>gsb->sb_max-gsb->sb_mustshow )
600 	pos = gsb->sb_max-gsb->sb_mustshow;
601     if ( pos<gsb->sb_min )
602 	pos = gsb->sb_min;
603     gsb->sb_pos = pos;
604 
605     if ( pos==gsb->sb_min || gsb->sb_min==gsb->sb_max )
606 	gsb->thumbpos = 0;
607     else
608 	gsb->thumbpos =
609 	    ((gsb->g.vert?gsb->g.inner.height:gsb->g.inner.width)-gsb->size_offset)
610 	      *(pos-gsb->sb_min)/(gsb->sb_max-gsb->sb_min);
611     _ggadget_redraw(g);
612 return( pos );
613 }
614 
GScrollBarSetMustShow(GGadget * g,int32 sb_min,int32 sb_max,int32 sb_pagesize,int32 sb_mustshow)615 void GScrollBarSetMustShow(GGadget *g, int32 sb_min, int32 sb_max, int32 sb_pagesize,
616 	int32 sb_mustshow ) {
617     GScrollBar *gsb = (GScrollBar *) g;
618 
619     if ( sb_min>sb_max || sb_pagesize<=0 ) {
620 	GDrawIError("Invalid scrollbar bounds min=%d max=%d, pagesize=%d",
621 		sb_min, sb_max, sb_pagesize );
622 return;
623     }
624     gsb->sb_min = sb_min;
625     gsb->sb_max = sb_max;
626     gsb->sb_pagesize = sb_pagesize;
627     gsb->sb_mustshow = sb_mustshow;
628     gsb->size_offset = 0;
629     gsb->thumbsize = (gsb->g.vert?gsb->g.inner.height:gsb->g.inner.width);
630     if ( sb_max-sb_min > sb_pagesize )
631 	gsb->thumbsize = (gsb->thumbsize*gsb->sb_pagesize)/(sb_max-sb_min);
632     if ( gsb->thumbsize<2*gsb->thumbborder+10 ) {
633         gsb->size_offset = 2*gsb->thumbborder+10 - gsb->thumbsize;
634 	gsb->thumbsize = 2*gsb->thumbborder+10;
635 	if ( gsb->thumbsize>(gsb->g.vert?gsb->g.inner.height:gsb->g.inner.width) ) {
636 	    gsb->size_offset = 0;
637 	    gsb->thumbsize = (gsb->g.vert?gsb->g.inner.height:gsb->g.inner.width);
638         }
639     }
640     GScrollBarSetPos(g,gsb->sb_pos);
641 }
642 
GScrollBarSetBounds(GGadget * g,int32 sb_min,int32 sb_max,int32 sb_pagesize)643 void GScrollBarSetBounds(GGadget *g, int32 sb_min, int32 sb_max, int32 sb_pagesize ) {
644     GScrollBarSetMustShow(g,sb_min,sb_max,sb_pagesize,sb_pagesize);
645 }
646 
GScrollBarGetBounds(GGadget * g,int32 * sb_min,int32 * sb_max,int32 * sb_pagesize)647 void GScrollBarGetBounds(GGadget *g, int32 *sb_min, int32 *sb_max, int32 *sb_pagesize ) {
648     GScrollBar *gsb = (GScrollBar *) g;
649     *sb_min = gsb->sb_min;
650     *sb_max = gsb->sb_max;
651     *sb_pagesize = gsb->sb_pagesize;
652 }
653 
_GScrollBarRIHead(void)654 GResInfo *_GScrollBarRIHead(void) {
655     if ( !gscrollbar_inited )
656 	GScrollBarInit();
657 return( &gscrollbar_ri );
658 }
659