#include "lib9.h"#include "draw.h"#include "tk.h"#include "textw.h" #define istring u.string#define iwin u.win#define imark u.mark#define iline u.line /* debugging */int tktdbg;extern void tktprinttext(TkText*);extern void tktprintindex(TkTindex*);extern void tktprintitem(TkTitem*);extern void tktprintline(TkTline*);extern void tktcheck(TkText*, char*);int tktutfpos(char*, int); char*tktnewitem(int kind, int tagextra,TkTitem **ret){ int n; TkTitem *i; n = sizeof(TkTitem) + tagextra * sizeof(ulong); i = malloc(n); if(i == nil) return TkNomem; memset(i, 0, n); i->kind = kind; i->tagextra = tagextra; *ret = i; return nil;} char*tktnewline(int flags, TkTitem *items, TkTline *prev, TkTline *next, TkTline **ret){ TkTline *l; TkTitem *i; l = malloc(sizeof(TkTline)); if(l == nil) return TkNomem; memset(l, 0, sizeof(TkTline)); l->flags = flags; l->items = items; l->prev = prev; l->next = next; next->prev = l; prev->next = l; for(i = items; i->next != nil;) i = i->next; if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) print("text:tktnewline botch\n"); i->iline = l; *ret = l; return nil;} /* * free items; freewins is 0 when the subwindows will be * freed anyway as the main text widget is being destroyed. */voidtktfreeitems(TkText *tkt, TkTitem *i, int freewins){ TkTitem *n; Tk *tk; while(i != nil) { n = i->next; if(tkt->mouse == i) tkt->mouse = nil; switch(i->kind) { case TkTascii: case TkTrune: if(i->istring != nil) free(i->istring); break; case TkTwin: if (i->iwin != nil) { tk = i->iwin->sub; if (tk != nil) { tk->geom = nil; tk->destroyed = nil; if (i->iwin->owned && freewins) { if (tk->name != nil) tkdestroy(tk->env->top, tk->name->name, nil); } else { tk->parent = nil; tk->geom = nil; tk->destroyed = nil; } } if(i->iwin->create != nil) free(i->iwin->create); free(i->iwin); } break; case TkTmark: break; } free(i); i = n; }} voidtktfreelines(TkText *tkt, TkTline *l, int freewins){ TkTline *n; while(l != nil) { n = l->next; tktfreeitems(tkt, l->items, freewins); free(l); l = n; }} voidtktfreetabs(TkTtabstop *t){ TkTtabstop *n; while(t != nil) { n = t->next; free(t); t = n; }} voidtkfreetext(Tk *tk){ TkText *tkt = TKobj(TkText, tk); if(tkt->start.next != nil && tkt->start.next != &(tkt->end)) { tkt->end.prev->next = nil; tktfreelines(tkt, tkt->start.next, 0); } tktfreeitems(tkt, tkt->start.items, 0); tktfreeitems(tkt, tkt->end.items, 0); tktfreetabs(tkt->tabs); if(tkt->tagshare == nil) tktfreetags(tkt->tags); else tk->binds = nil; tktfreemarks(tkt->marks); if(tkt->xscroll != nil) free(tkt->xscroll); if(tkt->yscroll != nil) free(tkt->yscroll); /* don't free image because it belongs to window */} /* * Remove the item at ix, joining previous and next items. * If item is at end of line, remove next line and join * its items to this one (except at end). * On return, ix is adjusted to point to the next item. */voidtktremitem(TkText *tkt, TkTindex *ix){ TkTline *l, *lnext; TkTindex prev, nx; TkTitem *i, *ilast; l = ix->line; i = ix->item; if(i->next == nil) { if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) { print("tktremitem: botch 1\n"); return; } lnext = l->next; if(lnext == &tkt->end) /* not supposed to remove final newline */ return; if(i->kind == TkTnewline) tkt->nlines--; ilast = tktlastitem(lnext->items); ilast->iline = l; i->next = lnext->items; l->flags = (l->flags & ~TkTlast) | (lnext->flags & TkTlast); l->next = lnext->next; lnext->next->prev = l; free(lnext); } if(l->items == i) l->items = i->next; else { prev = *ix; if(!tktadjustind(tkt, TkTbyitemback, &prev) && tktdbg) { print("tktremitem: botch 2\n"); return; } prev.item->next = i->next; } ix->item = i->next; ix->pos = 0; i->next = nil; nx = *ix; tktadjustind(tkt, TkTbycharstart, &nx); /* check against cached items */ if(tkt->selfirst == i) tkt->selfirst = nx.item; if(tkt->sellast == i) tkt->sellast = nx.item; if(tkt->selfirst == tkt->sellast) { tkt->selfirst = nil; tkt->sellast = nil; } tktfreeitems(tkt, i, 1);} inttktdispwidth(Tk *tk, TkTtabstop *tb, TkTitem *i, Font *f, int x, int pos, int nchars){ int w, del, locked; TkTtabstop *tbprev; Display *d; TkText *tkt; TkEnv env; tkt = TKobj(TkText, tk); d = tk->env->top->display; if (tb == nil) tb = tkt->tabs; switch(i->kind) { case TkTrune: pos = tktutfpos(i->istring, pos); /* FALLTHRU */ case TkTascii: if(f == nil) { if(!tktanytags(i)) f = tk->env->font; else { tkttagopts(tk, i, nil, &env, nil, 1); f = env.font; } } locked = 0; if(!(tkt->tflag&TkTdlocked)) locked = lockdisplay(d); if(nchars >= 0) w = stringnwidth(f, i->istring+pos, nchars); else w = stringwidth(f, i->istring+pos); if(locked) unlockdisplay(d); break; case TkTtab: if(tb == nil) w = 0; else { tbprev = nil; while(tb->pos <= x && tb->next != nil) { tbprev = tb; tb = tb->next; } w = tb->pos - x; if(w <= 0) { del = tb->pos; if(tbprev != nil) del -= tbprev->pos; while(w <= 0) w += del; } /* todo: other kinds of justification */ } break; case TkTwin: if(i->iwin->sub == 0) w = 0; else w = i->iwin->sub->act.width + 2*i->iwin->padx + 2*i->iwin->sub->borderwidth; break; default: w = 0; } return w;} inttktindrune(TkTindex *ix){ int ans; Rune r; switch(ix->item->kind) { case TkTascii: ans = ix->item->istring[ix->pos]; break; case TkTrune: chartorune(&r, ix->item->istring + tktutfpos(ix->item->istring, ix->pos)); ans = r; break; case TkTtab: ans = '\t'; break; case TkTnewline: ans = '\n'; break; default: /* only care that it isn't a word char */ ans = 0x80; } return ans;} TkTitem*tktlastitem(TkTitem *i){ while(i->next != nil) i = i->next; if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) print("text:tktlastitem botch\n"); return i;} TkTline*tktitemline(TkTitem *i){ i = tktlastitem(i); return i->iline;} inttktlinenum(TkText *tkt, TkTindex *p){ int n; TkTline *l; if(p->line->orig.y <= tkt->end.orig.y / 2) { /* line seems closer to beginning */ n = 1; for(l = tkt->start.next; l != p->line; l = l->next) { if(tktdbg && l->next == nil) { print("text: tktlinenum botch\n"); break; } if(l->flags & TkTlast) n++; } } else { n = tkt->nlines; for(l = tkt->end.prev; l != p->line; l = l->prev) { if(tktdbg && l->prev == nil) { print("text: tktlinenum botch\n"); break; } if(l->flags & TkTfirst) n--; } } return n;} inttktlinepos(TkText *tkt, TkTindex *p){ int n; TkTindex ix; TkTitem *i; n = 0; ix = *p; i = ix.item; tktadjustind(tkt, TkTbylinestart, &ix); while(ix.item != i) { if(tktdbg && ix.item->next == nil && (ix.line->flags&TkTlast)) { print("text: tktlinepos botch\n"); break; } n += tktposcount(ix.item); if(!tktadjustind(tkt, TkTbyitem, &ix)) { if(tktdbg) print("tktlinepos botch\n"); break; } } return (n+p->pos);} inttktposcount(TkTitem *i){ int n; if(i->kind == TkTascii) n = strlen(i->istring); else if(i->kind == TkTrune) n = utflen(i->istring); else if(i->kind == TkTmark || i->kind == TkTcontline) n = 0; else n = 1; return n;} /* * Insert item i before position ins. * If i is a newline or a contline, make a new line to contain the items up to * and including the new newline, and make the original line * contain the items from ins on. * Adjust ins so that it points just after inserted item. */char*tktiteminsert(TkText *tkt, TkTindex *ins, TkTitem *i){ int hasprev, flags; char *e; TkTindex prev; TkTline *l; TkTitem *items; prev = *ins; hasprev = tktadjustind(tkt, TkTbyitemback, &prev); if(i->kind == TkTnewline || i->kind == TkTcontline) { i->next = nil; if(hasprev && prev.line == ins->line) { items = ins->line->items; prev.item->next = i; } else items = i; flags = ins->line->flags&TkTfirst; if(i->kind == TkTnewline) flags |= TkTlast; e = tktnewline(flags, items, ins->line->prev, ins->line, &l); if(e != nil) { if(hasprev && prev.line == ins->line) prev.item->next = ins->item; return e; } if(i->kind == TkTnewline) ins->line->flags |= TkTfirst; if(i->kind == TkTcontline) ins->line->flags &= ~TkTfirst; ins->line->items = ins->item; ins->pos = 0; } else { if(hasprev && prev.line == ins->line) prev.item->next = i; else ins->line->items = i; i->next = ins->item; } return nil;} /* * If index p doesn't point at the beginning of an item, * split the item at p. Adjust p to point to the beginning of * the item after the split (same character it used to point at). * If there is a split, the old item gets the characters before * the split, and a new item gets the characters after it. */char*tktsplititem(TkTindex *p){ int l1, l2; char *s1, *s2, *e; TkTitem *i, *i2; i = p->item; if(p->pos != 0) { /* * Must be TkTascii or TkTrune * * Make new item i2, to be inserted after i, * with portion of string from p->pos on */ if (i->kind == TkTascii) l1 = p->pos; elseLines 1–500 of 1077. Load more
#include "lib9.h" #include "draw.h" #include "tk.h" #include "textw.h" #define istring u.string #define iwin u.win #define imark u.mark #define iline u.line /* debugging */ int tktdbg; extern void tktprinttext(TkText*); extern void tktprintindex(TkTindex*); extern void tktprintitem(TkTitem*); extern void tktprintline(TkTline*); extern void tktcheck(TkText*, char*); int tktutfpos(char*, int); char* tktnewitem(int kind, int tagextra,TkTitem **ret) { int n; TkTitem *i; n = sizeof(TkTitem) + tagextra * sizeof(ulong); i = malloc(n); if(i == nil) return TkNomem; memset(i, 0, n); i->kind = kind; i->tagextra = tagextra; *ret = i; return nil; } char* tktnewline(int flags, TkTitem *items, TkTline *prev, TkTline *next, TkTline **ret) { TkTline *l; TkTitem *i; l = malloc(sizeof(TkTline)); if(l == nil) return TkNomem; memset(l, 0, sizeof(TkTline)); l->flags = flags; l->items = items; l->prev = prev; l->next = next; next->prev = l; prev->next = l; for(i = items; i->next != nil;) i = i->next; if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) print("text:tktnewline botch\n"); i->iline = l; *ret = l; return nil; } /* * free items; freewins is 0 when the subwindows will be * freed anyway as the main text widget is being destroyed. */ void tktfreeitems(TkText *tkt, TkTitem *i, int freewins) { TkTitem *n; Tk *tk; while(i != nil) { n = i->next; if(tkt->mouse == i) tkt->mouse = nil; switch(i->kind) { case TkTascii: case TkTrune: if(i->istring != nil) free(i->istring); break; case TkTwin: if (i->iwin != nil) { tk = i->iwin->sub; if (tk != nil) { tk->geom = nil; tk->destroyed = nil; if (i->iwin->owned && freewins) { if (tk->name != nil) tkdestroy(tk->env->top, tk->name->name, nil); } else { tk->parent = nil; tk->geom = nil; tk->destroyed = nil; } } if(i->iwin->create != nil) free(i->iwin->create); free(i->iwin); } break; case TkTmark: break; } free(i); i = n; } } void tktfreelines(TkText *tkt, TkTline *l, int freewins) { TkTline *n; while(l != nil) { n = l->next; tktfreeitems(tkt, l->items, freewins); free(l); l = n; } } void tktfreetabs(TkTtabstop *t) { TkTtabstop *n; while(t != nil) { n = t->next; free(t); t = n; } } void tkfreetext(Tk *tk) { TkText *tkt = TKobj(TkText, tk); if(tkt->start.next != nil && tkt->start.next != &(tkt->end)) { tkt->end.prev->next = nil; tktfreelines(tkt, tkt->start.next, 0); } tktfreeitems(tkt, tkt->start.items, 0); tktfreeitems(tkt, tkt->end.items, 0); tktfreetabs(tkt->tabs); if(tkt->tagshare == nil) tktfreetags(tkt->tags); else tk->binds = nil; tktfreemarks(tkt->marks); if(tkt->xscroll != nil) free(tkt->xscroll); if(tkt->yscroll != nil) free(tkt->yscroll); /* don't free image because it belongs to window */ } /* * Remove the item at ix, joining previous and next items. * If item is at end of line, remove next line and join * its items to this one (except at end). * On return, ix is adjusted to point to the next item. */ void tktremitem(TkText *tkt, TkTindex *ix) { TkTline *l, *lnext; TkTindex prev, nx; TkTitem *i, *ilast; l = ix->line; i = ix->item; if(i->next == nil) { if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) { print("tktremitem: botch 1\n"); return; } lnext = l->next; if(lnext == &tkt->end) /* not supposed to remove final newline */ return; if(i->kind == TkTnewline) tkt->nlines--; ilast = tktlastitem(lnext->items); ilast->iline = l; i->next = lnext->items; l->flags = (l->flags & ~TkTlast) | (lnext->flags & TkTlast); l->next = lnext->next; lnext->next->prev = l; free(lnext); } if(l->items == i) l->items = i->next; else { prev = *ix; if(!tktadjustind(tkt, TkTbyitemback, &prev) && tktdbg) { print("tktremitem: botch 2\n"); return; } prev.item->next = i->next; } ix->item = i->next; ix->pos = 0; i->next = nil; nx = *ix; tktadjustind(tkt, TkTbycharstart, &nx); /* check against cached items */ if(tkt->selfirst == i) tkt->selfirst = nx.item; if(tkt->sellast == i) tkt->sellast = nx.item; if(tkt->selfirst == tkt->sellast) { tkt->selfirst = nil; tkt->sellast = nil; } tktfreeitems(tkt, i, 1); } int tktdispwidth(Tk *tk, TkTtabstop *tb, TkTitem *i, Font *f, int x, int pos, int nchars) { int w, del, locked; TkTtabstop *tbprev; Display *d; TkText *tkt; TkEnv env; tkt = TKobj(TkText, tk); d = tk->env->top->display; if (tb == nil) tb = tkt->tabs; switch(i->kind) { case TkTrune: pos = tktutfpos(i->istring, pos); /* FALLTHRU */ case TkTascii: if(f == nil) { if(!tktanytags(i)) f = tk->env->font; else { tkttagopts(tk, i, nil, &env, nil, 1); f = env.font; } } locked = 0; if(!(tkt->tflag&TkTdlocked)) locked = lockdisplay(d); if(nchars >= 0) w = stringnwidth(f, i->istring+pos, nchars); else w = stringwidth(f, i->istring+pos); if(locked) unlockdisplay(d); break; case TkTtab: if(tb == nil) w = 0; else { tbprev = nil; while(tb->pos <= x && tb->next != nil) { tbprev = tb; tb = tb->next; } w = tb->pos - x; if(w <= 0) { del = tb->pos; if(tbprev != nil) del -= tbprev->pos; while(w <= 0) w += del; } /* todo: other kinds of justification */ } break; case TkTwin: if(i->iwin->sub == 0) w = 0; else w = i->iwin->sub->act.width + 2*i->iwin->padx + 2*i->iwin->sub->borderwidth; break; default: w = 0; } return w; } int tktindrune(TkTindex *ix) { int ans; Rune r; switch(ix->item->kind) { case TkTascii: ans = ix->item->istring[ix->pos]; break; case TkTrune: chartorune(&r, ix->item->istring + tktutfpos(ix->item->istring, ix->pos)); ans = r; break; case TkTtab: ans = '\t'; break; case TkTnewline: ans = '\n'; break; default: /* only care that it isn't a word char */ ans = 0x80; } return ans; } TkTitem* tktlastitem(TkTitem *i) { while(i->next != nil) i = i->next; if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) print("text:tktlastitem botch\n"); return i; } TkTline* tktitemline(TkTitem *i) { i = tktlastitem(i); return i->iline; } int tktlinenum(TkText *tkt, TkTindex *p) { int n; TkTline *l; if(p->line->orig.y <= tkt->end.orig.y / 2) { /* line seems closer to beginning */ n = 1; for(l = tkt->start.next; l != p->line; l = l->next) { if(tktdbg && l->next == nil) { print("text: tktlinenum botch\n"); break; } if(l->flags & TkTlast) n++; } } else { n = tkt->nlines; for(l = tkt->end.prev; l != p->line; l = l->prev) { if(tktdbg && l->prev == nil) { print("text: tktlinenum botch\n"); break; } if(l->flags & TkTfirst) n--; } } return n; } int tktlinepos(TkText *tkt, TkTindex *p) { int n; TkTindex ix; TkTitem *i; n = 0; ix = *p; i = ix.item; tktadjustind(tkt, TkTbylinestart, &ix); while(ix.item != i) { if(tktdbg && ix.item->next == nil && (ix.line->flags&TkTlast)) { print("text: tktlinepos botch\n"); break; } n += tktposcount(ix.item); if(!tktadjustind(tkt, TkTbyitem, &ix)) { if(tktdbg) print("tktlinepos botch\n"); break; } } return (n+p->pos); } int tktposcount(TkTitem *i) { int n; if(i->kind == TkTascii) n = strlen(i->istring); else if(i->kind == TkTrune) n = utflen(i->istring); else if(i->kind == TkTmark || i->kind == TkTcontline) n = 0; else n = 1; return n; } /* * Insert item i before position ins. * If i is a newline or a contline, make a new line to contain the items up to * and including the new newline, and make the original line * contain the items from ins on. * Adjust ins so that it points just after inserted item. */ char* tktiteminsert(TkText *tkt, TkTindex *ins, TkTitem *i) { int hasprev, flags; char *e; TkTindex prev; TkTline *l; TkTitem *items; prev = *ins; hasprev = tktadjustind(tkt, TkTbyitemback, &prev); if(i->kind == TkTnewline || i->kind == TkTcontline) { i->next = nil; if(hasprev && prev.line == ins->line) { items = ins->line->items; prev.item->next = i; } else items = i; flags = ins->line->flags&TkTfirst; if(i->kind == TkTnewline) flags |= TkTlast; e = tktnewline(flags, items, ins->line->prev, ins->line, &l); if(e != nil) { if(hasprev && prev.line == ins->line) prev.item->next = ins->item; return e; } if(i->kind == TkTnewline) ins->line->flags |= TkTfirst; if(i->kind == TkTcontline) ins->line->flags &= ~TkTfirst; ins->line->items = ins->item; ins->pos = 0; } else { if(hasprev && prev.line == ins->line) prev.item->next = i; else ins->line->items = i; i->next = ins->item; } return nil; } /* * If index p doesn't point at the beginning of an item, * split the item at p. Adjust p to point to the beginning of * the item after the split (same character it used to point at). * If there is a split, the old item gets the characters before * the split, and a new item gets the characters after it. */ char* tktsplititem(TkTindex *p) { int l1, l2; char *s1, *s2, *e; TkTitem *i, *i2; i = p->item; if(p->pos != 0) { /* * Must be TkTascii or TkTrune * * Make new item i2, to be inserted after i, * with portion of string from p->pos on */ if (i->kind == TkTascii) l1 = p->pos; else