xref: /inferno-os/libtk/scale.c (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
1 #include <lib9.h>
2 #include <kernel.h>
3 #include "draw.h"
4 #include "tk.h"
5 #include "keyboard.h"
6 
7 #define	O(t, e)		((long)(&((t*)0)->e))
8 
9 typedef struct TkScale TkScale;
10 struct TkScale
11 {
12 	int	value;
13 	int	bigi;
14 	int	digits;
15 	int	digwidth;
16 	int	from;		/* Base of range */
17 	int	to;		/* Limit of range */
18 	int	len;		/* Length of groove */
19 	int	res;		/* Resolution */
20 	int	sv;		/* Show value */
21 	int	sl;		/* Slider length */
22 	int	sw;		/* Slider width div 2 */
23 	int	relief;
24 	int	tick;
25 	int	orient;
26 	char*	command;
27 	char*	label;
28 	int	pixmin;
29 	int	pixmax;
30 	int	pixpos;
31 	int	center;
32 	int	pix;
33 	int	base;
34 	int	flag;
35 	int	jump;
36 };
37 
38 enum {
39 	Dragging = (1<<0),
40 	Autorepeat = (1<<1),
41 };
42 
43 static
44 TkOption opts[] =
45 {
46 	"bigincrement",		OPTnnfrac,	O(TkScale, bigi),	nil,
47 	"digits",		OPTdist,	O(TkScale, digits),	nil,
48 	"from",			OPTfrac,	O(TkScale, from),	nil,
49 	"to",			OPTfrac,	O(TkScale, to),		nil,
50 	"length",		OPTdist,	O(TkScale, len),	nil,
51 	"resolution",		OPTnnfrac,	O(TkScale, res),	nil,
52 	"showrange",	OPTignore,	0,	nil,
53 	"showvalue",		OPTstab,	O(TkScale, sv),		tkbool,
54 	"jump",		OPTstab, O(TkScale, jump),	tkbool,
55 	"sliderlength",		OPTdist,	O(TkScale, sl),		nil,
56 	"sliderrelief",		OPTstab,	O(TkScale, relief),	tkrelief,
57 	"tickinterval",		OPTfrac,	O(TkScale, tick),	nil,
58 	"tick",		OPTfrac,	O(TkScale, tick),	nil,
59 	"label",		OPTtext,	O(TkScale, label),	nil,
60 	"command",		OPTtext,	O(TkScale, command),	nil,
61 	"orient",		OPTstab,	O(TkScale, orient),	tkorient,
62 	nil
63 };
64 
65 static char trough1[] = "trough1";
66 static char trough2[] = "trough2";
67 static char slider[]  = "slider";
68 
69 static
70 TkEbind b[] =
71 {
72 	{TkMotion,		"%W tkScaleMotion %x %y"},
73 	{TkButton1P|TkMotion,	"%W tkScaleDrag %x %y"},
74 	{TkButton1P,		"%W tkScaleMotion %x %y; %W tkScaleBut1P %x %y"},
75 	{TkButton1P|TkDouble,	"%W tkScaleMotion %x %y; %W tkScaleBut1P %x %y"},
76 	{TkButton1R,		"%W tkScaleDrag %x %y; %W tkScaleBut1R; %W tkScaleMotion %x %y"},
77 	{TkKey,		"%W tkScaleKey 0x%K"},
78 };
79 
80 enum
81 {
82 	Scalewidth	= 18,
83 	ScalePad	= 2,
84 	ScaleBW		= 2,
85 	ScaleSlider	= 16,
86 	ScaleLen	= 80,
87 
88 };
89 
90 static int
91 maximum(int a, int b)
92 {
93 	if (a > b)
94 		return a;
95 	return b;
96 }
97 
98 void
99 tksizescale(Tk *tk)
100 {
101 	Point p;
102 	char buf[32];
103 	TkScale *tks;
104 	int fh, w, h, digits, digits2;
105 
106 	tks = TKobj(TkScale, tk);
107 
108 	digits = tks->digits;
109 	if(digits <= 0) {
110 		digits = tkfprint(buf, tks->from) - buf;
111 		digits2 = tkfprint(buf, tks->to) - buf;
112 		digits = maximum(digits, digits2);
113 		if (tks->res > 0) {
114 			digits2 = tkfprint(buf, tks->from + tks->res) - buf;
115 			digits = maximum(digits, digits2);
116 			digits2 = tkfprint(buf, tks->to - tks->res) - buf;
117 			digits = maximum(digits, digits2);
118 		}
119 	}
120 
121 	digits *= tk->env->wzero;
122 	if(tks->sv != BoolT)
123 		digits = 0;
124 
125 	tks->digwidth = digits;
126 
127 	p = tkstringsize(tk, tks->label);
128 	if(tks->orient == Tkvertical) {
129 		h = tks->len + 2*ScaleBW + 2*ScalePad;
130 		w = Scalewidth + 2*ScalePad + 2*ScaleBW;
131 		if (p.x)
132 			w += p.x + ScalePad;
133 		if (tks->sv == BoolT)
134 			w += digits + ScalePad;
135 	}
136 	else {
137 		w = maximum(p.x, tks->len + ScaleBW + 2*ScalePad);
138 		h = Scalewidth + 2*ScalePad + 2*ScaleBW;
139 		fh = tk->env->font->height;
140 		if(tks->label != nil)
141 			h += fh + ScalePad;
142 		if(tks->sv == BoolT)
143 			h += fh + ScalePad;
144 	}
145 	w += 2*tk->highlightwidth;
146 	h += 2*tk->highlightwidth;
147 	if(!(tk->flag & Tksetwidth))
148 		tk->req.width = w;
149 	if(!(tk->flag & Tksetheight))
150 		tk->req.height = h;
151 }
152 
153 static int
154 tkscalecheckvalue(Tk *tk)
155 {
156 	int v;
157 	TkScale *tks = TKobj(TkScale, tk);
158 	int limit = 1;
159 
160 	v = tks->value;
161 	if (tks->res > 0)
162 		v = (v / tks->res) * tks->res;
163 	if (tks->to >= tks->from) {
164 		if (v < tks->from)
165 			v = tks->from;
166 		else if (v > tks->to)
167 			v = tks->to;
168 		else
169 			limit = 0;
170 	} else {
171 		if (v < tks->to)
172 			v = tks->to;
173 		else if (v > tks->from)
174 			v = tks->from;
175 		else
176 			limit = 0;
177 	}
178 	/*
179 	 *  it's possible for the value to end up as a non-whole
180 	 * multiple of resolution here, if the end points aren't
181 	 * themselves such a multiple. if so, tough - that's
182 	 * what you asked for! (it does mean that the endpoints
183 	 * are always accessible however, which could be a good thing).
184 	 */
185 	tks->value = v;
186 	return limit;
187 }
188 
189 char*
190 tkscale(TkTop *t, char *arg, char **ret)
191 {
192 	Tk *tk;
193 	char *e;
194 	TkName *names;
195 	TkScale *tks;
196 	TkOptab tko[3];
197 
198 	tk = tknewobj(t, TKscale, sizeof(Tk)+sizeof(TkScale));
199 	if(tk == nil)
200 		return TkNomem;
201 
202 	tk->flag |= Tktakefocus;
203 	tks = TKobj(TkScale, tk);
204 	tks->res = TKI2F(1);
205 	tks->to = TKI2F(100);
206 	tks->len = ScaleLen;
207 	tks->orient = Tkvertical;
208 	tks->relief = TKraised;
209 	tks->sl = ScaleSlider;
210 	tks->sv = BoolT;
211 	tks->bigi = 0;
212 
213 	tko[0].ptr = tk;
214 	tko[0].optab = tkgeneric;
215 	tko[1].ptr = tks;
216 	tko[1].optab = opts;
217 	tko[2].ptr = nil;
218 
219 	names = nil;
220 	e = tkparse(t, arg, tko, &names);
221 	if(e != nil) {
222 		tkfreeobj(tk);
223 		return e;
224 	}
225 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
226 	tkscalecheckvalue(tk);
227 	tksizescale(tk);
228 	if (tks->bigi == 0)
229 		tks->bigi = TKI2F(TKF2I(tks->to - tks->from) / 10);
230 	e = tkbindings(t, tk, b, nelem(b));
231 
232 	if(e != nil) {
233 		tkfreeobj(tk);
234 		return e;
235 	}
236 
237 	e = tkaddchild(t, tk, &names);
238 	tkfreename(names);
239 	if(e != nil) {
240 		tkfreeobj(tk);
241 		return e;
242 	}
243 	tk->name->link = nil;
244 
245 	return tkvalue(ret, "%s", tk->name->name);
246 }
247 
248 static char*
249 tkscalecget(Tk *tk, char *arg, char **val)
250 {
251 	TkOptab tko[3];
252 	TkScale *tks = TKobj(TkScale, tk);
253 
254 	tko[0].ptr = tk;
255 	tko[0].optab = tkgeneric;
256 	tko[1].ptr = tks;
257 	tko[1].optab = opts;
258 	tko[2].ptr = nil;
259 
260 	return tkgencget(tko, arg, val, tk->env->top);
261 }
262 
263 void
264 tkfreescale(Tk *tk)
265 {
266 	TkScale *tks = TKobj(TkScale, tk);
267 
268 	if(tks->command != nil)
269 		free(tks->command);
270 	if(tks->label != nil)
271 		free(tks->label);
272 }
273 
274 static void
275 tkscalehoriz(Tk *tk, Image *i)
276 {
277 	TkEnv *e;
278 	char sv[32];
279 	TkScale *tks;
280 	Image *d, *l;
281 	Rectangle r, r2, sr;
282 	Point p, q;
283 	int fh, sh, sl, v, w, h, len;
284 	int fgnd;
285 
286 	e = tk->env;
287 	tks = TKobj(TkScale, tk);
288 
289 
290 	fh = e->font->height;
291 	fgnd = TkCforegnd;
292 	if (tk->flag & Tkdisabled)
293 		fgnd = TkCdisablefgnd;
294 
295 	r = Rect(0, 0, tk->act.width, tk->act.height);
296 	r = rectaddpt(r, Pt(tk->borderwidth, tk->borderwidth));
297 	r = insetrect(r, tk->highlightwidth);
298 	r = insetrect(r, ScalePad);
299 
300 	if(tks->label != nil) {
301 		string(i, r.min, tkgc(e, fgnd), ZP, e->font, tks->label);
302 		r.min.y += fh + ScalePad;
303 	}
304 	if(tks->sv == BoolT)
305 		r.min.y += fh + ScalePad;
306 
307 	sr = insetrect(r, ScaleBW);
308 	w = Dx(sr);
309 	h = Dy(sr);
310 	sl = tks->sl + 2*ScaleBW;
311 
312 	l = tkgc(e, TkCbackgndlght);
313 	d = tkgc(e, TkCbackgnddark);
314 	tkbevel(i, r.min, w, h, ScaleBW, d, l);
315 
316 	tks->pixmin = sr.min.x;
317 	tks->pixmax = sr.max.x;
318 
319 	sh = h - 2*ScaleBW;
320 	tks->sw = sh/2;
321 
322 	w -= sl;
323 	if (w <= 0)
324 		w = 1;
325 	p.x = sr.min.x;
326 	p.y = sr.max.y;
327 	if(tks->tick > 0){
328 		int j, t, l;
329 		t = tks->tick;
330 		l = tks->to-tks->from;
331 		if (l < 0)
332 			l = -l;
333 		if (l == 0)
334 			l = 1;
335 		r2.min.y = p.y;
336 		r2.max.y = p.y + ScaleBW + ScalePad;
337 		for(j = 0; j <= l; j += t){
338 			r2.min.x = p.x+((vlong)j*w)/l+sl/2;
339 			r2.max.x = r2.min.x+1;
340 			draw(i, r2, tkgc(e, fgnd), nil, ZP);
341 		}
342 	}
343 	v = tks->value-tks->from;
344 	len = tks->to-tks->from;
345 	if (len != 0)
346 		p.x += ((vlong)v*w)/len;
347 	p.y = sr.min.y;
348 	q = p;
349 	q.x += tks->sl/2 + 1;
350 	q.y++;
351 	if(tk->flag & Tkactivated) {
352 		r2.min = p;
353 		r2.max.x = p.x+sl;
354 		r2.max.y = sr.max.y;
355 		draw(i, r2, tkgc(e, TkCactivebgnd), nil, ZP);
356 	}
357 	switch(tks->relief) {
358 	case TKsunken:
359 		tkbevel(i, p, tks->sl, sh, ScaleBW, d, l);
360 		tkbevel(i, q, 0, sh, 1, l, d);
361 		break;
362 	case TKraised:
363 		tkbevel(i, p, tks->sl, sh, ScaleBW, l, d);
364 		tkbevel(i, q, 0, sh, 1, d, l);
365 		break;
366 	}
367 	tks->pixpos = p.x;
368 	tks->center = p.y + sh/2 + ScaleBW;
369 
370 	if(tks->sv != BoolT)
371 		return;
372 
373 	tkfprint(sv, tks->value);
374 	if(tks->digits > 0 && tks->digits < strlen(sv))
375 		sv[tks->digits] = '\0';
376 
377 	w = stringwidth(e->font, sv);
378 	p.x = q.x;
379 	p.x -= w/2;
380 	p.y = r.min.y - fh - ScalePad;
381 	if(p.x < tks->pixmin)
382 		p.x = tks->pixmin;
383 	if(p.x+w > tks->pixmax)
384 		p.x = tks->pixmax - w;
385 
386 	string(i, p, tkgc(e, fgnd), ZP, e->font, sv);
387 }
388 
389 static void
390 tkscalevert(Tk *tk, Image *i)
391 {
392 	TkEnv *e;
393 	TkScale *tks;
394 	char sv[32];
395 	Image *d, *l;
396 	Rectangle r, r2, sr;
397 	Point p, q;
398 	int fh, v, sw, w, h, len, sl;
399 	int fgnd;
400 
401 	e = tk->env;
402 	tks = TKobj(TkScale, tk);
403 
404 	fh = e->font->height;
405 	fgnd = TkCforegnd;
406 	if (tk->flag & Tkdisabled)
407 		fgnd = TkCdisablefgnd;
408 
409 	r = Rect(0, 0, tk->act.width, tk->act.height);
410 	r = rectaddpt(r, Pt(tk->borderwidth, tk->borderwidth));
411 	r = insetrect(r, tk->highlightwidth);
412 	r = insetrect(r, ScalePad);
413 
414 	if (tks->sv)
415 		r.min.x += tks->digwidth + ScalePad;
416 
417 	if(tks->label != nil) {
418 		p =  stringsize(e->font, tks->label);
419 		r.max.x -= p.x;
420 		string(i, Pt(r.max.x, r.min.y), tkgc(e, fgnd), ZP, e->font, tks->label);
421 		r.max.x -= ScalePad;
422 	}
423 
424 	sr = insetrect(r, ScaleBW);
425 	h = Dy(sr);
426 	w = Dx(sr);
427 	sl = tks->sl + 2*ScaleBW;
428 
429 	l = tkgc(e, TkCbackgndlght);
430 	d = tkgc(e, TkCbackgnddark);
431 	tkbevel(i, r.min, w, h, ScaleBW, d, l);
432 
433 	tks->pixmin = sr.min.y;
434 	tks->pixmax = sr.max.y;
435 
436 	sw = w - 2*ScaleBW;
437 	tks->sw = sw/2;
438 
439 	h -= sl;
440 	if (h <= 0)
441 		h = 1;
442 	p.x = sr.max.x;
443 	p.y = sr.min.y;
444 	if(tks->tick > 0){
445 		int j, t, l;
446 		t = tks->tick;
447 		l = tks->to-tks->from;
448 		if (l < 0)
449 			l = -l;
450 		if (l == 0)
451 			l = 1;
452 		r2.min = p;
453 		r2.max.x = p.x + ScaleBW + ScalePad;
454 		for(j = 0; j <= l; j += t){
455 			r2.min.y = p.y+((vlong)j*h)/l+sl/2;
456 			r2.max.y = r2.min.y+1;
457 			draw(i, r2, tkgc(e, fgnd), nil, ZP);
458 		}
459 	}
460 
461 	v = tks->value-tks->from;
462 	len  = tks->to-tks->from;
463 	if (len != 0)
464 		p.y += ((vlong)v*h)/len;
465 	p.x = sr.min.x;
466 	q = p;
467 	q.x++;
468 	q.y += tks->sl/2 + 1;
469 	if(tk->flag & Tkactivated) {
470 		r2.min = p;
471 		r2.max.x = sr.max.x;
472 		r2.max.y = p.y+sl;
473 		draw(i, r2, tkgc(e, TkCactivebgnd), nil, ZP);
474 	}
475 	switch(tks->relief) {
476 	case TKsunken:
477 		tkbevel(i, p, sw, tks->sl, ScaleBW, d, l);
478 		tkbevel(i, q, sw, 0, 1, l, d);
479 		break;
480 	case TKraised:
481 		tkbevel(i, p, sw, tks->sl, ScaleBW, l, d);
482 		tkbevel(i, q, sw, 0, 1, d, l);
483 		break;
484 	}
485 	tks->pixpos = p.y;
486 	tks->center = p.x + sw/2 + ScaleBW;
487 
488 	if(tks->sv != BoolT)
489 		return;
490 
491 	tkfprint(sv, tks->value);
492 	if(tks->digits > 0 && tks->digits < strlen(sv))
493 		sv[tks->digits] = '\0';
494 
495 	p.x = r.min.x - ScalePad - stringwidth(e->font, sv);
496 	p.y = q.y;
497 	p.y -= fh/2;
498 	if (p.y < tks->pixmin)
499 		p.y = tks->pixmin;
500 	if (p.y + fh > tks->pixmax)
501 		p.y = tks->pixmax - fh;
502 	string(i, p, tkgc(e, fgnd), ZP, e->font, sv);
503 }
504 
505 char*
506 tkdrawscale(Tk *tk, Point orig)
507 {
508 	Point p;
509 	TkEnv *env;
510 	TkScale *tks;
511 	Rectangle r, fr;
512 	Image *i;
513 
514 	tks = TKobj(TkScale, tk);
515 	env = tk->env;
516 
517 	r.min = ZP;
518 	r.max.x = tk->act.width + 2*tk->borderwidth;
519 	r.max.y = tk->act.height + 2*tk->borderwidth;
520 	i = tkitmp(env, r.max, TkCbackgnd);
521 	if(i == nil)
522 		return nil;
523 
524 	if(tks->orient == Tkvertical)
525 		tkscalevert(tk, i);
526 	else
527 		tkscalehoriz(tk, i);
528 
529 	tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief);
530 	if (tkhaskeyfocus(tk)) {
531 		fr = insetrect(r, tk->borderwidth);
532 		tkbox(i, fr, tk->highlightwidth, tkgc(env, TkChighlightfgnd));
533 	}
534 
535 	p.x = tk->act.x + orig.x;
536 	p.y = tk->act.y + orig.y;
537 	r = rectaddpt(r, p);
538 	draw(tkimageof(tk), r, i, nil, ZP);
539 
540 	return nil;
541 }
542 
543 /* Widget Commands (+ means implemented)
544 	+cget
545 	+configure
546 	+coords
547 	+get
548 	+identify
549 	+set
550 */
551 
552 static char*
553 tkscaleconf(Tk *tk, char *arg, char **val)
554 {
555 	char *e;
556 	TkGeom g;
557 	int bd;
558 	TkOptab tko[3];
559 	TkScale *tks = TKobj(TkScale, tk);
560 
561 	tko[0].ptr = tk;
562 	tko[0].optab = tkgeneric;
563 	tko[1].ptr = tks;
564 	tko[1].optab = opts;
565 	tko[2].ptr = nil;
566 
567 	if(*arg == '\0')
568 		return tkconflist(tko, val);
569 
570 	g = tk->req;
571 	bd = tk->borderwidth;
572 	e = tkparse(tk->env->top, arg, tko, nil);
573 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
574 	tkscalecheckvalue(tk);
575 	tksizescale(tk);
576 	tkgeomchg(tk, &g, bd);
577 
578 	tk->dirty = tkrect(tk, 1);
579 	return e;
580 }
581 
582 char*
583 tkscaleposn(TkEnv *env, Tk *tk, char *arg, int *z)
584 {
585 	int x, y;
586 	TkScale *tks = TKobj(TkScale, tk);
587 	char *e;
588 
589 	e = tkfracword(env->top, &arg, &x, env);
590 	if(e != nil)
591 		return e;
592 	e = tkfracword(env->top, &arg, &y, env);
593 	if(e != nil)
594 		return e;
595 
596 	x = TKF2I(x) + tk->borderwidth;
597 	y = TKF2I(y) + tk->borderwidth;
598 
599 	if(tks->orient == Tkvertical) {
600 		if(z != nil) {
601 			z[0] = x;
602 			z[1] = y;
603 		}
604 		x = y;
605 	}
606 	else {
607 		if(z != nil) {
608 			z[0] = y;
609 			z[1] = x;
610 		}
611 	}
612 	if(x > tks->pixmin && x < tks->pixpos)
613 		return trough1;
614 	else
615 	if(x >= tks->pixpos && x < tks->pixpos+tks->sl+2*ScaleBW)
616 		return slider;
617 	else
618 	if(x >= tks->pixpos+tks->sl+2*ScaleBW && x < tks->pixmax)
619 		return trough2;
620 
621 	return "";
622 }
623 
624 static char*
625 tkscaleident(Tk *tk, char *arg, char **val)
626 {
627 	char *v;
628 
629 	v = tkscaleposn(tk->env, tk, arg, nil);
630 	if(v == nil)
631 		return TkBadvl;
632 	return tkvalue(val, "%s", v);
633 }
634 
635 static char*
636 tkscalecoords(Tk *tk, char *arg, char **val)
637 {
638 	int p, x, y, l, value;
639 	TkScale *tks = TKobj(TkScale, tk);
640 	char *e;
641 
642 	value = tks->value;
643 	if(arg != nil && arg[0] != '\0') {
644 		e = tkfracword(tk->env->top, &arg, &value, tk->env);
645 		if (e != nil)
646 			return e;
647 	}
648 
649 	value -= tks->from;
650 	p = tks->pixmax - tks->pixmin;
651 	l = TKF2I(tks->to-tks->from);
652 	if (l==0)
653 		p /= 2;
654 	else
655 		p = TKF2I(value*p/l);
656 	p += tks->pixmin;
657 	if(tks->orient == Tkvertical) {
658 		x = tks->center;
659 		y = p;
660 	}
661 	else {
662 		x = p;
663 		y = tks->center;
664 	}
665 	return tkvalue(val, "%d %d", x, y);
666 }
667 
668 static char*
669 tkscaleget(Tk *tk, char *arg, char **val)
670 {
671 	int x, y, value, v, l;
672 	char buf[Tkminitem], *e;
673 	TkScale *tks = TKobj(TkScale, tk);
674 
675 	value = tks->value;
676 	if(arg[0] != '\0') {
677 		e = tkfracword(tk->env->top, &arg, &x, tk->env);
678 		if (e != nil)
679 			return e;
680 		e = tkfracword(tk->env->top, &arg, &y, tk->env);
681 		if (e != nil)
682 			return e;
683 		if(tks->orient == Tkvertical)
684 			v = TKF2I(y) + tk->borderwidth;
685 		else
686 			v = TKF2I(x) + tk->borderwidth;
687 
688 		if(v < tks->pixmin)
689 			value = tks->from;
690 		else
691 		if(v > tks->pixmax)
692 			value = tks->to;
693 		else {
694 			l = tks->pixmax-tks->pixmin;
695 			value = 0;
696 			if (l!=0)
697 				value = v * ((tks->to-tks->from)/l);
698 			value += tks->from;
699 		}
700 		if(tks->res > 0)
701 			value = (value/tks->res)*tks->res;
702 	}
703 	tkfprint(buf, value);
704 	return tkvalue(val, "%s", buf);
705 }
706 
707 static char*
708 tkscaleset(Tk *tk, char *arg, char **val)
709 {
710 	TkScale *tks = TKobj(TkScale, tk);
711 	char *e;
712 
713 	USED(val);
714 
715 	e = tkfracword(tk->env->top, &arg, &tks->value, tk->env);
716 	if (e != nil)
717 		return e;
718 	tkscalecheckvalue(tk);
719 	tk->dirty = tkrect(tk, 1);
720 	return nil;
721 }
722 
723 /* tkScaleMotion %x %y */
724 static char*
725 tkscalemotion(Tk *tk, char *arg, char **val)
726 {
727 	int o, z[2];
728 	char *v;
729 	TkScale *tks = TKobj(TkScale, tk);
730 	extern int tkstylus;
731 
732 	USED(val);
733 	v = tkscaleposn(tk->env, tk, arg, z);
734 	if(v == nil)
735 		return TkBadvl;
736 
737 	o = tk->flag;
738 	if(v != slider || z[0] < tks->center-tks->sw || z[0] > tks->center+tks->sw)
739 		tk->flag &= ~Tkactivated;
740 	else if(tkstylus == 0 || tk->env->top->ctxt->mstate.b != 0)
741 		tk->flag |= Tkactivated;
742 
743 	if((o & Tkactivated) != (tk->flag & Tkactivated))
744 		tk->dirty = tkrect(tk, 1);
745 
746 	return nil;
747 }
748 
749 static char*
750 tkscaledrag(Tk *tk, char *arg, char **val)
751 {
752 	int x, y, v;
753 	char *e, buf[Tkmaxitem], f[32];
754 	TkScale *tks = TKobj(TkScale, tk);
755 
756 	USED(val);
757 	if((tks->flag & Dragging) == 0)
758 		return nil;
759 	if(tks->flag & Autorepeat)
760 		return nil;
761 
762 	e = tkfracword(tk->env->top, &arg, &x, tk->env);
763 	if(e != nil)
764 		return e;
765 	e = tkfracword(tk->env->top, &arg, &y, tk->env);
766 	if(e != nil)
767 		return e;
768 
769 	if(tks->orient == Tkvertical)
770 		v = TKF2I(y) + tk->borderwidth;
771 	else
772 		v = TKF2I(x) + tk->borderwidth;
773 
774 	v -= tks->pix;
775 	x = tks->pixmax-tks->pixmin;
776 	if (x!=tks->sl)
777 		v = tks->base + (vlong)v * (tks->to-tks->from)/(x-tks->sl);
778 	else
779 		v = tks->base;
780 	if(tks->res > 0) {
781 		int a = tks->res / 2;
782 		if (v < 0)
783 			a = -a;
784 		v = ((v+a)/tks->res)*tks->res;
785 	}
786 
787 	tks->value = v;
788 	tkscalecheckvalue(tk);
789 
790 	if(tks->command != nil && tks->jump != BoolT) {
791 		tkfprint(f, tks->value);
792 		snprint(buf, sizeof(buf), "%s %s", tks->command, f);
793 		e = tkexec(tk->env->top, buf, nil);
794 	}
795 	tk->dirty = tkrect(tk, 1);
796 	return e;
797 }
798 
799 static int
800 sgn(int v)
801 {
802 	return v >= 0 ? 1 : -1;
803 }
804 
805 static char*
806 stepscale(Tk *tk, char *pos, int *end)
807 {
808 	TkScale *tks = TKobj(TkScale, tk);
809 	char *e, buf[Tkmaxitem], f[32];
810 	int s;
811 
812 	s = sgn(tks->to - tks->from);
813 	if(pos == trough1) {
814 		tks->value -= s * tks->bigi;
815 	} else {
816 		/* trough2 */
817 		tks->value += s * tks->bigi;
818 	}
819 	s = !tkscalecheckvalue(tk);
820 	if (end != nil)
821 		*end = s;
822 	e = nil;
823 	if(tks->command != nil) {
824 		/* XXX perhaps should only send command if value has actually changed */
825 		tkfprint(f, tks->value);
826 		snprint(buf, sizeof(buf), "%s %s", tks->command, f);
827 		e = tkexec(tk->env->top, buf, nil);
828 	}
829 	return e;
830 }
831 
832 static void
833 screpeat(Tk *tk, void *v, int cancelled)
834 {
835 	char *e, *pos;
836 	int repeat;
837 	TkScale *tks = TKobj(TkScale, tk);
838 
839 	pos = v;
840 	if (cancelled) {
841 		tks->flag &= ~Autorepeat;
842 		return;
843 	}
844 	e = stepscale(tk, pos, &repeat);
845 	if(e != nil || !repeat) {
846 		tks->flag &= ~Autorepeat;
847 		tkcancelrepeat(tk);
848 	}
849 	tk->dirty = tkrect(tk, 1);
850 	tkupdate(tk->env->top);
851 }
852 
853 static char*
854 tkscalebut1p(Tk *tk, char *arg, char **val)
855 {
856 	int z[2];
857 	char *v, *e;
858 	TkScale *tks = TKobj(TkScale, tk);
859 	int repeat;
860 
861 	USED(val);
862 	v = tkscaleposn(tk->env, tk, arg, z);
863 	if(v == nil)
864 		return TkBadvl;
865 
866 	e = nil;
867 	if(v[0] == '\0' || z[0] < tks->center-tks->sw || z[0] > tks->center+tks->sw)
868 		return nil;
869 	if(v == slider) {
870 		tks->flag |= Dragging;
871 		tks->relief = TKsunken;
872 		tks->pix = z[1];
873 		tks->base = tks->value;
874 		tkscalecheckvalue(tk);
875 	} else  {
876 		e = stepscale(tk, v, &repeat);
877 		if (e == nil && repeat) {
878 			tks->flag |= Autorepeat;
879 			tkrepeat(tk, screpeat, v, TkRptpause, TkRptinterval);
880 		}
881 	}
882 
883 	tk->dirty = tkrect(tk, 1);
884 	return e;
885 }
886 
887 static char*
888 tkscalebut1r(Tk *tk, char *arg, char **val)
889 {
890 	TkScale *tks = TKobj(TkScale, tk);
891 	char *e, buf[Tkmaxitem], f[32];
892 	USED(val);
893 	USED(arg);
894 	if(tks->flag & Autorepeat) {
895 		tkcancelrepeat(tk);
896 		tks->flag &= ~Autorepeat;
897 	}
898 	e = nil;
899 	if (tks->flag & Dragging) {
900 		if (tks->command != nil && tks->jump == BoolT && (tks->flag & Dragging)) {
901 			tkfprint(f, tks->value);
902 			snprint(buf, sizeof(buf), "%s %s", tks->command, f);
903 			e = tkexec(tk->env->top, buf, nil);
904 		}
905 		tks->relief = TKraised;
906 		tks->flag &= ~Dragging;
907 		tk->dirty = tkrect(tk, 1);
908 	}
909 	return e;
910 }
911 
912 static char*
913 tkscalekey(Tk *tk, char *arg, char **val)
914 {
915 	char *e;
916 	int key;
917 	char *pos = nil;
918 	USED(arg);
919 	USED(val);
920 
921 	if(tk->flag & Tkdisabled)
922 		return nil;
923 
924 	key = atoi(arg);
925 	if (key == Up || key == Left)
926 		pos = trough1;
927 	else if (key == Down || key == Right)
928 		pos = trough2;
929 	if (pos != nil) {
930 		e = stepscale(tk, pos, nil);
931 		tk->dirty = tkrect(tk, 1);
932 		return e;
933 	}
934 	return nil;
935 }
936 
937 TkCmdtab tkscalecmd[] =
938 {
939 	"cget",			tkscalecget,
940 	"configure",		tkscaleconf,
941 	"set",			tkscaleset,
942 	"identify",		tkscaleident,
943 	"get",			tkscaleget,
944 	"coords",		tkscalecoords,
945 	"tkScaleMotion",	tkscalemotion,
946 	"tkScaleDrag",		tkscaledrag,
947 	"tkScaleBut1P",		tkscalebut1p,
948 	"tkScaleBut1R",		tkscalebut1r,
949 	"tkScaleKey",		tkscalekey,
950 	nil
951 };
952 
953 TkMethod scalemethod = {
954 	"scale",
955 	tkscalecmd,
956 	tkfreescale,
957 	tkdrawscale
958 };
959