#include "lib9.h"#include "draw.h"#include "keyboard.h"#include "tk.h"#include "textw.h" /* * useful text widget info to be found at: * :/coordinate.systems - what coord. systems are in use * textu.c:/assumed.invariants - some invariants that must be preserved */ #define istring u.string#define iwin u.win#define imark u.mark#define iline u.line #define FLUSH() flushimage(tk->env->top->display, 1) #define O(t, e) ((long)(&((t*)0)->e)) /* Layout constants */enum { Textpadx = 2, Textpady = 0,}; typedef struct Interval { int lo; int hi;} Interval; typedef struct Mprint Mprint;struct Mprint{ char* buf; int ptr; int len;}; typedef struct TkDump TkDump;struct TkDump{ int sgml; int metrics;}; staticTkOption dumpopts[] ={ "sgml", OPTbool, O(TkDump, sgml), nil, "metrics", OPTbool, O(TkDump, metrics), nil, nil}; staticTkStab tkcompare[] ={ "<", TkLt, "<=", TkLte, "==", TkEq, ">=", TkGte, ">", TkGt, "!=", TkNeq, nil}; staticTkOption textopts[] ={ "wrap", OPTstab, O(TkText, opts[TkTwrap]), tkwrap, "spacing1", OPTnndist, O(TkText, opts[TkTspacing1]), (void *)O(Tk, env), "spacing2", OPTnndist, O(TkText, opts[TkTspacing2]), (void *)O(Tk, env), "spacing3", OPTnndist, O(TkText, opts[TkTspacing3]), (void *)O(Tk, env), "tabs", OPTtabs, O(TkText, tabs), (void *)O(Tk, env), "xscrollcommand", OPTtext, O(TkText, xscroll), nil, "yscrollcommand", OPTtext, O(TkText, yscroll), nil, "insertwidth", OPTnndist, O(TkText, inswidth), nil, "tagshare", OPTwinp, O(TkText, tagshare), nil, "propagate", OPTstab, O(TkText, propagate), tkbool, "selectborderwidth", OPTnndist, O(TkText, sborderwidth), nil, nil}; #define CNTL(c) ((c)&0x1f)#define DEL 0x7f static TkEbind tktbinds[] = { {TkButton1P, "%W tkTextButton1 %X %Y"}, {TkButton1P|TkMotion, "%W tkTextSelectTo %X %Y"}, {TkButton1P|TkDouble, "%W tkTextSelectTo %X %Y double"}, {TkButton1R, "%W tkTextButton1R"}, {TkButton2P, "%W scan mark %x %y"}, {TkButton2P|TkMotion, "%W scan dragto %x %y"}, {TkKey, "%W tkTextInsert {%A}"}, {TkKey|CNTL('a'), "%W tkTextSetCursor {insert linestart}"}, {TkKey|Home, "%W tkTextSetCursor {insert linestart}"}, {TkKey|CNTL('<'), "%W tkTextSetCursor {insert linestart}"}, {TkKey|CNTL('b'), "%W tkTextSetCursor insert-1c"}, {TkKey|Left, "%W tkTextSetCursor insert-1c"}, {TkKey|CNTL('d'), "%W tkTextDelIns"}, {TkKey|CNTL('e'), "%W tkTextSetCursor {insert lineend}"}, {TkKey|End, "%W tkTextSetCursor {insert lineend}"}, {TkKey|CNTL('>'), "%W tkTextSetCursor {insert lineend}"}, {TkKey|CNTL('f'), "%W tkTextSetCursor insert+1c"}, {TkKey|Right, "%W tkTextSetCursor insert+1c"}, {TkKey|CNTL('h'), "%W tkTextDelIns -c"}, {TkKey|DEL, "%W tkTextDelIns"}, {TkKey|CNTL('k'), "%W tkTextDelIns +l"}, {TkKey|CNTL('n'), "%W tkTextSetCursor {insert+1l}"}, {TkKey|Down, "%W tkTextSetCursor {insert+1l}"}, {TkKey|CNTL('o'), "%W tkTextInsert {\n}; %W mark set insert insert-1c"}, {TkKey|CNTL('p'), "%W tkTextSetCursor {insert-1l}"}, {TkKey|Up, "%W tkTextSetCursor {insert-1l}"}, {TkKey|CNTL('u'), "%W tkTextDelIns -l"}, {TkKey|CNTL('v'), "%W yview scroll 0.75 page"}, {TkKey|Pgdown, "%W yview scroll 0.75 page"}, {TkKey|CNTL('w'), "%W tkTextDelIns -w"}, {TkKey|Pgup, "%W yview scroll -0.75 page"}, {TkButton4P, "%W yview scroll -0.2 page"}, {TkButton5P, "%W yview scroll 0.2 page"}, {TkFocusout, "%W tkTextCursor delete"}, {TkKey|APP|'\t', ""}, {TkKey|BackTab, ""},}; static int tktclickmatch(TkText *, int, int, int, TkTindex *);static void tktdoubleclick(TkText *, TkTindex *, TkTindex *);static char* tktdrawline(Image*, Tk*, TkTline*, Point);static void tktextcursordraw(Tk *, int);static char* tktsetscroll(Tk*, int);static void tktsetclip(Tk *);static char* tktview(Tk*, char*, char**, int, int*, int, int);static Interval tkttranslate(Tk*, Interval, int);static void tktfixscroll(Tk*, Point);static void tktnotdrawn(Tk*, int, int, int);static void tktdrawbg(Tk*, int, int, int);static int tktwidbetween(Tk*, int, TkTindex*, TkTindex*);static int tktpostspace(Tk*, TkTline*);static int tktprespace(Tk*, TkTline*);static void tktsee(Tk*, TkTindex*, int);static Point tktrelpos(Tk*);static void autoselect(Tk*, void*, int);static void blinkreset(Tk*); /* debugging */extern int tktdbg;extern void tktprinttext(TkText*);extern void tktprintindex(TkTindex*);extern void tktprintitem(TkTitem*);extern void tktprintline(TkTline*);extern void tktcheck(TkText*, char*);extern int tktutfpos(char *, int); char*tktext(TkTop *t, char* arg, char **ret){ Tk *tk; char *e; TkEnv *ev; TkTline *l; TkTitem *it = nil; TkName *names = nil; TkTtaginfo *ti = nil; TkOptab tko[3]; TkTmarkinfo *mi = nil; TkText *tkt, *tktshare; tk = tknewobj(t, TKtext, sizeof(Tk)+sizeof(TkText)); if(tk == nil) return TkNomem; tkt = TKobj(TkText, tk); tk->relief = TKsunken; tk->borderwidth = 1; tk->ipad.x = Textpadx * 2; tk->ipad.y = Textpady * 2; tk->flag |= Tktakefocus; tkt->sborderwidth = 0; tkt->inswidth = 2; tkt->cur_flag = 0; /* text cursor doesn't show up initially */ tkt->opts[TkTwrap] = Tkwrapchar; tkt->opts[TkTrelief] = TKflat; tkt->opts[TkTjustify] = Tkleft; tkt->propagate = BoolX; tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkt; tko[1].optab = textopts; tko[2].ptr = nil; tk->req.width = tk->env->wzero*Textwidth; tk->req.height = tk->env->font->height*Textheight; names = nil; e = tkparse(t, arg, tko, &names); if(e != nil) goto err; tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); if(names == nil) { /* tkerr(t, arg); XXX */ e = TkBadwp; goto err; } if(tkt->tagshare != nil) { tkputenv(tk->env); tk->env = tkt->tagshare->env; tk->env->ref++; } if(tk->flag&Tkdisabled) tkt->inswidth = 0; if(tkt->tabs == nil) { tkt->tabs = malloc(sizeof(TkTtabstop)); if(tkt->tabs == nil) goto err; tkt->tabs->pos = 8*tk->env->wzero; tkt->tabs->justify = Tkleft; tkt->tabs->next = nil; } if(tkt->tagshare != nil) { tktshare = TKobj(TkText, tkt->tagshare); tkt->tags = tktshare->tags; tkt->nexttag = tktshare->nexttag; } else { /* Note: sel should have id == TkTselid == 0 */ e = tktaddtaginfo(tk, "sel", &ti); if(e != nil) goto err; tkputenv(ti->env); ti->env = tknewenv(t); if(ti->env == nil) goto err; ev = ti->env; ev->colors[TkCbackgnd] = tk->env->colors[TkCselectbgnd]; ev->colors[TkCbackgndlght] = tk->env->colors[TkCselectbgndlght]; ev->colors[TkCbackgnddark] = tk->env->colors[TkCselectbgnddark]; ev->colors[TkCforegnd] = tk->env->colors[TkCselectfgnd]; ev->set = (1<<TkCbackgnd)|(1<<TkCbackgndlght)| (1<<TkCbackgnddark)|(1<<TkCforegnd); ti->opts[TkTborderwidth] = tkt->sborderwidth; if(tkt->sborderwidth > 0) ti->opts[TkTrelief] = TKraised; } e = tktaddmarkinfo(tkt, "current", &mi); if(e != nil) goto err; e = tktaddmarkinfo(tkt, "insert", &mi); if(e != nil) goto err; tkt->start.flags = TkTfirst|TkTlast; tkt->end.flags = TkTlast; e = tktnewitem(TkTnewline, 0, &it); if(e != nil) goto err; e = tktnewline(TkTfirst|TkTlast, it, &tkt->start, &tkt->end, &l); if(e != nil) goto err; e = tktnewitem(TkTmark, 0, &it); if(e != nil) goto err; it->next = l->items; l->items = it; it->imark = mi; mi->cur = it; tkt->nlines = 1; tkt->scrolltop[Tkvertical] = -1; tkt->scrolltop[Tkhorizontal] = -1; tkt->scrollbot[Tkvertical] = -1; tkt->scrollbot[Tkhorizontal] = -1; if(tkt->tagshare != nil) tk->binds = tkt->tagshare->binds; else { e = tkbindings(t, tk, tktbinds, nelem(tktbinds)); if(e != nil) goto err; } if (tkt->propagate == BoolT) { if ((tk->flag & Tksetwidth) == 0) tk->req.width = tktmaxwid(tkt->start.next); if ((tk->flag & Tksetheight) == 0) tk->req.height = tkt->end.orig.y; } e = tkaddchild(t, tk, &names); tkfreename(names); if(e != nil) goto err; tk->name->link = nil; return tkvalue(ret, "%s", tk->name->name); err: /* XXX it's possible there's a memory leak here */ tkfreeobj(tk); return e;} /* * There are four coordinate systems of interest: * S - screen coordinate system (i.e. top left corner of * inferno screen is (0,0) in S space.) * I - image coordinate system (i.e. top left corner of * tkimageof(this widget) is (0,0) in I space.) * T - text coordinate system (i.e., top left of first line * is at (0,0) in T space.) * V - view coordinate system (i.e., top left of visible * portion of widget is at (0,0) in V space.) * * A point P in the four systems (Ps, Pi, Pt, Pv) satisfies: * Pt = Ps - deltast * Pv = Ps - deltasv * Pv = Pi - deltaiv * (where deltast is vector from S origin to T origin; * deltasv is vector from S origin to V origin; * deltaiv is vector from I origin to V origin) * * We keep deltatv, deltasv, and deltaiv in tkt. * Deltatv is updated by scrolling. * Deltasv is updated by geom changes: * tkposn(tk)+ipad/2 * Deltaiv is affected by geom changes and the call to the draw function: * tk->act+orig+ipad/2+(bw,bw) (orig is the parameter to tkdrawtext), * * We can derive * Ps = Pt + deltast * = Pt + deltasv - deltatv * * Pv = Pt - deltatv * * Here are various coordinates in the text widget according * to which coordinate system they use: * * S - Mouse coordinates (coming in to tktextevent); * the deltasv parameter to tkdrawtext; * coords in tkt->image, where drawing is done to * (to get same bit-alignment as screen, for fast transfer) * T - orig in TkTlines * V - %x,%y delivered via binds to TkText or its tags * Note deltasv changes underneath us, so is calculated on the fly * when it needs to be (in tktextevent). * */static voidtktsetdeltas(Tk *tk, Point orig){ TkText *tkt = TKobj(TkText, tk); tkt->deltaiv.x = orig.x + tk->act.x + tk->ipad.x/2 + tk->borderwidth; tkt->deltaiv.y = orig.y + tk->act.y + tk->ipad.y/2 + tk->borderwidth;} static Pointtktrelpos(Tk *sub){ Tk *tk; TkTindex ix; Rectangle r; Point ans; tk = sub->parent; if(tk == nil) return ZP; if(tktfindsubitem(sub, &ix)) { r = tktbbox(tk, &ix); ans.x = r.min.x; ans.y = r.min.y; return r.min; } return ZP;} static voidtktreplclipr(Image *dst, Rectangle r){ int locked; locked = lockdisplay(dst->display); replclipr(dst, 0, r); if(locked) unlockdisplay(dst->display);} char*tkdrawtext(Tk *tk, Point orig){ int vh; Image *dst; TkText *tkt; TkTline *l, *lend; Point p, deltait; Rectangle oclipr; int reldone = 1; char *e; tkt = TKobj(TkText, tk); dst = tkimageof(tk); if (dst == nil) return nil; tkt->image = dst; tktsetdeltas(tk, orig); tkt->tflag |= TkTdrawn|TkTdlocked; oclipr = dst->clipr; tktsetclip(tk); if(tk->flag&Tkrefresh) { reldone = 0; tktnotdrawn(tk, 0, tkt->end.orig.y, 1); } tk->flag &= ~Tkrefresh; deltait = subpt(tkt->deltaiv, tkt->deltatv); vh = tk->act.height - tk->ipad.y/2; lend = &tkt->end; for(l = tkt->start.next; l != lend; l = l->next) { if(l->orig.y+l->height < tkt->deltatv.y) continue; if(l->orig.y > tkt->deltatv.y + vh) break; if(!(l->flags&TkTdrawn)) { e = tktdrawline(dst, tk, l, deltait); if(e != nil) return e; } } tktreplclipr(dst, oclipr); if(!reldone) { p.x = orig.x + tk->act.x; p.y = orig.y + tk->act.y; tkdrawrelief(dst, tk, p, TkCbackgnd, tk->relief); } tkt->tflag &= ~TkTdlocked; return nil;} /* * Set the clipping rectangle of the destination image to the * intersection of the current clipping rectangle and the area inside * the text widget that needs to be redrawn. * The caller should save the old one and restore it later. */static voidtktsetclip(Tk *tk){ Rectangle r; Image *dst; TkText *tkt = TKobj(TkText, tk); dst = tkt->image; r.min = tkt->deltaiv; r.max.x = r.min.x + tk->act.width - tk->ipad.x / 2; r.max.y = r.min.y + tk->act.height - tk->ipad.y / 2; if(!rectclip(&r, dst->clipr)) r.max = r.min; tktreplclipr(dst, r);} static char*tktdrawline(Image *i, Tk *tk, TkTline *l, Point deltait){ Tk *sub; Font *f; Image *bg; Point p, q; Rectangle r; TkText *tkt; TkTitem *it, *z; int bevtop, bevbot; TkEnv *e, *et, *env; int *opts; int o, bd, ul, ov, h, w, la, lh, cursorx, join; char *err; env = mallocz(sizeof(TkEnv), 0); if(env == nil) return TkNomem; opts = mallocz(TkTnumopts*sizeof(int), 0);Lines 1–500 of 3701. Load more
#include "lib9.h" #include "draw.h" #include "keyboard.h" #include "tk.h" #include "textw.h" /* * useful text widget info to be found at: * :/coordinate.systems - what coord. systems are in use * textu.c:/assumed.invariants - some invariants that must be preserved */ #define istring u.string #define iwin u.win #define imark u.mark #define iline u.line #define FLUSH() flushimage(tk->env->top->display, 1) #define O(t, e) ((long)(&((t*)0)->e)) /* Layout constants */ enum { Textpadx = 2, Textpady = 0, }; typedef struct Interval { int lo; int hi; } Interval; typedef struct Mprint Mprint; struct Mprint { char* buf; int ptr; int len; }; typedef struct TkDump TkDump; struct TkDump { int sgml; int metrics; }; static TkOption dumpopts[] = { "sgml", OPTbool, O(TkDump, sgml), nil, "metrics", OPTbool, O(TkDump, metrics), nil, nil }; static TkStab tkcompare[] = { "<", TkLt, "<=", TkLte, "==", TkEq, ">=", TkGte, ">", TkGt, "!=", TkNeq, nil }; static TkOption textopts[] = { "wrap", OPTstab, O(TkText, opts[TkTwrap]), tkwrap, "spacing1", OPTnndist, O(TkText, opts[TkTspacing1]), (void *)O(Tk, env), "spacing2", OPTnndist, O(TkText, opts[TkTspacing2]), (void *)O(Tk, env), "spacing3", OPTnndist, O(TkText, opts[TkTspacing3]), (void *)O(Tk, env), "tabs", OPTtabs, O(TkText, tabs), (void *)O(Tk, env), "xscrollcommand", OPTtext, O(TkText, xscroll), nil, "yscrollcommand", OPTtext, O(TkText, yscroll), nil, "insertwidth", OPTnndist, O(TkText, inswidth), nil, "tagshare", OPTwinp, O(TkText, tagshare), nil, "propagate", OPTstab, O(TkText, propagate), tkbool, "selectborderwidth", OPTnndist, O(TkText, sborderwidth), nil, nil }; #define CNTL(c) ((c)&0x1f) #define DEL 0x7f static TkEbind tktbinds[] = { {TkButton1P, "%W tkTextButton1 %X %Y"}, {TkButton1P|TkMotion, "%W tkTextSelectTo %X %Y"}, {TkButton1P|TkDouble, "%W tkTextSelectTo %X %Y double"}, {TkButton1R, "%W tkTextButton1R"}, {TkButton2P, "%W scan mark %x %y"}, {TkButton2P|TkMotion, "%W scan dragto %x %y"}, {TkKey, "%W tkTextInsert {%A}"}, {TkKey|CNTL('a'), "%W tkTextSetCursor {insert linestart}"}, {TkKey|Home, "%W tkTextSetCursor {insert linestart}"}, {TkKey|CNTL('<'), "%W tkTextSetCursor {insert linestart}"}, {TkKey|CNTL('b'), "%W tkTextSetCursor insert-1c"}, {TkKey|Left, "%W tkTextSetCursor insert-1c"}, {TkKey|CNTL('d'), "%W tkTextDelIns"}, {TkKey|CNTL('e'), "%W tkTextSetCursor {insert lineend}"}, {TkKey|End, "%W tkTextSetCursor {insert lineend}"}, {TkKey|CNTL('>'), "%W tkTextSetCursor {insert lineend}"}, {TkKey|CNTL('f'), "%W tkTextSetCursor insert+1c"}, {TkKey|Right, "%W tkTextSetCursor insert+1c"}, {TkKey|CNTL('h'), "%W tkTextDelIns -c"}, {TkKey|DEL, "%W tkTextDelIns"}, {TkKey|CNTL('k'), "%W tkTextDelIns +l"}, {TkKey|CNTL('n'), "%W tkTextSetCursor {insert+1l}"}, {TkKey|Down, "%W tkTextSetCursor {insert+1l}"}, {TkKey|CNTL('o'), "%W tkTextInsert {\n}; %W mark set insert insert-1c"}, {TkKey|CNTL('p'), "%W tkTextSetCursor {insert-1l}"}, {TkKey|Up, "%W tkTextSetCursor {insert-1l}"}, {TkKey|CNTL('u'), "%W tkTextDelIns -l"}, {TkKey|CNTL('v'), "%W yview scroll 0.75 page"}, {TkKey|Pgdown, "%W yview scroll 0.75 page"}, {TkKey|CNTL('w'), "%W tkTextDelIns -w"}, {TkKey|Pgup, "%W yview scroll -0.75 page"}, {TkButton4P, "%W yview scroll -0.2 page"}, {TkButton5P, "%W yview scroll 0.2 page"}, {TkFocusout, "%W tkTextCursor delete"}, {TkKey|APP|'\t', ""}, {TkKey|BackTab, ""}, }; static int tktclickmatch(TkText *, int, int, int, TkTindex *); static void tktdoubleclick(TkText *, TkTindex *, TkTindex *); static char* tktdrawline(Image*, Tk*, TkTline*, Point); static void tktextcursordraw(Tk *, int); static char* tktsetscroll(Tk*, int); static void tktsetclip(Tk *); static char* tktview(Tk*, char*, char**, int, int*, int, int); static Interval tkttranslate(Tk*, Interval, int); static void tktfixscroll(Tk*, Point); static void tktnotdrawn(Tk*, int, int, int); static void tktdrawbg(Tk*, int, int, int); static int tktwidbetween(Tk*, int, TkTindex*, TkTindex*); static int tktpostspace(Tk*, TkTline*); static int tktprespace(Tk*, TkTline*); static void tktsee(Tk*, TkTindex*, int); static Point tktrelpos(Tk*); static void autoselect(Tk*, void*, int); static void blinkreset(Tk*); /* debugging */ extern int tktdbg; extern void tktprinttext(TkText*); extern void tktprintindex(TkTindex*); extern void tktprintitem(TkTitem*); extern void tktprintline(TkTline*); extern void tktcheck(TkText*, char*); extern int tktutfpos(char *, int); char* tktext(TkTop *t, char* arg, char **ret) { Tk *tk; char *e; TkEnv *ev; TkTline *l; TkTitem *it = nil; TkName *names = nil; TkTtaginfo *ti = nil; TkOptab tko[3]; TkTmarkinfo *mi = nil; TkText *tkt, *tktshare; tk = tknewobj(t, TKtext, sizeof(Tk)+sizeof(TkText)); if(tk == nil) return TkNomem; tkt = TKobj(TkText, tk); tk->relief = TKsunken; tk->borderwidth = 1; tk->ipad.x = Textpadx * 2; tk->ipad.y = Textpady * 2; tk->flag |= Tktakefocus; tkt->sborderwidth = 0; tkt->inswidth = 2; tkt->cur_flag = 0; /* text cursor doesn't show up initially */ tkt->opts[TkTwrap] = Tkwrapchar; tkt->opts[TkTrelief] = TKflat; tkt->opts[TkTjustify] = Tkleft; tkt->propagate = BoolX; tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkt; tko[1].optab = textopts; tko[2].ptr = nil; tk->req.width = tk->env->wzero*Textwidth; tk->req.height = tk->env->font->height*Textheight; names = nil; e = tkparse(t, arg, tko, &names); if(e != nil) goto err; tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); if(names == nil) { /* tkerr(t, arg); XXX */ e = TkBadwp; goto err; } if(tkt->tagshare != nil) { tkputenv(tk->env); tk->env = tkt->tagshare->env; tk->env->ref++; } if(tk->flag&Tkdisabled) tkt->inswidth = 0; if(tkt->tabs == nil) { tkt->tabs = malloc(sizeof(TkTtabstop)); if(tkt->tabs == nil) goto err; tkt->tabs->pos = 8*tk->env->wzero; tkt->tabs->justify = Tkleft; tkt->tabs->next = nil; } if(tkt->tagshare != nil) { tktshare = TKobj(TkText, tkt->tagshare); tkt->tags = tktshare->tags; tkt->nexttag = tktshare->nexttag; } else { /* Note: sel should have id == TkTselid == 0 */ e = tktaddtaginfo(tk, "sel", &ti); if(e != nil) goto err; tkputenv(ti->env); ti->env = tknewenv(t); if(ti->env == nil) goto err; ev = ti->env; ev->colors[TkCbackgnd] = tk->env->colors[TkCselectbgnd]; ev->colors[TkCbackgndlght] = tk->env->colors[TkCselectbgndlght]; ev->colors[TkCbackgnddark] = tk->env->colors[TkCselectbgnddark]; ev->colors[TkCforegnd] = tk->env->colors[TkCselectfgnd]; ev->set = (1<<TkCbackgnd)|(1<<TkCbackgndlght)| (1<<TkCbackgnddark)|(1<<TkCforegnd); ti->opts[TkTborderwidth] = tkt->sborderwidth; if(tkt->sborderwidth > 0) ti->opts[TkTrelief] = TKraised; } e = tktaddmarkinfo(tkt, "current", &mi); if(e != nil) goto err; e = tktaddmarkinfo(tkt, "insert", &mi); if(e != nil) goto err; tkt->start.flags = TkTfirst|TkTlast; tkt->end.flags = TkTlast; e = tktnewitem(TkTnewline, 0, &it); if(e != nil) goto err; e = tktnewline(TkTfirst|TkTlast, it, &tkt->start, &tkt->end, &l); if(e != nil) goto err; e = tktnewitem(TkTmark, 0, &it); if(e != nil) goto err; it->next = l->items; l->items = it; it->imark = mi; mi->cur = it; tkt->nlines = 1; tkt->scrolltop[Tkvertical] = -1; tkt->scrolltop[Tkhorizontal] = -1; tkt->scrollbot[Tkvertical] = -1; tkt->scrollbot[Tkhorizontal] = -1; if(tkt->tagshare != nil) tk->binds = tkt->tagshare->binds; else { e = tkbindings(t, tk, tktbinds, nelem(tktbinds)); if(e != nil) goto err; } if (tkt->propagate == BoolT) { if ((tk->flag & Tksetwidth) == 0) tk->req.width = tktmaxwid(tkt->start.next); if ((tk->flag & Tksetheight) == 0) tk->req.height = tkt->end.orig.y; } e = tkaddchild(t, tk, &names); tkfreename(names); if(e != nil) goto err; tk->name->link = nil; return tkvalue(ret, "%s", tk->name->name); err: /* XXX it's possible there's a memory leak here */ tkfreeobj(tk); return e; } /* * There are four coordinate systems of interest: * S - screen coordinate system (i.e. top left corner of * inferno screen is (0,0) in S space.) * I - image coordinate system (i.e. top left corner of * tkimageof(this widget) is (0,0) in I space.) * T - text coordinate system (i.e., top left of first line * is at (0,0) in T space.) * V - view coordinate system (i.e., top left of visible * portion of widget is at (0,0) in V space.) * * A point P in the four systems (Ps, Pi, Pt, Pv) satisfies: * Pt = Ps - deltast * Pv = Ps - deltasv * Pv = Pi - deltaiv * (where deltast is vector from S origin to T origin; * deltasv is vector from S origin to V origin; * deltaiv is vector from I origin to V origin) * * We keep deltatv, deltasv, and deltaiv in tkt. * Deltatv is updated by scrolling. * Deltasv is updated by geom changes: * tkposn(tk)+ipad/2 * Deltaiv is affected by geom changes and the call to the draw function: * tk->act+orig+ipad/2+(bw,bw) (orig is the parameter to tkdrawtext), * * We can derive * Ps = Pt + deltast * = Pt + deltasv - deltatv * * Pv = Pt - deltatv * * Here are various coordinates in the text widget according * to which coordinate system they use: * * S - Mouse coordinates (coming in to tktextevent); * the deltasv parameter to tkdrawtext; * coords in tkt->image, where drawing is done to * (to get same bit-alignment as screen, for fast transfer) * T - orig in TkTlines * V - %x,%y delivered via binds to TkText or its tags * Note deltasv changes underneath us, so is calculated on the fly * when it needs to be (in tktextevent). * */ static void tktsetdeltas(Tk *tk, Point orig) { TkText *tkt = TKobj(TkText, tk); tkt->deltaiv.x = orig.x + tk->act.x + tk->ipad.x/2 + tk->borderwidth; tkt->deltaiv.y = orig.y + tk->act.y + tk->ipad.y/2 + tk->borderwidth; } static Point tktrelpos(Tk *sub) { Tk *tk; TkTindex ix; Rectangle r; Point ans; tk = sub->parent; if(tk == nil) return ZP; if(tktfindsubitem(sub, &ix)) { r = tktbbox(tk, &ix); ans.x = r.min.x; ans.y = r.min.y; return r.min; } return ZP; } static void tktreplclipr(Image *dst, Rectangle r) { int locked; locked = lockdisplay(dst->display); replclipr(dst, 0, r); if(locked) unlockdisplay(dst->display); } char* tkdrawtext(Tk *tk, Point orig) { int vh; Image *dst; TkText *tkt; TkTline *l, *lend; Point p, deltait; Rectangle oclipr; int reldone = 1; char *e; tkt = TKobj(TkText, tk); dst = tkimageof(tk); if (dst == nil) return nil; tkt->image = dst; tktsetdeltas(tk, orig); tkt->tflag |= TkTdrawn|TkTdlocked; oclipr = dst->clipr; tktsetclip(tk); if(tk->flag&Tkrefresh) { reldone = 0; tktnotdrawn(tk, 0, tkt->end.orig.y, 1); } tk->flag &= ~Tkrefresh; deltait = subpt(tkt->deltaiv, tkt->deltatv); vh = tk->act.height - tk->ipad.y/2; lend = &tkt->end; for(l = tkt->start.next; l != lend; l = l->next) { if(l->orig.y+l->height < tkt->deltatv.y) continue; if(l->orig.y > tkt->deltatv.y + vh) break; if(!(l->flags&TkTdrawn)) { e = tktdrawline(dst, tk, l, deltait); if(e != nil) return e; } } tktreplclipr(dst, oclipr); if(!reldone) { p.x = orig.x + tk->act.x; p.y = orig.y + tk->act.y; tkdrawrelief(dst, tk, p, TkCbackgnd, tk->relief); } tkt->tflag &= ~TkTdlocked; return nil; } /* * Set the clipping rectangle of the destination image to the * intersection of the current clipping rectangle and the area inside * the text widget that needs to be redrawn. * The caller should save the old one and restore it later. */ static void tktsetclip(Tk *tk) { Rectangle r; Image *dst; TkText *tkt = TKobj(TkText, tk); dst = tkt->image; r.min = tkt->deltaiv; r.max.x = r.min.x + tk->act.width - tk->ipad.x / 2; r.max.y = r.min.y + tk->act.height - tk->ipad.y / 2; if(!rectclip(&r, dst->clipr)) r.max = r.min; tktreplclipr(dst, r); } static char* tktdrawline(Image *i, Tk *tk, TkTline *l, Point deltait) { Tk *sub; Font *f; Image *bg; Point p, q; Rectangle r; TkText *tkt; TkTitem *it, *z; int bevtop, bevbot; TkEnv *e, *et, *env; int *opts; int o, bd, ul, ov, h, w, la, lh, cursorx, join; char *err; env = mallocz(sizeof(TkEnv), 0); if(env == nil) return TkNomem; opts = mallocz(TkTnumopts*sizeof(int), 0);