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