#include #include #include "draw.h" #include "tk.h" #include "keyboard.h" #define O(t, e) ((long)(&((t*)0)->e)) typedef struct TkScale TkScale; struct TkScale { int value; int bigi; int digits; int digwidth; int from; /* Base of range */ int to; /* Limit of range */ int len; /* Length of groove */ int res; /* Resolution */ int sv; /* Show value */ int sl; /* Slider length */ int sw; /* Slider width div 2 */ int relief; int tick; int orient; char* command; char* label; int pixmin; int pixmax; int pixpos; int center; int pix; int base; int flag; int jump; }; enum { Dragging = (1<<0), Autorepeat = (1<<1), }; static TkOption opts[] = { "bigincrement", OPTnnfrac, O(TkScale, bigi), nil, "digits", OPTdist, O(TkScale, digits), nil, "from", OPTfrac, O(TkScale, from), nil, "to", OPTfrac, O(TkScale, to), nil, "length", OPTdist, O(TkScale, len), nil, "resolution", OPTnnfrac, O(TkScale, res), nil, "showrange", OPTignore, 0, nil, "showvalue", OPTstab, O(TkScale, sv), tkbool, "jump", OPTstab, O(TkScale, jump), tkbool, "sliderlength", OPTdist, O(TkScale, sl), nil, "sliderrelief", OPTstab, O(TkScale, relief), tkrelief, "tickinterval", OPTfrac, O(TkScale, tick), nil, "tick", OPTfrac, O(TkScale, tick), nil, "label", OPTtext, O(TkScale, label), nil, "command", OPTtext, O(TkScale, command), nil, "orient", OPTstab, O(TkScale, orient), tkorient, nil }; static char trough1[] = "trough1"; static char trough2[] = "trough2"; static char slider[] = "slider"; static TkEbind b[] = { {TkMotion, "%W tkScaleMotion %x %y"}, {TkButton1P|TkMotion, "%W tkScaleDrag %x %y"}, {TkButton1P, "%W tkScaleMotion %x %y; %W tkScaleBut1P %x %y"}, {TkButton1P|TkDouble, "%W tkScaleMotion %x %y; %W tkScaleBut1P %x %y"}, {TkButton1R, "%W tkScaleDrag %x %y; %W tkScaleBut1R; %W tkScaleMotion %x %y"}, {TkKey, "%W tkScaleKey 0x%K"}, }; enum { Scalewidth = 18, ScalePad = 2, ScaleBW = 1, ScaleSlider = 16, ScaleLen = 80, }; static int maximum(int a, int b) { if (a > b) return a; return b; } void tksizescale(Tk *tk) { Point p; char buf[32]; TkScale *tks; int fh, w, h, digits, digits2; tks = TKobj(TkScale, tk); digits = tks->digits; if(digits <= 0) { digits = tkfprint(buf, tks->from) - buf; digits2 = tkfprint(buf, tks->to) - buf; digits = maximum(digits, digits2); if (tks->res > 0) { digits2 = tkfprint(buf, tks->from + tks->res) - buf; digits = maximum(digits, digits2); digits2 = tkfprint(buf, tks->to - tks->res) - buf; digits = maximum(digits, digits2); } } digits *= tk->env->wzero; if(tks->sv != BoolT) digits = 0; tks->digwidth = digits; p = tkstringsize(tk, tks->label); if(tks->orient == Tkvertical) { h = tks->len + 2*ScaleBW + 2*ScalePad; w = Scalewidth + 2*ScalePad + 2*ScaleBW; if (p.x) w += p.x + ScalePad; if (tks->sv == BoolT) w += digits + ScalePad; } else { w = maximum(p.x, tks->len + ScaleBW + 2*ScalePad); h = Scalewidth + 2*ScalePad + 2*ScaleBW; fh = tk->env->font->height; if(tks->label != nil) h += fh + ScalePad; if(tks->sv == BoolT) h += fh + ScalePad; } w += 2*tk->highlightwidth; h += 2*tk->highlightwidth; if(!(tk->flag & Tksetwidth)) tk->req.width = w; if(!(tk->flag & Tksetheight)) tk->req.height = h; } static int tkscalecheckvalue(Tk *tk) { int v; TkScale *tks = TKobj(TkScale, tk); int limit = 1; v = tks->value; if (tks->res > 0) v = (v / tks->res) * tks->res; if (tks->to >= tks->from) { if (v < tks->from) v = tks->from; else if (v > tks->to) v = tks->to; else limit = 0; } else { if (v < tks->to) v = tks->to; else if (v > tks->from) v = tks->from; else limit = 0; } /* * it's possible for the value to end up as a non-whole * multiple of resolution here, if the end points aren't * themselves such a multiple. if so, tough - that's * what you asked for! (it does mean that the endpoints * are always accessible however, which could be a good thing). */ tks->value = v; return limit; } char* tkscale(TkTop *t, char *arg, char **ret) { Tk *tk; char *e; TkName *names; TkScale *tks; TkOptab tko[3]; tk = tknewobj(t, TKscale, sizeof(Tk)+sizeof(TkScale)); if(tk == nil) return TkNomem; tk->flag |= Tktakefocus; tks = TKobj(TkScale, tk); tks->res = TKI2F(1); tks->to = TKI2F(100); tks->len = ScaleLen; tks->orient = Tkvertical; tks->relief = TKraised; tks->sl = ScaleSlider; tks->sv = BoolT; tks->bigi = 0; tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tks; tko[1].optab = opts; tko[2].ptr = nil; names = nil; e = tkparse(t, arg, tko, &names); if(e != nil) { tkfreeobj(tk); return e; } tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); tkscalecheckvalue(tk); tksizescale(tk); if (tks->bigi == 0) tks->bigi = TKI2F(TKF2I(tks->to - tks->from) / 10); e = tkbindings(t, tk, b, nelem(b)); if(e != nil) { tkfreeobj(tk); return e; } e = tkaddchild(t, tk, &names); tkfreename(names); if(e != nil) { tkfreeobj(tk); return e; } tk->name->link = nil; return tkvalue(ret, "%s", tk->name->name); } static char* tkscalecget(Tk *tk, char *arg, char **val) { TkOptab tko[3]; TkScale *tks = TKobj(TkScale, tk); tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tks; tko[1].optab = opts; tko[2].ptr = nil; return tkgencget(tko, arg, val, tk->env->top); } void tkfreescale(Tk *tk) { TkScale *tks = TKobj(TkScale, tk); if(tks->command != nil) free(tks->command); if(tks->label != nil) free(tks->label); } static void tkscalehoriz(Tk *tk, Image *i) { TkEnv *e; char sv[32]; TkScale *tks; Image *d, *l; Rectangle r, r2, sr; Point p, q; int fh, sh, gh, sl, v, w, h, len; int fgnd; e = tk->env; tks = TKobj(TkScale, tk); fh = e->font->height; fgnd = TkCforegnd; if (tk->flag & Tkdisabled) fgnd = TkCdisablefgnd; r = Rect(0, 0, tk->act.width, tk->act.height); r = rectaddpt(r, Pt(tk->borderwidth, tk->borderwidth)); r = insetrect(r, tk->highlightwidth); r = insetrect(r, ScalePad); if(tks->label != nil) { string(i, r.min, tkgc(e, fgnd), ZP, e->font, tks->label); r.min.y += fh + ScalePad; } if(tks->sv == BoolT) r.min.y += fh + ScalePad; sr = insetrect(r, ScaleBW); w = Dx(sr); h = Dy(sr); sl = tks->sl + 2*ScaleBW; l = tkgc(e, TkCbackgndlght); d = tkgc(e, TkCbackgnddark); tkbevel(i, r.min, w, h, ScaleBW, d, l); tks->pixmin = sr.min.x; tks->pixmax = sr.max.x; sh = h - 2*ScaleBW; tks->sw = sh/2; w -= sl; if (w <= 0) w = 1; p.x = sr.min.x; p.y = sr.max.y; if(tks->tick > 0){ int j, t, l; t = tks->tick; l = tks->to-tks->from; if (l < 0) l = -l; if (l == 0) l = 1; r2.min.y = p.y; r2.max.y = p.y + ScaleBW + ScalePad; for(j = 0; j <= l; j += t){ r2.min.x = p.x+((vlong)j*w)/l+sl/2; r2.max.x = r2.min.x+1; draw(i, r2, tkgc(e, fgnd), nil, ZP); } } v = tks->value-tks->from; len = tks->to-tks->from; if (len != 0) p.x += ((vlong)v*w)/len; p.y = sr.min.y; q = p; q.x += tks->sl/2 + 1; if(ScaleBW > 1) { gh = sh; q.y++; } else gh = sh-1; if(tk->flag & Tkactivated) { r2.min = p; r2.max.x = p.x+sl; r2.max.y = sr.max.y; draw(i, r2, tkgc(e, TkCactivebgnd), nil, ZP); } switch(tks->relief) { case TKsunken: tkbevel(i, p, tks->sl, sh, ScaleBW, d, l); tkbevel(i, q, 0, gh, 1, l, d); break; case TKraised: tkbevel(i, p, tks->sl, sh, ScaleBW, l, d); tkbevel(i, q, 0, gh, 1, d, l); break; } tks->pixpos = p.x; tks->center = p.y + sh/2 + ScaleBW; if(tks->sv != BoolT) return; tkfprint(sv, tks->value); if(tks->digits > 0 && tks->digits < strlen(sv)) sv[tks->digits] = '\0'; w = stringwidth(e->font, sv); p.x = q.x; p.x -= w/2; p.y = r.min.y - fh - ScalePad; if(p.x < tks->pixmin) p.x = tks->pixmin; if(p.x+w > tks->pixmax) p.x = tks->pixmax - w; string(i, p, tkgc(e, fgnd), ZP, e->font, sv); } static void tkscalevert(Tk *tk, Image *i) { TkEnv *e; TkScale *tks; char sv[32]; Image *d, *l; Rectangle r, r2, sr; Point p, q; int fh, v, sw, gw, w, h, len, sl; int fgnd; e = tk->env; tks = TKobj(TkScale, tk); fh = e->font->height; fgnd = TkCforegnd; if (tk->flag & Tkdisabled) fgnd = TkCdisablefgnd; r = Rect(0, 0, tk->act.width, tk->act.height); r = rectaddpt(r, Pt(tk->borderwidth, tk->borderwidth)); r = insetrect(r, tk->highlightwidth); r = insetrect(r, ScalePad); if (tks->sv) r.min.x += tks->digwidth + ScalePad; if(tks->label != nil) { p = stringsize(e->font, tks->label); r.max.x -= p.x; string(i, Pt(r.max.x, r.min.y), tkgc(e, fgnd), ZP, e->font, tks->label); r.max.x -= ScalePad; } sr = insetrect(r, ScaleBW); h = Dy(sr); w = Dx(sr); sl = tks->sl + 2*ScaleBW; l = tkgc(e, TkCbackgndlght); d = tkgc(e, TkCbackgnddark); tkbevel(i, r.min, w, h, ScaleBW, d, l); tks->pixmin = sr.min.y; tks->pixmax = sr.max.y; sw = w - 2*ScaleBW; tks->sw = sw/2; h -= sl; if (h <= 0) h = 1; p.x = sr.max.x; p.y = sr.min.y; if(tks->tick > 0){ int j, t, l; t = tks->tick; l = tks->to-tks->from; if (l < 0) l = -l; if (l == 0) l = 1; r2.min = p; r2.max.x = p.x + ScaleBW + ScalePad; for(j = 0; j <= l; j += t){ r2.min.y = p.y+((vlong)j*h)/l+sl/2; r2.max.y = r2.min.y+1; draw(i, r2, tkgc(e, fgnd), nil, ZP); } } v = tks->value-tks->from; len = tks->to-tks->from; if (len != 0) p.y += ((vlong)v*h)/len; p.x = sr.min.x; q = p; if(ScaleBW > 1) { q.x++; gw = sw; } else gw = sw-1; q.y += tks->sl/2 + 1; if(tk->flag & Tkactivated) { r2.min = p; r2.max.x = sr.max.x; r2.max.y = p.y+sl; draw(i, r2, tkgc(e, TkCactivebgnd), nil, ZP); } switch(tks->relief) { case TKsunken: tkbevel(i, p, sw, tks->sl, ScaleBW, d, l); tkbevel(i, q, gw, 0, 1, l, d); break; case TKraised: tkbevel(i, p, sw, tks->sl, ScaleBW, l, d); tkbevel(i, q, gw, 0, 1, d, l); break; } tks->pixpos = p.y; tks->center = p.x + sw/2 + ScaleBW; if(tks->sv != BoolT) return; tkfprint(sv, tks->value); if(tks->digits > 0 && tks->digits < strlen(sv)) sv[tks->digits] = '\0'; p.x = r.min.x - ScalePad - stringwidth(e->font, sv); p.y = q.y; p.y -= fh/2; if (p.y < tks->pixmin) p.y = tks->pixmin; if (p.y + fh > tks->pixmax) p.y = tks->pixmax - fh; string(i, p, tkgc(e, fgnd), ZP, e->font, sv); } char* tkdrawscale(Tk *tk, Point orig) { Point p; TkEnv *env; TkScale *tks; Rectangle r, fr; Image *i; tks = TKobj(TkScale, tk); env = tk->env; r.min = ZP; r.max.x = tk->act.width + 2*tk->borderwidth; r.max.y = tk->act.height + 2*tk->borderwidth; i = tkitmp(env, r.max, TkCbackgnd); if(i == nil) return nil; if(tks->orient == Tkvertical) tkscalevert(tk, i); else tkscalehoriz(tk, i); tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief); if (tkhaskeyfocus(tk)) { fr = insetrect(r, tk->borderwidth); tkbox(i, fr, tk->highlightwidth, tkgc(env, TkChighlightfgnd)); } p.x = tk->act.x + orig.x; p.y = tk->act.y + orig.y; r = rectaddpt(r, p); draw(tkimageof(tk), r, i, nil, ZP); return nil; } /* Widget Commands (+ means implemented) +cget +configure +coords +get +identify +set */ static char* tkscaleconf(Tk *tk, char *arg, char **val) { char *e; TkGeom g; int bd; TkOptab tko[3]; TkScale *tks = TKobj(TkScale, tk); tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tks; tko[1].optab = opts; tko[2].ptr = nil; if(*arg == '\0') return tkconflist(tko, val); g = tk->req; bd = tk->borderwidth; e = tkparse(tk->env->top, arg, tko, nil); tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); tkscalecheckvalue(tk); tksizescale(tk); tkgeomchg(tk, &g, bd); tk->dirty = tkrect(tk, 1); return e; } char* tkscaleposn(TkEnv *env, Tk *tk, char *arg, int *z) { int x, y; TkScale *tks = TKobj(TkScale, tk); char *e; e = tkfracword(env->top, &arg, &x, env); if(e != nil) return e; e = tkfracword(env->top, &arg, &y, env); if(e != nil) return e; x = TKF2I(x) + tk->borderwidth; y = TKF2I(y) + tk->borderwidth; if(tks->orient == Tkvertical) { if(z != nil) { z[0] = x; z[1] = y; } x = y; } else { if(z != nil) { z[0] = y; z[1] = x; } } if(x > tks->pixmin && x < tks->pixpos) return trough1; else if(x >= tks->pixpos && x < tks->pixpos+tks->sl+2*ScaleBW) return slider; else if(x >= tks->pixpos+tks->sl+2*ScaleBW && x < tks->pixmax) return trough2; return ""; } static char* tkscaleident(Tk *tk, char *arg, char **val) { char *v; v = tkscaleposn(tk->env, tk, arg, nil); if(v == nil) return TkBadvl; return tkvalue(val, "%s", v); } static char* tkscalecoords(Tk *tk, char *arg, char **val) { int p, x, y, l, value; TkScale *tks = TKobj(TkScale, tk); char *e; value = tks->value; if(arg != nil && arg[0] != '\0') { e = tkfracword(tk->env->top, &arg, &value, tk->env); if (e != nil) return e; } value -= tks->from; p = tks->pixmax - tks->pixmin; l = TKF2I(tks->to-tks->from); if (l==0) p /= 2; else p = TKF2I(value*p/l); p += tks->pixmin; if(tks->orient == Tkvertical) { x = tks->center; y = p; } else { x = p; y = tks->center; } return tkvalue(val, "%d %d", x, y); } static char* tkscaleget(Tk *tk, char *arg, char **val) { int x, y, value, v, l; char buf[Tkminitem], *e; TkScale *tks = TKobj(TkScale, tk); value = tks->value; if(arg[0] != '\0') { e = tkfracword(tk->env->top, &arg, &x, tk->env); if (e != nil) return e; e = tkfracword(tk->env->top, &arg, &y, tk->env); if (e != nil) return e; if(tks->orient == Tkvertical) v = TKF2I(y) + tk->borderwidth; else v = TKF2I(x) + tk->borderwidth; if(v < tks->pixmin) value = tks->from; else if(v > tks->pixmax) value = tks->to; else { l = tks->pixmax-tks->pixmin; value = 0; if (l!=0) value = v * ((tks->to-tks->from)/l); value += tks->from; } if(tks->res > 0) value = (value/tks->res)*tks->res; } tkfprint(buf, value); return tkvalue(val, "%s", buf); } static char* tkscaleset(Tk *tk, char *arg, char **val) { TkScale *tks = TKobj(TkScale, tk); char *e; USED(val); e = tkfracword(tk->env->top, &arg, &tks->value, tk->env); if (e != nil) return e; tkscalecheckvalue(tk); tk->dirty = tkrect(tk, 1); return nil; } /* tkScaleMotion %x %y */ static char* tkscalemotion(Tk *tk, char *arg, char **val) { int o, z[2]; char *v; TkScale *tks = TKobj(TkScale, tk); extern int tkstylus; USED(val); v = tkscaleposn(tk->env, tk, arg, z); if(v == nil) return TkBadvl; o = tk->flag; if(v != slider || z[0] < tks->center-tks->sw || z[0] > tks->center+tks->sw) tk->flag &= ~Tkactivated; else if(tkstylus == 0 || tk->env->top->ctxt->mstate.b != 0) tk->flag |= Tkactivated; if((o & Tkactivated) != (tk->flag & Tkactivated)) tk->dirty = tkrect(tk, 1); return nil; } static char* tkscaledrag(Tk *tk, char *arg, char **val) { int x, y, v; char *e, buf[Tkmaxitem], f[32]; TkScale *tks = TKobj(TkScale, tk); USED(val); if((tks->flag & Dragging) == 0) return nil; if(tks->flag & Autorepeat) return nil; e = tkfracword(tk->env->top, &arg, &x, tk->env); if(e != nil) return e; e = tkfracword(tk->env->top, &arg, &y, tk->env); if(e != nil) return e; if(tks->orient == Tkvertical) v = TKF2I(y) + tk->borderwidth; else v = TKF2I(x) + tk->borderwidth; v -= tks->pix; x = tks->pixmax-tks->pixmin; if (x!=tks->sl) v = tks->base + (vlong)v * (tks->to-tks->from)/(x-tks->sl); else v = tks->base; if(tks->res > 0) { int a = tks->res / 2; if (v < 0) a = -a; v = ((v+a)/tks->res)*tks->res; } tks->value = v; tkscalecheckvalue(tk); if(tks->command != nil && tks->jump != BoolT) { tkfprint(f, tks->value); snprint(buf, sizeof(buf), "%s %s", tks->command, f); e = tkexec(tk->env->top, buf, nil); } tk->dirty = tkrect(tk, 1); return e; } static int sgn(int v) { return v >= 0 ? 1 : -1; } static char* stepscale(Tk *tk, char *pos, int *end) { TkScale *tks = TKobj(TkScale, tk); char *e, buf[Tkmaxitem], f[32]; int s; s = sgn(tks->to - tks->from); if(pos == trough1) { tks->value -= s * tks->bigi; } else { /* trough2 */ tks->value += s * tks->bigi; } s = !tkscalecheckvalue(tk); if (end != nil) *end = s; e = nil; if(tks->command != nil) { /* XXX perhaps should only send command if value has actually changed */ tkfprint(f, tks->value); snprint(buf, sizeof(buf), "%s %s", tks->command, f); e = tkexec(tk->env->top, buf, nil); } return e; } static void screpeat(Tk *tk, void *v, int cancelled) { char *e, *pos; int repeat; TkScale *tks = TKobj(TkScale, tk); pos = v; if (cancelled) { tks->flag &= ~Autorepeat; return; } e = stepscale(tk, pos, &repeat); if(e != nil || !repeat) { tks->flag &= ~Autorepeat; tkcancelrepeat(tk); } tk->dirty = tkrect(tk, 1); tkupdate(tk->env->top); } static char* tkscalebut1p(Tk *tk, char *arg, char **val) { int z[2]; char *v, *e; TkScale *tks = TKobj(TkScale, tk); int repeat; USED(val); v = tkscaleposn(tk->env, tk, arg, z); if(v == nil) return TkBadvl; e = nil; if(v[0] == '\0' || z[0] < tks->center-tks->sw || z[0] > tks->center+tks->sw) return nil; if(v == slider) { tks->flag |= Dragging; tks->relief = TKsunken; tks->pix = z[1]; tks->base = tks->value; tkscalecheckvalue(tk); } else { e = stepscale(tk, v, &repeat); if (e == nil && repeat) { tks->flag |= Autorepeat; tkrepeat(tk, screpeat, v, TkRptpause, TkRptinterval); } } tk->dirty = tkrect(tk, 1); return e; } static char* tkscalebut1r(Tk *tk, char *arg, char **val) { TkScale *tks = TKobj(TkScale, tk); char *e, buf[Tkmaxitem], f[32]; USED(val); USED(arg); if(tks->flag & Autorepeat) { tkcancelrepeat(tk); tks->flag &= ~Autorepeat; } e = nil; if (tks->flag & Dragging) { if (tks->command != nil && tks->jump == BoolT && (tks->flag & Dragging)) { tkfprint(f, tks->value); snprint(buf, sizeof(buf), "%s %s", tks->command, f); e = tkexec(tk->env->top, buf, nil); } tks->relief = TKraised; tks->flag &= ~Dragging; tk->dirty = tkrect(tk, 1); } return e; } static char* tkscalekey(Tk *tk, char *arg, char **val) { char *e; int key; char *pos = nil; USED(arg); USED(val); if(tk->flag & Tkdisabled) return nil; key = strtol(arg, nil, 0); if (key == Up || key == Left) pos = trough1; else if (key == Down || key == Right) pos = trough2; if (pos != nil) { e = stepscale(tk, pos, nil); tk->dirty = tkrect(tk, 1); return e; } return nil; } TkCmdtab tkscalecmd[] = { "cget", tkscalecget, "configure", tkscaleconf, "set", tkscaleset, "identify", tkscaleident, "get", tkscaleget, "coords", tkscalecoords, "tkScaleMotion", tkscalemotion, "tkScaleDrag", tkscaledrag, "tkScaleBut1P", tkscalebut1p, "tkScaleBut1R", tkscalebut1r, "tkScaleKey", tkscalekey, nil }; TkMethod scalemethod = { "scale", tkscalecmd, tkfreescale, tkdrawscale };