diff options
Diffstat (limited to 'libtk/entry.c')
| -rw-r--r-- | libtk/entry.c | 1379 |
1 files changed, 1379 insertions, 0 deletions
diff --git a/libtk/entry.c b/libtk/entry.c new file mode 100644 index 00000000..344e39c4 --- /dev/null +++ b/libtk/entry.c @@ -0,0 +1,1379 @@ +#include <lib9.h> +#include <kernel.h> +#include "draw.h" +#include "keyboard.h" +#include "tk.h" + +/* Widget Commands (+ means implemented) + +bbox + +cget + +configure + +delete + +get + +icursor + +index + scan + +selection + +xview + +see +*/ + +#define O(t, e) ((long)(&((t*)0)->e)) + +#define CNTL(c) ((c)&0x1f) +#define DEL 0x7f + +/* Layout constants */ +enum { + Entrypady = 0, + Entrypadx = 0, + Inswidth = 2, + + Ecursoron = 1<<0, + Ecenter = 1<<1, + Eright = 1<<2, + Eleft = 1<<3, + Ewordsel = 1<<4, + + Ejustify = Ecenter|Eleft|Eright +}; + +static TkStab tkjust[] = +{ + "left", Eleft, + "right", Eright, + "center", Ecenter, + nil +}; + +static +TkEbind b[] = +{ + {TkKey, "%W delete sel.first sel.last; %W insert insert {%A};%W see insert"}, + {TkKey|CNTL('a'), "%W icursor 0;%W see insert;%W selection clear"}, + {TkKey|Home, "%W icursor 0;%W see insert;%W selection clear"}, + {TkKey|CNTL('d'), "%W delete insert; %W see insert"}, + {TkKey|CNTL('e'), "%W icursor end; %W see insert;%W selection clear"}, + {TkKey|End, "%W icursor end; %W see insert;%W selection clear"}, + {TkKey|CNTL('h'), "%W tkEntryBS;%W see insert"}, + {TkKey|CNTL('k'), "%W delete insert end;%W see insert"}, + {TkKey|CNTL('u'), "%W delete 0 end;%W see insert"}, + {TkKey|CNTL('w'), "%W delete sel.first sel.last; %W tkEntryBW;%W see insert"}, + {TkKey|DEL, "%W tkEntryBS 1;%W see insert"}, + {TkKey|CNTL('\\'), "%W selection clear"}, + {TkKey|CNTL('/'), "%W selection range 0 end"}, + {TkKey|Left, "%W icursor insert-1;%W selection clear;%W selection from insert;%W see insert"}, + {TkKey|Right, "%W icursor insert+1;%W selection clear;%W selection from insert;%W see insert"}, + {TkButton1P, "focus %W; %W tkEntryB1P %X"}, + {TkButton1P|TkMotion, "%W tkEntryB1M %X"}, + {TkButton1R, "%W tkEntryB1R"}, + {TkButton1P|TkDouble, "%W tkEntryB1P %X;%W selection word @%x"}, + {TkButton2P, "%W tkEntryB2P %x"}, + {TkButton2P|TkMotion, "%W xview scroll %x scr"}, + {TkFocusin, "%W tkEntryFocus in"}, + {TkFocusout, "%W tkEntryFocus out"}, + {TkKey|APP|'\t', ""}, + {TkKey|BackTab, ""}, +}; + +typedef struct TkEntry TkEntry; +struct TkEntry +{ + Rune* text; + int textlen; + + char* xscroll; + char* show; + int flag; + int oldx; + + int icursor; /* index of insertion cursor */ + int anchor; /* selection anchor point */ + int sel0; /* index of start of selection */ + int sel1; /* index of end of selection */ + + int x0; /* x-offset of visible area */ + + /* derived values */ + int v0; /* index of first visible character */ + int v1; /* index of last visible character + 1 */ + int xlen; /* length of text in pixels*/ + int xv0; /* position of first visible character */ + int xsel0; /* position of start of selection */ + int xsel1; /* position of end of selection */ + int xicursor; /* position of insertion cursor */ +}; + +static void blinkreset(Tk*); + +static +TkOption opts[] = +{ + "xscrollcommand", OPTtext, O(TkEntry, xscroll), nil, + "justify", OPTstab, O(TkEntry, flag), tkjust, + "show", OPTtext, O(TkEntry, show), nil, + nil +}; + +static int +xinset(Tk *tk) +{ + return Entrypadx + tk->highlightwidth; +} + +static int +yinset(Tk *tk) +{ + return Entrypady + tk->highlightwidth; +} + +static void +tksizeentry(Tk *tk) +{ + if((tk->flag & Tksetwidth) == 0) + tk->req.width = tk->env->wzero*25 + 2*xinset(tk) + Inswidth; + if((tk->flag & Tksetheight) == 0) + tk->req.height = tk->env->font->height+ 2*yinset(tk); +} + +int +entrytextwidth(Tk *tk, int n) +{ + TkEntry *tke = TKobj(TkEntry, tk); + Rune c; + Font *f; + + f = tk->env->font; + if (tke->show != nil) { + chartorune(&c, tke->show); + return n * runestringnwidth(f, &c, 1); + } + return runestringnwidth(f, tke->text, n); +} + +static int +x2index(Tk *tk, int x, int *xc) +{ + TkEntry *tke = TKobj(TkEntry, tk); + int t0, t1, r, q; + + t0 = 0; + t1 = tke->textlen; + while (t0 <= t1) { + r = (t0 + t1) / 2; + q = entrytextwidth(tk, r); + if (q == x) { + if (xc != nil) + *xc = q; + return r; + } + if (q < x) + t0 = r + 1; + else + t1 = r - 1; + } + if (xc != nil) + *xc = t1 > 0 ? entrytextwidth(tk, t1) : 0; + if (t1 < 0) + t1 = 0; + return t1; +} + +static int +x2index0(Tk *tk, int x, int *xc) +{ + int xx, z; + z = x2index(tk, x, &xx); + print("x2index(%d)-> (%d, %d)\n", x, z, xx); + if (xc) + *xc = xx; + return z; +} + +/* + * recalculate derived values + */ +static void +recalcentry(Tk *tk) +{ + TkEntry *tke = TKobj(TkEntry, tk); + int x, avail, locked; + + locked = lockdisplay(tk->env->top->display); + + tke->xlen = entrytextwidth(tk, tke->textlen) + Inswidth; + + avail = tk->act.width - 2*xinset(tk); + if (tke->xlen < avail) { + switch(tke->flag & Ejustify) { + default: + tke->x0 = 0; + break; + case Eright: + tke->x0 = -(avail - tke->xlen); + break; + case Ecenter: + tke->x0 = -(avail - tke->xlen) / 2; + break; + } + } + + tke->v0 = x2index(tk, tke->x0, &tke->xv0); + tke->v1 = x2index(tk, tk->act.width + tke->x0, &x); + /* perhaps include partial last character */ + if (tke->v1 < tke->textlen && x < avail + tke->x0) + tke->v1++; + tke->xsel0 = entrytextwidth(tk, tke->sel0); + tke->xsel1 = entrytextwidth(tk, tke->sel1); + tke->xicursor = entrytextwidth(tk, tke->icursor); + + if (locked) + unlockdisplay(tk->env->top->display); +} + +char* +tkentry(TkTop *t, char *arg, char **ret) +{ + Tk *tk; + char *e; + TkName *names; + TkEntry *tke; + TkOptab tko[3]; + + tk = tknewobj(t, TKentry, sizeof(Tk)+sizeof(TkEntry)); + if(tk == nil) + return TkNomem; + + tk->relief = TKsunken; + tk->borderwidth = 2; + tk->flag |= Tktakefocus; + tk->highlightwidth = 1; + + tke = TKobj(TkEntry, tk); + + tko[0].ptr = tk; + tko[0].optab = tkgeneric; + tko[1].ptr = tke; + 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)); + tksizeentry(tk); + 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; + recalcentry(tk); + + return tkvalue(ret, "%s", tk->name->name); +} + +static char* +tkentrycget(Tk *tk, char *arg, char **val) +{ + TkOptab tko[3]; + TkEntry *tke = TKobj(TkEntry, tk); + + tko[0].ptr = tk; + tko[0].optab = tkgeneric; + tko[1].ptr = tke; + tko[1].optab = opts; + tko[2].ptr = nil; + + return tkgencget(tko, arg, val, tk->env->top); +} + +void +tkfreeentry(Tk *tk) +{ + TkEntry *tke = TKobj(TkEntry, tk); + + free(tke->xscroll); + free(tke->text); + free(tke->show); +} + +static void +tkentrytext(Image *i, Rectangle s, Tk *tk, TkEnv *env) +{ + TkEntry *tke = TKobj(TkEntry, tk); + Point dp; + int s0, s1, xs0, xs1, j; + Rectangle r; + Rune showr, *text; + + dp = Pt(s.min.x - (tke->x0 - tke->xv0), s.min.y); + if (tke->show) { + chartorune(&showr, tke->show); + text = mallocz(sizeof(Rune) * (tke->textlen+1), 0); + if (text == nil) + return; + for (j = 0; j < tke->textlen; j++) + text[j] = showr; + } else + text = tke->text; + + runestringn(i, dp, tkgc(env, TkCforegnd), dp, env->font, + text+tke->v0, tke->v1-tke->v0); + + if (tke->sel0 < tke->v1 && tke->sel1 > tke->v0) { + if (tke->sel0 < tke->v0) { + s0 = tke->v0; + xs0 = tke->xv0 - tke->x0; + } else { + s0 = tke->sel0; + xs0 = tke->xsel0 - tke->x0; + } + + if (tke->sel1 > tke->v1) { + s1 = tke->v1; + xs1 = s.max.x; + } else { + s1 = tke->sel1; + xs1 = tke->xsel1 - tke->x0; + } + + r = rectaddpt(Rect(xs0, 0, xs1, env->font->height), s.min); + tktextsdraw(i, r, env, 1); + runestringn(i, r.min, tkgc(env, TkCselectfgnd), r.min, env->font, + text+s0, s1-s0); + } + + if((tke->flag&Ecursoron) && tke->icursor >= tke->v0 && tke->icursor <= tke->v1) { + r = Rect( + tke->xicursor - tke->x0, 0, + tke->xicursor - tke->x0 + Inswidth, env->font->height + ); + draw(i, rectaddpt(r, s.min), tkgc(env, TkCforegnd), nil, ZP); + } + if (tke->show) + free(text); +} + +char* +tkdrawentry(Tk *tk, Point orig) +{ + Point p; + TkEnv *env; + Rectangle r, s; + Image *i; + int xp, yp; + + 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; + + xp = tk->borderwidth + xinset(tk); + yp = tk->borderwidth + yinset(tk); + s = r; + s.min.x += xp; + s.max.x -= xp; + s.min.y += yp; + s.max.y -= yp; + tkentrytext(i, s, tk, env); + + tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief); + + if (tkhaskeyfocus(tk)) + tkbox(i, insetrect(r, tk->borderwidth), tk->highlightwidth, tkgc(tk->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; +} + +char* +tkentrysh(Tk *tk) +{ + TkEntry *tke = TKobj(TkEntry, tk); + int dx, top, bot; + char *val, *cmd, *v, *e; + + if(tke->xscroll == nil) + return nil; + + bot = 0; + top = Tkfpscalar; + + if(tke->text != 0 && tke->textlen != 0) { + dx = tk->act.width - 2*xinset(tk); + + if (tke->xlen > dx) { + bot = TKI2F(tke->x0) / tke->xlen; + top = TKI2F(tke->x0 + dx) / tke->xlen; + } + } + + val = mallocz(Tkminitem, 0); + if(val == nil) + return TkNomem; + v = tkfprint(val, bot); + *v++ = ' '; + tkfprint(v, top); + cmd = mallocz(Tkminitem, 0); + if(cmd == nil) { + free(val); + return TkNomem; + } + sprint(cmd, "%s %s", tke->xscroll, val); + e = tkexec(tk->env->top, cmd, nil); + free(cmd); + free(val); + return e; +} + +void +tkentrygeom(Tk *tk) +{ + char *e; + e = tkentrysh(tk); + if ((e != nil) && /* XXX - Tad: should propagate not print */ + (tk->name != nil)) + print("tk: xscrollcommand \"%s\": %s\n", tk->name->name, e); + recalcentry(tk); +} + +static char* +tkentryconf(Tk *tk, char *arg, char **val) +{ + char *e; + TkGeom g; + int bd; + TkOptab tko[3]; + TkEntry *tke = TKobj(TkEntry, tk); + + tko[0].ptr = tk; + tko[0].optab = tkgeneric; + tko[1].ptr = tke; + tko[1].optab = opts; + tko[2].ptr = nil; + + if(*arg == '\0') + return tkconflist(tko, val); + + bd = tk->borderwidth; + g = tk->req; + e = tkparse(tk->env->top, arg, tko, nil); + tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); + tksizeentry(tk); + tkgeomchg(tk, &g, bd); + recalcentry(tk); + tk->dirty = tkrect(tk, 1); + return e; +} + +static char* +tkentryparseindex(Tk *tk, char *buf, int *index) +{ + TkEntry *tke = TKobj(TkEntry, tk); + TkEnv *env; + char *mod; + int i, x, locked, modstart; + + modstart = 0; + for(mod = buf; *mod != '\0'; mod++) + if(*mod == '-' || *mod == '+') { + modstart = *mod; + *mod = '\0'; + break; + } + if(strcmp(buf, "end") == 0) + i = tke->textlen; + else + if(strcmp(buf, "anchor") == 0) + i = tke->anchor; + else + if(strcmp(buf, "insert") == 0) + i = tke->icursor; + else + if(strcmp(buf, "sel.first") == 0) + i = tke->sel0; + else + if(strcmp(buf, "sel.last") == 0) + i = tke->sel1; + else + if(buf[0] >= '0' && buf[0] <= '9') + i = atoi(buf); + else + if(buf[0] == '@') { + x = atoi(buf+1) - xinset(tk); + if(tke->textlen == 0) { + *index = 0; + return nil; + } + env = tk->env; + locked = lockdisplay(env->top->display); + i = x2index(tk, x + tke->x0, nil); /* XXX could possibly select nearest character? */ + if(locked) + unlockdisplay(env->top->display); + } + else + return TkBadix; + + if(i < 0 || i > tke->textlen) + return TkBadix; + if(modstart) { + *mod = modstart; + i += atoi(mod); + if(i < 0) + i = 0; + if(i > tke->textlen) + i = tke->textlen; + } + *index = i; + return nil; +} + +/* + * return bounding box of character at index, in coords relative to + * the top left position of the text. + */ +static Rectangle +tkentrybbox(Tk *tk, int index) +{ + TkEntry *tke; + TkEnv *env; + Display *d; + int x, cw, locked; + Rectangle r; + + tke = TKobj(TkEntry, tk); + env = tk->env; + + d = env->top->display; + + locked = lockdisplay(d); + x = entrytextwidth(tk, index); + if (index < tke->textlen) + cw = entrytextwidth(tk, index+1) - x; + else + cw = Inswidth; + if(locked) + unlockdisplay(d); + + r.min.x = x; + r.min.y = 0; + r.max.x = x + cw; + r.max.y = env->font->height; + return r; +} + +static void +tkentrysee(Tk *tk, int index, int jump) +{ + TkEntry *tke = TKobj(TkEntry, tk); + int dx, margin; + Rectangle r; + + r = tkentrybbox(tk, index); + dx = tk->act.width - 2*xinset(tk); + if (jump) + margin = dx / 4; + else + margin = 0; + if (r.min.x <= tke->x0 || r.max.x > tke->x0 + dx) { + if (r.min.x <= tke->x0) { + tke->x0 = r.min.x - margin; + if (tke->x0 < 0) + tke->x0 = 0; + } else if (r.max.x >= tke->x0 + dx) { + tke->x0 = r.max.x - dx + margin; + if (tke->x0 > tke->xlen - dx) + tke->x0 = tke->xlen - dx; + } + tk->dirty = tkrect(tk, 0); + } + r = rectaddpt(r, Pt(xinset(tk) - tke->x0, yinset(tk))); + tksee(tk, r, r.min); +} + +static char* +tkentryseecmd(Tk *tk, char *arg, char **val) +{ + int index; + char *e, *buf; + + USED(val); + + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &index); + free(buf); + if(e != nil) + return e; + + tkentrysee(tk, index, 1); + recalcentry(tk); + + return nil; +} + +static char* +tkentrybboxcmd(Tk *tk, char *arg, char **val) +{ + TkEntry *tke = TKobj(TkEntry, tk); + char *r, *buf; + int index; + Rectangle bbox; + + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); + r = tkentryparseindex(tk, buf, &index); + free(buf); + if(r != nil) + return r; + bbox = rectaddpt(tkentrybbox(tk, index), Pt(xinset(tk) - tke->x0, yinset(tk))); + return tkvalue(val, "%d %d %d %d", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y); +} + +static char* +tkentryindex(Tk *tk, char *arg, char **val) +{ + int index; + char *r, *buf; + + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); + r = tkentryparseindex(tk, buf, &index); + free(buf); + if(r != nil) + return r; + return tkvalue(val, "%d", index); +} + +static char* +tkentryicursor(Tk *tk, char *arg, char **val) +{ + TkEntry *tke = TKobj(TkEntry, tk); + int index, locked; + char *r, *buf; + + USED(val); + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); + r = tkentryparseindex(tk, buf, &index); + free(buf); + if(r != nil) + return r; + tke->icursor = index; + locked = lockdisplay(tk->env->top->display); + tke->xicursor = entrytextwidth(tk, tke->icursor); + if (locked) + unlockdisplay(tk->env->top->display); + + blinkreset(tk); + tk->dirty = tkrect(tk, 1); + return nil; +} + +static int +adjustforins(int i, int n, int q) +{ + if (i <= q) + q += n; + return q; +} + +static int +adjustfordel(int d0, int d1, int q) +{ + if (d1 <= q) + q -= d1 - d0; + else if (d0 <= q && q <= d1) + q = d0; + return q; +} + +static char* +tkentryget(Tk *tk, char *arg, char **val) +{ + TkTop *top; + TkEntry *tke; + int first, last; + char *e, *buf; + + tke = TKobj(TkEntry, tk); + if(tke->text == nil) + return nil; + + arg = tkskip(arg, " \t"); + if(*arg == '\0') + return tkvalue(val, "%.*S", tke->textlen, tke->text); + + top = tk->env->top; + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &first); + if(e != nil) { + free(buf); + return e; + } + last = first+1; + tkword(top, arg, buf, buf+Tkmaxitem, nil); + if(buf[0] != '\0') { + e = tkentryparseindex(tk, buf, &last); + if(e != nil) { + free(buf); + return e; + } + } + free(buf); + if(last <= first || tke->textlen == 0 || first == tke->textlen) + return tkvalue(val, "%S", L""); + return tkvalue(val, "%.*S", last-first, tke->text+first); +} + +static char* +tkentryinsert(Tk *tk, char *arg, char **val) +{ + TkTop *top; + TkEntry *tke; + int ins, i, n, locked; + char *e, *t, *text, *buf; + Rune *etext; + + USED(val); + tke = TKobj(TkEntry, tk); + + top = tk->env->top; + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &ins); + free(buf); + if(e != nil) + return e; + + if(*arg == '\0') + return nil; + + n = strlen(arg) + 1; + if(n < Tkmaxitem) + n = Tkmaxitem; + text = malloc(n); + if(text == nil) + return TkNomem; + + tkword(top, arg, text, text+n, nil); + n = utflen(text); + etext = realloc(tke->text, (tke->textlen+n+1)*sizeof(Rune)); + if(etext == nil) { + free(text); + return TkNomem; + } + tke->text = etext; + + memmove(tke->text+ins+n, tke->text+ins, (tke->textlen-ins)*sizeof(Rune)); + t = text; + for(i=0; i<n; i++) + t += chartorune(tke->text+ins+i, t); + free(text); + + tke->textlen += n; + + tke->sel0 = adjustforins(ins, n, tke->sel0); + tke->sel1 = adjustforins(ins, n, tke->sel1); + tke->icursor = adjustforins(ins, n, tke->icursor); + tke->anchor = adjustforins(ins, n, tke->anchor); + + locked = lockdisplay(tk->env->top->display); + if (ins < tke->v0) + tke->x0 += entrytextwidth(tk, tke->v0 + n) + (tke->x0 - tke->xv0); + if (locked) + unlockdisplay(tk->env->top->display); + recalcentry(tk); + + e = tkentrysh(tk); + blinkreset(tk); + tk->dirty = tkrect(tk, 1); + + return e; +} + +static char* +tkentrydelete(Tk *tk, char *arg, char **val) +{ + TkTop *top; + TkEntry *tke; + int d0, d1, locked; + char *e, *buf; + Rune *text; + + USED(val); + + tke = TKobj(TkEntry, tk); + + top = tk->env->top; + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &d0); + if(e != nil) { + free(buf); + return e; + } + + d1 = d0+1; + tkword(top, arg, buf, buf+Tkmaxitem, nil); + if(buf[0] != '\0') { + e = tkentryparseindex(tk, buf, &d1); + if(e != nil) { + free(buf); + return e; + } + } + free(buf); + if(d1 <= d0 || tke->textlen == 0 || d0 >= tke->textlen) + return nil; + + memmove(tke->text+d0, tke->text+d1, (tke->textlen-d1)*sizeof(Rune)); + tke->textlen -= d1 - d0; + + text = realloc(tke->text, (tke->textlen+1) * sizeof(Rune)); + if (text != nil) + tke->text = text; + tke->sel0 = adjustfordel(d0, d1, tke->sel0); + tke->sel1 = adjustfordel(d0, d1, tke->sel1); + tke->icursor = adjustfordel(d0, d1, tke->icursor); + tke->anchor = adjustfordel(d0, d1, tke->anchor); + + locked = lockdisplay(tk->env->top->display); + if (d1 < tke->v0) + tke->x0 = entrytextwidth(tk, tke->v0 - (d1 - d0)) + (tke->x0 - tke->xv0); + else if (d0 < tke->v0) + tke->x0 = entrytextwidth(tk, d0); + if (locked) + unlockdisplay(tk->env->top->display); + recalcentry(tk); + + e = tkentrysh(tk); + blinkreset(tk); + tk->dirty = tkrect(tk, 1); + + return e; +} + +/* Used for both backspace and DEL. If a selection exists, delete it. + * Otherwise delete the character to the left(right) of the insertion + * cursor, if any. + */ +static char* +tkentrybs(Tk *tk, char *arg, char **val) +{ + TkEntry *tke = TKobj(TkEntry, tk); + char *buf, *e; + int ix; + + USED(val); + USED(arg); + + if(tke->textlen == 0) + return nil; + + if(tke->sel0 < tke->sel1) + return tkentrydelete(tk, "sel.first sel.last", nil); + + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); + ix = -1; + if(buf[0] != '\0') { + e = tkentryparseindex(tk, buf, &ix); + if(e != nil) { + free(buf); + return e; + } + } + if(ix > -1) { /* DEL */ + if(tke->icursor >= tke->textlen) { + free(buf); + return nil; + } + } + else { /* backspace */ + if(tke->icursor == 0) { + free(buf); + return nil; + } + tke->icursor--; + } + snprint(buf, Tkmaxitem, "%d", tke->icursor); + e = tkentrydelete(tk, buf, nil); + free(buf); + return e; +} + +static char* +tkentrybw(Tk *tk, char *arg, char **val) +{ + int start; + Rune *text; + TkEntry *tke; + char buf[32]; + + USED(val); + USED(arg); + + tke = TKobj(TkEntry, tk); + if(tke->textlen == 0 || tke->icursor == 0) + return nil; + + text = tke->text; + start = tke->icursor-1; + while(start > 0 && !tkiswordchar(text[start])) + --start; + while(start > 0 && tkiswordchar(text[start-1])) + --start; + + snprint(buf, sizeof(buf), "%d %d", start, tke->icursor); + return tkentrydelete(tk, buf, nil); +} + +char* +tkentryselect(Tk *tk, char *arg, char **val) +{ + TkTop *top; + int start, from, to, locked; + TkEntry *tke; + char *e, *buf; + + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + + tke = TKobj(TkEntry, tk); + + top = tk->env->top; + arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); + if(strcmp(buf, "clear") == 0) { + tke->sel0 = 0; + tke->sel1 = 0; + } + else + if(strcmp(buf, "from") == 0) { + tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &tke->anchor); + tke->flag &= ~Ewordsel; + free(buf); + return e; + } + else + if(strcmp(buf, "to") == 0) { + tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &to); + if(e != nil) { + free(buf); + return e; + } + + if(to < tke->anchor) { + if(tke->flag & Ewordsel) + while(to > 0 && tkiswordchar(tke->text[to-1])) + --to; + tke->sel0 = to; + tke->sel1 = tke->anchor; + } + else + if(to >= tke->anchor) { + if(tke->flag & Ewordsel) + while(to < tke->textlen && + tkiswordchar(tke->text[to])) + to++; + tke->sel0 = tke->anchor; + tke->sel1 = to; + } + tkentrysee(tk, to, 0); + recalcentry(tk); + } + else + if(strcmp(buf, "word") == 0) { /* inferno invention */ + tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &start); + if(e != nil) { + free(buf); + return e; + } + from = start; + while(from > 0 && tkiswordchar(tke->text[from-1])) + --from; + to = start; + while(to < tke->textlen && tkiswordchar(tke->text[to])) + to++; + tke->sel0 = from; + tke->sel1 = to; + tke->anchor = from; + tke->icursor = from; + tke->flag |= Ewordsel; + locked = lockdisplay(tk->env->top->display); + tke->xicursor = entrytextwidth(tk, tke->icursor); + if (locked) + unlockdisplay(tk->env->top->display); + } + else + if(strcmp(buf, "present") == 0) { + e = tkvalue(val, "%d", tke->sel1 > tke->sel0); + free(buf); + return e; + } + else + if(strcmp(buf, "range") == 0) { + arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &from); + if(e != nil) { + free(buf); + return e; + } + tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &to); + if(e != nil) { + free(buf); + return e; + } + tke->sel0 = from; + tke->sel1 = to; + if(to <= from) { + tke->sel0 = 0; + tke->sel1 = 0; + } + } + else + if(strcmp(buf, "adjust") == 0) { + tkword(top, arg, buf, buf+Tkmaxitem, nil); + e = tkentryparseindex(tk, buf, &to); + if(e != nil) { + free(buf); + return e; + } + if(tke->sel0 == 0 && tke->sel1 == 0) { + tke->sel0 = tke->anchor; + tke->sel1 = to; + } + else { + if(abs(tke->sel0-to) < abs(tke->sel1-to)) { + tke->sel0 = to; + tke->anchor = tke->sel1; + } + else { + tke->sel1 = to; + tke->anchor = tke->sel0; + } + } + if(tke->sel0 > tke->sel1) { + to = tke->sel0; + tke->sel0 = tke->sel1; + tke->sel1 = to; + } + } + else { + free(buf); + return TkBadcm; + } + locked = lockdisplay(tk->env->top->display); + tke->xsel0 = entrytextwidth(tk, tke->sel0); + tke->xsel1 = entrytextwidth(tk, tke->sel1); + if (locked) + unlockdisplay(tk->env->top->display); + tk->dirty = tkrect(tk, 1); + free(buf); + return nil; +} + + +static char* +tkentryb2p(Tk *tk, char *arg, char **val) +{ + TkEntry *tke; + char *buf; + + USED(val); + + tke = TKobj(TkEntry, tk); + buf = malloc(Tkmaxitem); + if (buf == nil) + return TkNomem; + + tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); + tke->oldx = atoi(buf); + return nil; +} + +static char* +tkentryxview(Tk *tk, char *arg, char **val) +{ + int locked; + TkEnv *env; + TkEntry *tke; + char *buf, *v; + int dx, top, bot, amount, ix, x; + char *e; + + tke = TKobj(TkEntry, tk); + env = tk->env; + dx = tk->act.width - 2*xinset(tk); + + buf = mallocz(Tkmaxitem, 0); + if(buf == nil) + return TkNomem; + + if(*arg == '\0') { + if (tke->textlen == 0 || tke->xlen < dx) { + bot = TKI2F(0); + top = TKI2F(1); + } else { + bot = TKI2F(tke->x0) / tke->xlen; + top = TKI2F(tke->x0 + dx) / tke->xlen; + } + v = tkfprint(buf, bot); + *v++ = ' '; + tkfprint(v, top); + e = tkvalue(val, "%s", buf); + free(buf); + return e; + } + + arg = tkitem(buf, arg); + if(strcmp(buf, "moveto") == 0) { + e = tkfracword(env->top, &arg, &top, nil); + if (e != nil) { + free(buf); + return e; + } + tke->x0 = TKF2I(top*tke->xlen); + } + else + if(strcmp(buf, "scroll") == 0) { + arg = tkitem(buf, arg); + amount = atoi(buf); + if(*arg == 'p') /* Pages */ + amount *= (9*tke->xlen)/10; + else + if(*arg == 's') { /* Inferno-ism, "scr", must be used in the context of button2p */ + x = amount; + amount = x < tke->oldx ? env->wzero : (x > tke->oldx ? -env->wzero : 0); + tke->oldx = x; + } + tke->x0 += amount; + } + else { + e = tkentryparseindex(tk, buf, &ix); + if(e != nil) { + free(buf); + return e; + } + locked = lockdisplay(env->top->display); + tke->x0 = entrytextwidth(tk, ix); + if (locked) + unlockdisplay(env->top->display); + } + free(buf); + + if (tke->x0 > tke->xlen - dx) + tke->x0 = tke->xlen - dx; + if (tke->x0 < 0) + tke->x0 = 0; + recalcentry(tk); + e = tkentrysh(tk); + blinkreset(tk); + tk->dirty = tkrect(tk, 1); + return e; +} + +static void +autoselect(Tk *tk, void *v, int cancelled) +{ + TkEntry *tke = TKobj(TkEntry, tk); + Rectangle hitr; + char buf[32]; + Point p; + + USED(v); + + if (cancelled) + return; + + p = tkscrn2local(tk, Pt(tke->oldx, 0)); + p.y = 0; + if (tkvisiblerect(tk, &hitr) && ptinrect(p, hitr)) + return; + + snprint(buf, sizeof(buf), "to @%d", p.x); + tkentryselect(tk, buf, nil); + tkdirty(tk); + tkupdate(tk->env->top); +} + +static char* +tkentryb1p(Tk *tk, char* arg, char **ret) +{ + TkEntry *tke = TKobj(TkEntry, tk); + Point p; + int i, locked, x; + char buf[32], *e; + USED(ret); + + x = atoi(arg); + p = tkscrn2local(tk, Pt(x, 0)); + sprint(buf, "@%d", p.x); + e = tkentryparseindex(tk, buf, &i); + if (e != nil) + return e; + tke->sel0 = 0; + tke->sel1 = 0; + tke->icursor = i; + tke->anchor = i; + tke->flag &= ~Ewordsel; + + locked = lockdisplay(tk->env->top->display); + tke->xsel0 = 0; + tke->xsel1 = 0; + tke->xicursor = entrytextwidth(tk, tke->icursor); + if (locked) + unlockdisplay(tk->env->top->display); + + tke->oldx = x; + blinkreset(tk); + tkrepeat(tk, autoselect, nil, TkRptpause, TkRptinterval); + tk->dirty = tkrect(tk, 0); + return nil; +} + +static char* +tkentryb1m(Tk *tk, char* arg, char **ret) +{ + TkEntry *tke = TKobj(TkEntry, tk); + Point p; + Rectangle hitr; + char buf[32]; + USED(ret); + + p.x = atoi(arg); + tke->oldx = p.x; + p = tkscrn2local(tk, p); + p.y = 0; + if (!tkvisiblerect(tk, &hitr) || !ptinrect(p, hitr)) + return nil; + snprint(buf, sizeof(buf), "to @%d", p.x); + tkentryselect(tk, buf, nil); + return nil; +} + +static char* +tkentryb1r(Tk *tk, char* arg, char **ret) +{ + USED(tk); + USED(arg); + USED(ret); + tkcancelrepeat(tk); + return nil; +} + +static void +blinkreset(Tk *tk) +{ + TkEntry *e = TKobj(TkEntry, tk); + if (!tkhaskeyfocus(tk) || tk->flag&Tkdisabled) + return; + e->flag |= Ecursoron; + tkblinkreset(tk); +} + +static void +showcaret(Tk *tk, int on) +{ + TkEntry *e = TKobj(TkEntry, tk); + + if (on) + e->flag |= Ecursoron; + else + e->flag &= ~Ecursoron; + tk->dirty = tkrect(tk, 0); +} + +char* +tkentryfocus(Tk *tk, char* arg, char **ret) +{ + int on = 0; + USED(ret); + + if (tk->flag&Tkdisabled) + return nil; + + if(strcmp(arg, " in") == 0) { + tkblink(tk, showcaret); + on = 1; + } + else + tkblink(nil, nil); + + showcaret(tk, on); + return nil; +} + +static +TkCmdtab tkentrycmd[] = +{ + "cget", tkentrycget, + "configure", tkentryconf, + "delete", tkentrydelete, + "get", tkentryget, + "icursor", tkentryicursor, + "index", tkentryindex, + "insert", tkentryinsert, + "selection", tkentryselect, + "xview", tkentryxview, + "tkEntryBS", tkentrybs, + "tkEntryBW", tkentrybw, + "tkEntryB1P", tkentryb1p, + "tkEntryB1M", tkentryb1m, + "tkEntryB1R", tkentryb1r, + "tkEntryB2P", tkentryb2p, + "tkEntryFocus", tkentryfocus, + "bbox", tkentrybboxcmd, + "see", tkentryseecmd, + nil +}; + +TkMethod entrymethod = { + "entry", + tkentrycmd, + tkfreeentry, + tkdrawentry, + tkentrygeom +}; |
