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