xref: /inferno-os/libtk/scrol.c (revision 0b97835064f7a6adffa5529a9676920f6c1ad3bf)
1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 
5 #define	O(t, e)		((long)(&((t*)0)->e))
6 
7 /* Layout constants */
8 enum {
9 	Triangle	= 10,	/* Height of scroll bar triangle */
10 	Elembw	= 1,		/* border around elements (triangles etc.) */
11 	Scrollbw	= 1,		/* bevel border on scrollbar */
12 	Tribw=	1,	/* shadow border on triangle */
13 };
14 
15 typedef struct TkScroll TkScroll;
16 struct TkScroll
17 {
18 	int		activer;
19 	int		orient;		/* Horitontal or Vertical */
20 	int		dragpix;	/* Scroll delta in button drag */
21 	int		dragtop;
22 	int		dragbot;
23 	int		jump;		/* Jump scroll enable */
24 	int		flag;		/* Display flags */
25 	int		top;		/* Top fraction */
26 	int		bot;		/* Bottom fraction */
27 	int		a1;		/* Pixel top/left arrow1 */
28 	int		t1;		/* Pixel top/left trough */
29 	int		t2;		/* Pixel top/left lower trough */
30 	int		a2;		/* Pixel top/left arrow2 */
31 	char*		cmd;
32 };
33 
34 enum {
35 	ActiveA1	= (1<<0),	/* Scrollbar control */
36 	ActiveA2	= (1<<1),
37 	ActiveB1	= (1<<2),
38 	ButtonA1	= (1<<3),
39 	ButtonA2	= (1<<4),
40 	ButtonB1	= (1<<5),
41 	Autorepeat = (1<<6)
42 };
43 
44 static
45 TkOption opts[] =
46 {
47 	"activerelief",	OPTstab,	O(TkScroll, activer),	tkrelief,
48 	"command",	OPTtext,	O(TkScroll, cmd),	nil,
49 	"jump",	OPTstab,	O(TkScroll, jump),	tkbool,
50 	"orient",	OPTstab,	O(TkScroll, orient),	tkorient,
51 	nil
52 };
53 
54 static
55 TkEbind b[] =
56 {
57 	{TkLeave,		"%W activate {}"},
58 	{TkEnter,		"%W activate [%W identify %x %y]"},
59 	{TkMotion,		"%W activate [%W identify %x %y]"},
60 	{TkButton1P|TkMotion,	"%W tkScrollDrag %x %y"},
61 	{TkButton1P,		"%W tkScrolBut1P %x %y"},
62 	{TkButton1P|TkDouble,	"%W tkScrolBut1P %x %y"},
63 	{TkButton1R,	"%W tkScrolBut1R; %W activate [%W identify %x %y]"},
64 	{TkButton2P,		"%W tkScrolBut2P [%W fraction %x %y]"},
65 };
66 
67 static char*
tkinitscroll(Tk * tk)68 tkinitscroll(Tk *tk)
69 {
70 	int gap;
71 	TkScroll *tks;
72 
73 	tks = TKobj(TkScroll, tk);
74 
75 	gap = 2*tk->borderwidth;
76 	if(tks->orient == Tkvertical) {
77 		if(tk->req.width == 0)
78 			tk->req.width = Triangle + gap;
79 		if(tk->req.height == 0)
80 			tk->req.height = 2*Triangle + gap + 6*Elembw;
81 	}
82 	else {
83 		if(tk->req.width == 0)
84 			tk->req.width = 2*Triangle + gap + 6*Elembw;
85 		if(tk->req.height == 0)
86 			tk->req.height = Triangle + gap;
87 	}
88 
89 
90 	return tkbindings(tk->env->top, tk, b, nelem(b));
91 }
92 
93 char*
tkscrollbar(TkTop * t,char * arg,char ** ret)94 tkscrollbar(TkTop *t, char *arg, char **ret)
95 {
96 	Tk *tk;
97 	char *e;
98 	TkName *names;
99 	TkScroll *tks;
100 	TkOptab tko[3];
101 
102 	tk = tknewobj(t, TKscrollbar, sizeof(Tk)+sizeof(TkScroll));
103 	if(tk == nil)
104 		return TkNomem;
105 
106 	tks = TKobj(TkScroll, tk);
107 
108 	tk->relief = TKflat;
109 	tk->borderwidth = 1;
110 	tks->activer = TKraised;
111 	tks->orient = Tkvertical;
112 
113 	tko[0].ptr = tk;
114 	tko[0].optab = tkgeneric;
115 	tko[1].ptr = tks;
116 	tko[1].optab = opts;
117 	tko[2].ptr = nil;
118 
119 	names = nil;
120 	e = tkparse(t, arg, tko, &names);
121 	if(e != nil) {
122 		tkfreeobj(tk);
123 		return e;
124 	}
125 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
126 
127 	e = tkinitscroll(tk);
128 	if(e != nil) {
129 		tkfreeobj(tk);
130 		return e;
131 	}
132 
133 	e = tkaddchild(t, tk, &names);
134 	tkfreename(names);
135 	if(e != nil) {
136 		tkfreeobj(tk);
137 		return e;
138 	}
139 	tk->name->link = nil;
140 
141 	return tkvalue(ret, "%s", tk->name->name);
142 }
143 
144 static char*
tkscrollcget(Tk * tk,char * arg,char ** val)145 tkscrollcget(Tk *tk, char *arg, char **val)
146 {
147 	TkOptab tko[3];
148 	TkScroll *tks = TKobj(TkScroll, tk);
149 
150 	tko[0].ptr = tk;
151 	tko[0].optab = tkgeneric;
152 	tko[1].ptr = tks;
153 	tko[1].optab = opts;
154 	tko[2].ptr = nil;
155 
156 	return tkgencget(tko, arg, val, tk->env->top);
157 }
158 
159 void
tkfreescrlb(Tk * tk)160 tkfreescrlb(Tk *tk)
161 {
162 	TkScroll *tks = TKobj(TkScroll, tk);
163 
164 	if(tks->cmd != nil)
165 		free(tks->cmd);
166 }
167 
168 static void
drawarrow(TkScroll * tks,Image * i,Point p[3],TkEnv * e,int activef,int buttonf)169 drawarrow(TkScroll *tks, Image *i, Point p[3], TkEnv *e, int activef, int buttonf)
170 {
171 	Image *l, *d, *t;
172 	int bgnd;
173 
174 	bgnd = TkCbackgnd;
175 	if(tks->flag & (activef|buttonf)) {
176 		bgnd = TkCactivebgnd;
177 		fillpoly(i, p, 3, ~0, tkgc(e, bgnd), p[0]);
178 	}
179 
180 	l = tkgc(e, bgnd+TkLightshade);
181 	d = tkgc(e, bgnd+TkDarkshade);
182 	if(tks->flag & buttonf) {
183 		t = d;
184 		d = l;
185 		l = t;
186 	}
187 	line(i, p[1], p[2], 0, 0, Tribw-1, d, p[1]);
188 	line(i, p[2], p[0], 0, 0, Tribw-1, d, p[2]);
189 	line(i, p[0], p[1], 0, 0, Tribw-1, l, p[0]);
190 }
191 
192 static void
drawslider(TkScroll * tks,Image * i,Point o,int w,int h,TkEnv * e)193 drawslider(TkScroll *tks, Image *i, Point o, int w, int h, TkEnv *e)
194 {
195 	Image *l, *d;
196 	Rectangle r;
197 	int bgnd;
198 
199 	bgnd = TkCbackgnd;
200 	if(tks->flag & (ActiveB1|ButtonB1)) {
201 		r.min = o;
202 		r.max.x = o.x + w + Elembw*2;
203 		r.max.y = o.y + h + Elembw*2;
204 		bgnd = TkCactivebgnd;
205 		draw(i, r, tkgc(e, bgnd), nil, ZP);
206 	}
207 
208 	l = tkgc(e, bgnd+TkLightshade);
209 	d = tkgc(e, bgnd+TkDarkshade);
210 	if(tks->flag & ButtonB1)
211 		tkbevel(i, o, w, h, Scrollbw, d, l);
212 	else
213 		tkbevel(i, o, w, h, Scrollbw, l, d);
214 }
215 
216 static void
tkvscroll(Tk * tk,TkScroll * tks,Image * i,Point size)217 tkvscroll(Tk *tk, TkScroll *tks, Image *i, Point size)
218 {
219 	TkEnv *e;
220 	Point p[3], o;
221 	int bo, w, h, triangle;
222 
223 	e = tk->env;
224 
225 	triangle = tk->act.width - Elembw;
226 
227 	bo = tk->borderwidth + Elembw;
228 	p[0].x = size.x/2;
229 	p[0].y = bo;
230 	p[1].x = p[0].x - triangle/2;
231 	p[1].y = p[0].y + triangle;
232 	p[2].x = p[0].x + triangle/2;
233 	p[2].y = p[0].y + triangle;
234 	drawarrow(tks, i, p, e, ActiveA1, ButtonA1);
235 
236 	tks->a1 = p[2].y;
237 	h = p[2].y + Elembw;
238 
239 	p[0].y = size.y - bo - 1;
240 	p[1].y = p[0].y - triangle;
241 	p[2].y = p[0].y - triangle;
242 	drawarrow(tks, i, p, e, ActiveA2, ButtonA2);
243 
244 	tks->a2 = p[2].y;
245 
246 	o.x = tk->borderwidth ;
247 	o.y = bo + triangle + 2*Elembw;
248 	w = size.x - 2*bo;
249 	h = p[2].y - 2*Elembw - h - 2*tk->borderwidth;
250 
251 	o.y += TKF2I(tks->top*h);
252 	h *= tks->bot - tks->top;
253 	h = TKF2I(h);
254 
255 	tks->t1 = o.y - Elembw;
256 	tks->t2 = o.y + h + Elembw;
257 
258 	drawslider(tks, i, o, w, h, e);
259 }
260 
261 static void
tkhscroll(Tk * tk,TkScroll * tks,Image * i,Point size)262 tkhscroll(Tk *tk, TkScroll *tks, Image *i, Point size)
263 {
264 	TkEnv *e;
265 	Point p[3], o;
266 	int bo, w, h, triangle;
267 
268 	e = tk->env;
269 
270 	triangle = tk->act.height - Elembw;
271 
272 	bo = tk->borderwidth + Elembw;
273 	p[0].x = bo;
274 	p[0].y = size.y/2;
275 	p[1].x = p[0].x + triangle;
276 	p[1].y = p[0].y - triangle/2 + 1;
277 	p[2].x = p[0].x + triangle;
278 	p[2].y = p[0].y + triangle/2 - 2;
279 	drawarrow(tks, i, p, e, ActiveA1, ButtonA1);
280 
281 	tks->a1 = p[2].x;
282 	w = p[2].x + Elembw;
283 
284 	p[0].x = size.x - bo - 1;
285 	p[1].x = p[0].x - triangle;
286 	p[2].x = p[0].x - triangle;
287 	drawarrow(tks, i, p, e, ActiveA2, ButtonA2);
288 
289 	tks->a2 = p[2].x;
290 
291 	o.x = bo + triangle + 2*Elembw;
292 	o.y = tk->borderwidth;
293 	w = p[2].x - 2*Elembw - w - 2*tk->borderwidth;
294 	h = size.y - 2*bo;
295 
296 	o.x += TKF2I(tks->top*w);
297 	w *= tks->bot - tks->top;
298 	w = TKF2I(w);
299 
300 	tks->t1 = o.x - Elembw;
301 	tks->t2 = o.x + w + Elembw;
302 
303 	drawslider(tks, i, o, w, h, e);
304 }
305 
306 char*
tkdrawscrlb(Tk * tk,Point orig)307 tkdrawscrlb(Tk *tk, Point orig)
308 {
309 	Point p;
310 	TkEnv *e;
311 	Rectangle r;
312 	Image *i, *dst;
313 	TkScroll *tks = TKobj(TkScroll, tk);
314 
315 	e = tk->env;
316 
317 	dst = tkimageof(tk);
318 	if(dst == nil)
319 		return nil;
320 
321 	r.min = ZP;
322 	r.max.x = tk->act.width + 2*tk->borderwidth;
323 	r.max.y = tk->act.height + 2*tk->borderwidth;
324 
325 	i = tkitmp(e, r.max, TkCbackgnd);
326 	if(i == nil)
327 		return nil;
328 
329 	if(tks->orient == Tkvertical)
330 		tkvscroll(tk, tks, i, r.max);
331 	else
332 		tkhscroll(tk, tks, i, r.max);
333 
334 	tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief);
335 
336 	p.x = tk->act.x + orig.x;
337 	p.y = tk->act.y + orig.y;
338 	r = rectaddpt(r, p);
339 	draw(dst, r, i, nil, ZP);
340 
341 	return nil;
342 }
343 
344 /* Widget Commands (+ means implemented)
345 	+activate
346 	+cget
347 	+configure
348 	+delta
349 	+fraction
350 	+get
351 	+identify
352 	+set
353 */
354 
355 static char*
tkscrollconf(Tk * tk,char * arg,char ** val)356 tkscrollconf(Tk *tk, char *arg, char **val)
357 {
358 	char *e;
359 	TkGeom g;
360 	int bd;
361 	TkOptab tko[3];
362 	TkScroll *tks = TKobj(TkScroll, tk);
363 
364 	tko[0].ptr = tk;
365 	tko[0].optab = tkgeneric;
366 	tko[1].ptr = tks;
367 	tko[1].optab = opts;
368 	tko[2].ptr = nil;
369 
370 	if(*arg == '\0')
371 		return tkconflist(tko, val);
372 
373 	g = tk->req;
374 	bd = tk->borderwidth;
375 	e = tkparse(tk->env->top, arg, tko, nil);
376 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
377 	tkgeomchg(tk, &g, bd);
378 
379 	tk->dirty = tkrect(tk, 1);
380 	return e;
381 }
382 
383 static char*
tkscrollactivate(Tk * tk,char * arg,char ** val)384 tkscrollactivate(Tk *tk, char *arg, char **val)
385 {
386 	int s, gotarg;
387 	char buf[Tkmaxitem];
388 	TkScroll *tks = TKobj(TkScroll, tk);
389 
390 	USED(val);
391 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), &gotarg);
392 	s = tks->flag;
393 	if (!gotarg) {
394 		char *a;
395 		if (s & ActiveA1)
396 			a = "arrow1";
397 		else if (s & ActiveA2)
398 			a = "arrow2";
399 		else if (s & ActiveB1)
400 			a = "slider";
401 		else
402 			a = "";
403 		return tkvalue(val, a);
404 	}
405 	tks->flag &= ~(ActiveA1 | ActiveA2 | ActiveB1);
406 	if(strcmp(buf, "arrow1") == 0)
407 		tks->flag |= ActiveA1;
408 	else
409 	if(strcmp(buf, "arrow2") == 0)
410 		tks->flag |= ActiveA2;
411 	else
412 	if(strcmp(buf, "slider") == 0)
413 		tks->flag |= ActiveB1;
414 
415 	if(s ^ tks->flag)
416 		tk->dirty = tkrect(tk, 1);
417 	return nil;
418 }
419 
420 static char*
tkscrollset(Tk * tk,char * arg,char ** val)421 tkscrollset(Tk *tk, char *arg, char **val)
422 {
423 	TkTop *t;
424 	char *e;
425 	TkScroll *tks = TKobj(TkScroll, tk);
426 
427 	USED(val);
428 	t = tk->env->top;
429 	e = tkfracword(t, &arg, &tks->top, nil);
430 	if (e != nil)
431 		return e;
432 	e = tkfracword(t, &arg, &tks->bot, nil);
433 	if (e != nil)
434 		return e;
435 	if(tks->top < 0)
436 		tks->top = 0;
437 	if(tks->top > TKI2F(1))
438 		tks->top = TKI2F(1);
439 	if(tks->bot < 0)
440 		tks->bot = 0;
441 	if(tks->bot > TKI2F(1))
442 		tks->bot = TKI2F(1);
443 
444 	tk->dirty = tkrect(tk, 1);
445 	return nil;
446 }
447 
448 static char*
tkscrolldelta(Tk * tk,char * arg,char ** val)449 tkscrolldelta(Tk *tk, char *arg, char **val)
450 {
451 	int l, delta;
452 	char buf[Tkmaxitem];
453 	TkScroll *tks = TKobj(TkScroll, tk);
454 
455 	arg = tkitem(buf, arg);
456 	if(tks->orient == Tkvertical)
457 		tkitem(buf, arg);
458 	if(*arg == '\0' || *buf == '\0')
459 		return TkBadvl;
460 
461 	l = tks->a2-tks->a1-4*Elembw;
462 	delta = TKI2F(1);
463 	if(l != 0)
464 		delta = TKI2F(atoi(buf)) / l;
465 	tkfprint(buf, delta);
466 
467 	return tkvalue(val, "%s", buf);
468 }
469 
470 static char*
tkscrollget(Tk * tk,char * arg,char ** val)471 tkscrollget(Tk *tk, char *arg, char **val)
472 {
473 	char *v, buf[Tkmaxitem];
474 	TkScroll *tks = TKobj(TkScroll, tk);
475 
476 	USED(arg);
477 	v = tkfprint(buf, tks->top);
478 	*v++ = ' ';
479 	tkfprint(v, tks->bot);
480 
481 	return tkvalue(val, "%s", buf);
482 }
483 
484 static char*
tkscrollidentify(Tk * tk,char * arg,char ** val)485 tkscrollidentify(Tk *tk, char *arg, char **val)
486 {
487 	int gotarg;
488 	TkTop *t;
489 	char *v, buf[Tkmaxitem];
490 	Point p;
491 	TkScroll *tks = TKobj(TkScroll, tk);
492 
493 	t = tk->env->top;
494 	arg = tkword(t, arg, buf, buf+sizeof(buf), &gotarg);
495 	if (!gotarg)
496 		return TkBadvl;
497 	p.x = atoi(buf);
498 	tkword(t, arg, buf, buf+sizeof(buf), &gotarg);
499 	if (!gotarg)
500 		return TkBadvl;
501 	p.y = atoi(buf);
502 	if (!ptinrect(p, tkrect(tk, 0)))
503 		return nil;
504 	if (tks->orient == Tkvertical)
505 		p.x = p.y;
506 	p.x += tk->borderwidth;
507 
508 	v = "";
509 	if(p.x <= tks->a1)
510 		v = "arrow1";
511 	if(p.x > tks->a1 && p.x <= tks->t1)
512 		v = "trough1";
513 	if(p.x > tks->t1 && p.x < tks->t2)
514 		v = "slider";
515 	if(p.x >= tks->t2 && p.x < tks->a2)
516 		v = "trough2";
517 	if(p.x >= tks->a2)
518 		v = "arrow2";
519 	return tkvalue(val, "%s", v);
520 }
521 
522 static char*
tkscrollfraction(Tk * tk,char * arg,char ** val)523 tkscrollfraction(Tk *tk, char *arg, char **val)
524 {
525 	int len, frac, pos;
526 	char buf[Tkmaxitem];
527 	TkScroll *tks = TKobj(TkScroll, tk);
528 
529 	arg = tkitem(buf, arg);
530 	if(tks->orient == Tkvertical)
531 		tkitem(buf, arg);
532 	if(*arg == '\0' || *buf == '\0')
533 		return TkBadvl;
534 
535 	pos = atoi(buf);
536 	if(pos < tks->a1)
537 		pos = tks->a1;
538 	if(pos > tks->a2)
539 		pos = tks->a2;
540 	len = tks->a2 - tks->a1 - 4*Elembw;
541 	frac = TKI2F(1);
542 	if(len != 0)
543 		frac = TKI2F(pos-tks->a1)/len;
544 	tkfprint(buf, frac);
545 	return tkvalue(val, "%s", buf);
546 }
547 
548 static char*
tkScrolBut1R(Tk * tk,char * arg,char ** val)549 tkScrolBut1R(Tk *tk, char *arg, char **val)
550 {
551 	TkScroll *tks = TKobj(TkScroll, tk);
552 
553 	USED(val);
554 	USED(arg);
555 	tkcancelrepeat(tk);
556 	tks->flag &= ~(ActiveA1|ActiveA2|ActiveB1|ButtonA1|ButtonA2|ButtonB1|Autorepeat);
557 	tk->dirty = tkrect(tk, 1);
558 	return nil;
559 }
560 
561 /* tkScrolBut2P fraction */
562 static char*
tkScrolBut2P(Tk * tk,char * arg,char ** val)563 tkScrolBut2P(Tk *tk, char *arg, char **val)
564 {
565 	TkTop *t;
566 	char *e, buf[Tkmaxitem], fracbuf[Tkmaxitem];
567 	TkScroll *tks = TKobj(TkScroll, tk);
568 
569 
570 	USED(val);
571 	t = tk->env->top;
572 
573 	if(arg[0] == '\0')
574 		return TkBadvl;
575 
576 	tkword(t, arg, fracbuf, fracbuf+sizeof(fracbuf), nil);
577 
578 	e = nil;
579 	if(tks->cmd != nil) {
580 		snprint(buf, sizeof(buf), "%s moveto %s", tks->cmd, fracbuf);
581 		e = tkexec(t, buf, nil);
582 	}
583 	return e;
584 }
585 
586 static void
sbrepeat(Tk * tk,void * v,int cancelled)587 sbrepeat(Tk *tk, void *v, int cancelled)
588 {
589 	char *e, buf[Tkmaxitem];
590 	TkScroll *tks = TKobj(TkScroll, tk);
591 	char *fmt = (char *)v;
592 
593 	if (cancelled) {
594 		tks->flag &= ~Autorepeat;
595 		return;
596 	}
597 
598 	if(tks->cmd != nil && fmt != nil) {
599 		snprint(buf, sizeof(buf), fmt, tks->cmd);
600 		e = tkexec(tk->env->top, buf, nil);
601 		if (e != nil) {
602 			tks->flag &= ~Autorepeat;
603 			tkcancelrepeat(tk);
604 		} else
605 			tkupdate(tk->env->top);
606 	}
607 }
608 
609 /* tkScrolBut1P %x %y */
610 static char*
tkScrolBut1P(Tk * tk,char * arg,char ** val)611 tkScrolBut1P(Tk *tk, char *arg, char **val)
612 {
613 	int pix;
614 	TkTop *t;
615 	char *e, *fmt, buf[Tkmaxitem];
616 	TkScroll *tks = TKobj(TkScroll, tk);
617 
618 	USED(val);
619 	t = tk->env->top;
620 
621 	if (tks->flag & Autorepeat)
622 		return nil;
623 	arg = tkword(t, arg, buf, buf+sizeof(buf), nil);
624 	if(tks->orient == Tkvertical)
625 		tkword(t, arg, buf, buf+sizeof(buf), nil);
626 	if(buf[0] == '\0')
627 		return TkBadvl;
628 
629 	pix = atoi(buf);
630 
631 	tks->dragpix = pix;
632 	tks->dragtop = tks->top;
633 	tks->dragbot = tks->bot;
634 
635 	pix += tk->borderwidth;
636 
637 	fmt = nil;
638 	e = nil;
639 	if(pix <= tks->a1) {
640 		fmt = "%s scroll -1 unit";
641 		tks->flag |= ButtonA1;
642 	}
643 	if(pix > tks->a1 && pix <= tks->t1)
644 		fmt = "%s scroll -1 page";
645 	if(pix > tks->t1 && pix < tks->t2)
646 		tks->flag |= ButtonB1;
647 	if(pix >= tks->t2 && pix < tks->a2)
648 		fmt = "%s scroll 1 page";
649 	if(pix >= tks->a2) {
650 		fmt = "%s scroll 1 unit";
651 		tks->flag |= ButtonA2;
652 	}
653 	if(tks->cmd != nil && fmt != nil) {
654 		snprint(buf, sizeof(buf), fmt, tks->cmd);
655 		e = tkexec(t, buf, nil);
656 		tks->flag |= Autorepeat;
657 		tkrepeat(tk, sbrepeat, fmt, TkRptpause, TkRptinterval);
658 	}
659 	tk->dirty = tkrect(tk, 1);
660 	return e;
661 }
662 
663 /* tkScrolDrag %x %y */
664 static char*
tkScrollDrag(Tk * tk,char * arg,char ** val)665 tkScrollDrag(Tk *tk, char *arg, char **val)
666 {
667 	TkTop *t;
668 	int pix, delta;
669 	char frac[32], buf[Tkmaxitem];
670 	TkScroll *tks = TKobj(TkScroll, tk);
671 
672 	USED(val);
673 	t = tk->env->top;
674 
675 	if (tks->flag & Autorepeat)
676 		return nil;
677 	if((tks->flag & ButtonB1) == 0)
678 		return nil;
679 
680 	arg = tkword(t, arg, buf, buf+sizeof(buf), nil);
681 	if(tks->orient == Tkvertical)
682 		tkword(t, arg, buf, buf+sizeof(buf), nil);
683 	if(buf[0] == '\0')
684 		return TkBadvl;
685 
686 	pix = atoi(buf);
687 
688 	delta = TKI2F(pix-tks->dragpix);
689 	if ( tks->a2 == tks->a1 )
690 		return TkBadvl;
691 	delta = delta/(tks->a2-tks->a1-4*Elembw);
692 	if(tks->jump == BoolT) {
693 		if(tks->dragtop+delta >= 0 &&
694 		   tks->dragbot+delta <= TKI2F(1)) {
695 			tks->top = tks->dragtop+delta;
696 			tks->bot = tks->dragbot+delta;
697 		}
698 		return nil;
699 	}
700 	if(tks->cmd != nil) {
701 		delta += tks->dragtop;
702 		if(delta < 0)
703 			delta = 0;
704 		if(delta > TKI2F(1))
705 			delta = TKI2F(1);
706 		tkfprint(frac, delta);
707 		snprint(buf, sizeof(buf), "%s moveto %s", tks->cmd, frac);
708 		return tkexec(t, buf, nil);
709 	}
710 	return nil;
711 }
712 
713 TkCmdtab tkscrlbcmd[] =
714 {
715 	"activate",		tkscrollactivate,
716 	"cget",			tkscrollcget,
717 	"configure",		tkscrollconf,
718 	"delta",		tkscrolldelta,
719 	"fraction",		tkscrollfraction,
720 	"get",			tkscrollget,
721 	"identify",		tkscrollidentify,
722 	"set",			tkscrollset,
723 	"tkScrollDrag",		tkScrollDrag,
724 	"tkScrolBut1P",		tkScrolBut1P,
725 	"tkScrolBut1R",		tkScrolBut1R,
726 	"tkScrolBut2P",		tkScrolBut2P,
727 	nil
728 };
729 
730 TkMethod scrollbarmethod = {
731 	"scrollbar",
732 	tkscrlbcmd,
733 	tkfreescrlb,
734 	tkdrawscrlb
735 };
736