#include #include #include #include #include #include #include #include #include #include <9p.h> #include #include "gui.h" int framedebug; static void framedump(Panel* p) { if (framedebug) print("froff %d chr %d ln %d max %d, llf %d full %d\n", p->froff, p->f.nchars, p->f.nlines, p->f.maxlines, p->f.lastlinefull, p->f.lastlinefull); } /* * The routines below update both the Tblock and the Frame, * they do nothing else. No events, no resizes, no flushimages. */ void filltext(Panel* p) { Tblock* b; int n; int pos; Rune nl[1]; Rectangle wr; Point pt; nl[0] = L'\n'; if (!p->loaded || p->f.lastlinefull) return; b = blockseek(p->blks, &n, p->froff + p->f.nchars); for(; b && !p->f.lastlinefull; b = b->next){ pos = p->f.nchars; if (b->nr){ fdprint("fill at %d+%d with ", p->froff, b->nr); fdprint("%d runes: [%.*S]\n", b->nr, (b->nr < 100 ? b->nr : 100), b->r + n); frinsert(&p->f, b->r+n, b->r+b->nr, pos); } n = 0; } /* Libframe may not clear the right of the last line. BUG? * we force that by inserting a fake \n */ if (!p->f.lastlinefull){ frinsert(&p->f, nl, nl+1, p->f.nchars); frdelete(&p->f, p->f.nchars-1, p->f.nchars); } /* Clear what's left unused */ pt = p->rect.min; pt.y += p->f.nlines * p->font->height; wr = Rpt(pt, p->rect.max); draw(screen, wr, cols[BACK], nil, ZP); } static int canonpos(Panel* p, int pos) { if (pos < 0) return 0; else if (pos > p->f.nchars) return p->f.nchars; else return pos; } void hidesel(Panel* p) { Frame* f; if (!p->loaded) return; f = &p->f; if (f->p0 != f->p1 || (p->flags&Pedit)) frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0); } void drawsel(Panel* p) { int ss, se; Frame* f; if (!p->loaded) return; f = &p->f; ss = canonpos(p, p->ss - p->froff); se = canonpos(p, p->se - p->froff); if(ss == f->p0 && se == f->p1) // it's already there. return; if (f->p0 != f->p1 || (p->flags&Pedit)) frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0); f->p0 = ss; f->p1 = se; if (f->p0 != f->p1 || (p->flags&Pedit)) frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1); } /* Returns true if we want a resize */ int settextsize(Panel* p) { int oncols, onrows; int res; Point omin; int n; res = p->maxsz.y = 0; if (p->flags&Pline){ omin = p->minsz; packblock(p->blks); p->minsz.y = fontheight(p->font); if (!(p->flags&Pedit) && p->blks->nr > 0){ n = runestringnwidth(p->font, p->blks->r, p->blks->nr); p->minsz.x = n; } res = !eqpt(omin, p->minsz); } else { p->minsz = Pt(p->font->height*2,p->font->height*2); if (p->nlines == 0) p->maxsz.y = 0; else { n = p->nlines + 1; if (n < 3) n = 3; p->maxsz.y = p->font->height * n; } } oncols = p->ncols; onrows = p->nrows; p->ncols = gettextwid(p); p->nrows = gettextht(p); fdprint("setframesizes: %s: sz %P min %P max [0 %d] txt %dx%d wx %d wy %d\n", p->name, p->size, p->minsz, p->maxsz.y, p->ncols, p->nrows, p->wants.x, p->wants.y); if (!res) res = oncols != p->ncols || onrows != p->nrows; return res; } static int posselcmp(Panel* p, int pos) { if (pos < p->ss) return -1; if (pos >= p->se) return 1; return 0; } int findln(Tblock* b, int* pp) { int i; int pos; pos = *pp; for(i = 0; i < 128 && pos < b->nr; i++, pos++) if (b->r[pos] == '\n') break; if (i == 128 || b->r[pos] == '\n'){ *pp = pos; return 1; } return 0; } int findrln(Tblock* b, int* pp) { int i; int pos; pos = *pp; for(i = 0; i < 128 && pos > 0; i++, pos--) if (b->r[pos] == '\n') break; if (i == 128 || b->r[pos] == '\n' || pos == 0){ *pp = pos; return 1; } return 0; } int scrollframe(Panel* p, int nscroll) { Tblock* b; int n; int pos; int nlines; int l; fdprint("scroll %d\n", nscroll); nlines = abs(nscroll); assert(p->froff >= 0); if (nscroll == 0) return 0; packblock(p->blks); b = p->blks; if (nscroll > 0) { l = blocklen(b); if (p->froff + p->f.nchars >= l) return 0; n = p->froff; while(n < b->nr && nlines-- && findln(b, &n)) if (n < b->nr) n++; } else { nlines++; // adjust to skip the \n before this line if (p->froff <= 0){ if (blockdebug) blockdump(b); return 0; } n = p->froff; while(n > 0 && nlines-- && findrln(b, &n)) if (n > 0) n--; if (n > 0 && n < b->nr) // advance to skip last \n n++; } pos = n; if (b->r[pos] == '\n') pos++; p->froff = pos; framedump(p); return 1; } int frameins(Panel* p, Rune* r, int nr, int pos) { Tblock* b; int n; assert(pos <= blocklen(p->blks)); fdprint("ins %d (p0 %uld) %d runes [%.*S]\n", pos, p->f.p0, nr, nr, r); b = blockseek(p->blks, &n, pos); blockins(b, n, r, nr); p->dfile->length += runenlen(r, nr); if (pos >= p->froff){ if (p->loaded) frinsert(&p->f, r, r +nr, pos - p->froff); } else p->froff += nr; if (pos < p->ss) p->ss += nr; if (pos < p->se) p->se += nr; if (pos < p->s0) p->s0 += nr; if (pos < p->mark) p->mark += nr; return p->loaded ? p->f.lastlinefull : 0; } static int fixpos(int pos, int x, int n) { if (x < pos){ if (x + n > pos) n = pos - x; pos -= n; } return pos; } int framedel(Panel* p, Rune* r, int nr, int pos) { Tblock* b; int n; Rune nl[1]; Frame* f; nl[0] = L'\n'; assert(r); b = blockseek(p->blks, &n, pos); if (b == nil) return 0; blockdel(b, n, nr, r); p->dfile->length -= runenlen(r, nr); if (pos >= p->froff && p->loaded){ f = &p->f; frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0); frdelete(f, pos - p->froff, pos - p->froff + nr); /* Libframe may not clear the right of the last line. BUG? * we force that by inserting a fake \n */ if (!p->f.lastlinefull){ frinsert(f, nl, nl+1, f->nchars); frdelete(f, f->nchars-1, f->nchars); } frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1); } else p->froff = fixpos(p->froff, pos, nr); p->ss = fixpos(p->ss, pos, nr); p->se = fixpos(p->se, pos, nr); p->s0 = fixpos(p->s0, pos, nr); p->mark = fixpos(p->mark, pos, nr); return nr; } static int iswordchar(Rune r) { return isalpharune(r) || runestrchr(L"0123456789|&?=._-+/:", r); } static Rune lparen[] = L"{[(«<“"; static Rune rparen[] = L"}])»>”"; static Rune paren[] = L"\"'`"; static int isparen(Rune* set, Rune r) { Rune* p; p = runestrchr(set, r); if (p) return p - set + 1; else return 0; } static int findexpsep(void* a, int i, Rune r) { int* inword = a; //fprint(2, "[%d %C] ", i, r); if (*inword) return !iswordchar(r); else return (i > 128 || (int)r == '\n'); } /* Returns the word at pos. * If we are looking at the end of line, we pretend we look right * before it. In any case: * The word is the selection when it exists. * It is the longest set of s if pos at * It is the text between {} [] '' "" () if pos is at delim. * It is the current line otherwise (if pos at blank) */ Rune* gettextword(Panel* p, int pos, int* ss, int* se) { Rune* r; Tblock* b; int spos, epos; int nr; int pi; int nparen; b = p->blks; packblock(b); assert(pos <= b->nr); if (pos == b->nr && pos > 0) pos--; spos = epos = pos; if (b->nr == 0) goto Done; if (!posselcmp(p, pos) && p->ss != p->se){ spos = p->ss; epos = p->se; } else if (iswordchar(b->r[pos])){ while(spos > 0 && iswordchar(b->r[spos])) spos--; if (spos > 0) spos++; while(epos < b->nr && iswordchar(b->r[epos])) epos++; } else if (pi = isparen(paren, b->r[pos])){ spos++; for(epos = spos; epos < b->nr; epos++) if (isparen(paren, b->r[epos]) == pi) break; } else if (pi = isparen(lparen, b->r[pos])){ nparen = 1; spos++; for(epos = spos; epos < b->nr; epos++){ if (isparen(lparen, b->r[epos]) == pi) nparen++; if (isparen(rparen, b->r[epos]) == pi) nparen--; if (nparen <= 0){ break; } } } else if (pi = isparen(rparen, b->r[pos])){ nparen = 1; if (spos > 0) for(spos--; spos > 0; spos--){ if (isparen(rparen, b->r[spos]) == pi) nparen++; if (isparen(lparen, b->r[spos]) == pi) nparen--; if (nparen <= 0){ spos++; break; } } } else { // pos at blank if (b->r[spos] == '\n' && spos > 0 && b->r[spos-1] != '\n'){ // click at right part of line; step back // so that expanding leads to previous line spos--; } while(spos > 0 && b->r[spos-1] != '\n') spos--; while(epos < b->nr && b->r[epos] != '\n') epos++; if (epos < b->nr) epos++; // include \n } Done: if (spos < 0 || epos < 0 || epos < spos || epos > b->nr){ fprint(2, "spos/epos bug: %d %d %d\n", spos, epos, b->nr); abort(); } if (ss){ *ss = spos; *se = epos; } nr = epos - spos; r = emalloc9p((nr + 1) * sizeof(Rune)); if (nr > 0) blockget(b, spos, nr, r); r[nr] = 0; edprint("gettextword %.*S %d %d\n", nr, r, spos, epos); return r; }