#include #include #include #include #include #include #include #include "flayer.h" #include "samterm.h" #define DELTA 10 static Flayer **llist; /* front to back */ static int nllist; static int nlalloc; static Rectangle lDrect; Vis visibility(Flayer *); void newvisibilities(int); void llinsert(Flayer*); void lldelete(Flayer*); Image *maincols[NCOL]; Image *cmdcols[NCOL]; void flstart(Rectangle r) { lDrect = r; /* Main text is yellowish */ maincols[BACK] = allocimagemix(display, DPaleyellow, DWhite); maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow); maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DYellowgreen); maincols[TEXT] = display->black; maincols[HTEXT] = display->black; /* Command text is blueish */ cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite); cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen); cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue); cmdcols[TEXT] = display->black; cmdcols[HTEXT] = display->black; } void flnew(Flayer *l, Rune *(*fn)(Flayer*, long, ulong*), int u0, void *u1) { if(nllist == nlalloc){ nlalloc += DELTA; llist = realloc(llist, nlalloc*sizeof(Flayer**)); if(llist == 0) panic("flnew"); } l->textfn = fn; l->user0 = u0; l->user1 = u1; l->lastsr = ZR; llinsert(l); } Rectangle flrect(Flayer *l, Rectangle r) { rectclip(&r, lDrect); l->entire = r; l->scroll = insetrect(r, FLMARGIN); r.min.x = l->scroll.max.x = r.min.x+FLMARGIN+FLSCROLLWID+(FLGAP-FLMARGIN); return r; } static void fontbuggered(char *name) { fprint(2, "samterm: font %s has zero-width \"0\" character\n", name); threadexits("font zero-width"); } void flinit(Flayer *l, Rectangle r, Font *ft, Image **cols) { lldelete(l); llinsert(l); l->visible = All; l->origin = l->p0 = l->p1 = 0; frinit(&l->f, insetrect(flrect(l, r), FLMARGIN), ft, screen, cols); l->f.maxtab = maxtab*stringwidth(ft, "0"); if(l->f.maxtab == 0) fontbuggered(ft->name); newvisibilities(1); draw(screen, l->entire, l->f.cols[BACK], nil, ZP); scrdraw(l, 0L); flborder(l, 0); } void flclose(Flayer *l) { if(l->visible == All) draw(screen, l->entire, display->white, nil, ZP); else if(l->visible == Some){ if(l->f.b == 0) l->f.b = allocimage(display, l->entire, screen->chan, 0, DNofill); if(l->f.b){ draw(l->f.b, l->entire, display->white, nil, ZP); flrefresh(l, l->entire, 0); } } frclear(&l->f, 1); lldelete(l); if(l->f.b && l->visible!=All) freeimage(l->f.b); l->textfn = 0; newvisibilities(1); } void flborder(Flayer *l, int wide) { if(flprepare(l)){ border(l->f.b, l->entire, FLMARGIN, l->f.cols[BACK], ZP); border(l->f.b, l->entire, wide? FLMARGIN : 1, l->f.cols[BORD], ZP); if(l->visible==Some) flrefresh(l, l->entire, 0); } } Flayer * flwhich(Point p) { int i; if(p.x==0 && p.y==0) return nllist? llist[0] : 0; for(i=0; ientire)) return llist[i]; return 0; } void flupfront(Flayer *l) { int v = l->visible; lldelete(l); llinsert(l); if(v!=All) newvisibilities(0); } void newvisibilities(int redraw) /* if redraw false, we know it's a flupfront, and needn't * redraw anyone becoming partially covered */ { int i; Vis ov; Flayer *l; for(i = 0; ilastsr = ZR; /* make sure scroll bar gets redrawn */ ov = l->visible; l->visible = visibility(l); #define V(a, b) (((a)<<2)|((b))) switch(V(ov, l->visible)){ case V(Some, None): if(l->f.b) freeimage(l->f.b); case V(All, None): case V(All, Some): l->f.b = 0; frclear(&l->f, 0); break; case V(Some, Some): if(l->f.b==0 && redraw) case V(None, Some): flprepare(l); if(l->f.b && redraw){ flrefresh(l, l->entire, 0); freeimage(l->f.b); l->f.b = 0; frclear(&l->f, 0); } case V(None, None): case V(All, All): break; case V(Some, All): if(l->f.b){ draw(screen, l->entire, l->f.b, nil, l->entire.min); freeimage(l->f.b); l->f.b = screen; break; } case V(None, All): flprepare(l); break; } if(ov==None && l->visible!=None) flnewlyvisible(l); } } void llinsert(Flayer *l) { int i; for(i=nllist; i>0; --i) llist[i]=llist[i-1]; llist[0]=l; nllist++; } void lldelete(Flayer *l) { int i; for(i=0; if, sp, ep, p0-l->origin); scrdraw(l, scrtotal(l)); if(l->visible==Some) flrefresh(l, l->entire, 0); } } void fldelete(Flayer *l, long p0, long p1) { if(flprepare(l)){ p0 -= l->origin; if(p0 < 0) p0 = 0; p1 -= l->origin; if(p1<0) p1 = 0; frdelete(&l->f, p0, p1); scrdraw(l, scrtotal(l)); if(l->visible==Some) flrefresh(l, l->entire, 0); } } int flselect(Flayer *l) { int ret; if(l->visible!=All) flupfront(l); frselect(&l->f, mousectl); ret = 0; if(l->f.p0==l->f.p1){ if(mousep->msec-l->clickf.p0+l->origin==l->p0){ ret = 1; l->click = 0; }else l->click = mousep->msec; }else l->click = 0; l->p0 = l->f.p0+l->origin, l->p1 = l->f.p1+l->origin; return ret; } void flsetselect(Flayer *l, long p0, long p1) { ulong fp0, fp1; l->click = 0; if(l->visible==None || !flprepare(l)){ l->p0 = p0, l->p1 = p1; return; } l->p0 = p0, l->p1 = p1; flfp0p1(l, &fp0, &fp1); if(fp0==l->f.p0 && fp1==l->f.p1) return; if(fp1<=l->f.p0 || fp0>=l->f.p1 || l->f.p0==l->f.p1 || fp0==fp1){ /* no overlap or trivial repainting */ frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, l->f.p1, 0); frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, fp1, 1); goto Refresh; } /* the current selection and the desired selection overlap and are both non-empty */ if(fp0 < l->f.p0){ /* extend selection backwards */ frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, l->f.p0, 1); }else if(fp0 > l->f.p0){ /* trim first part of selection */ frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, fp0, 0); } if(fp1 > l->f.p1){ /* extend selection forwards */ frdrawsel(&l->f, frptofchar(&l->f, l->f.p1), l->f.p1, fp1, 1); }else if(fp1 < l->f.p1){ /* trim last part of selection */ frdrawsel(&l->f, frptofchar(&l->f, fp1), fp1, l->f.p1, 0); } Refresh: l->f.p0 = fp0; l->f.p1 = fp1; if(l->visible==Some) flrefresh(l, l->entire, 0); } void flfp0p1(Flayer *l, ulong *pp0, ulong *pp1) { long p0 = l->p0-l->origin, p1 = l->p1-l->origin; if(p0 < 0) p0 = 0; if(p1 < 0) p1 = 0; if(p0 > l->f.nchars) p0 = l->f.nchars; if(p1 > l->f.nchars) p1 = l->f.nchars; *pp0 = p0; *pp1 = p1; } Rectangle rscale(Rectangle r, Point old, Point new) { r.min.x = r.min.x*new.x/old.x; r.min.y = r.min.y*new.y/old.y; r.max.x = r.max.x*new.x/old.x; r.max.y = r.max.y*new.y/old.y; return r; } void flresize(Rectangle dr) { int i; Flayer *l; Frame *f; Rectangle r, olDrect; int move; olDrect = lDrect; lDrect = dr; move = 0; /* no moving on rio; must repaint */ if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect)) move = 1; else draw(screen, lDrect, display->white, nil, ZP); for(i=0; ilastsr = ZR; f = &l->f; if(move) r = rectaddpt(rectsubpt(l->entire, olDrect.min), dr.min); else{ r = rectaddpt(rscale(rectsubpt(l->entire, olDrect.min), subpt(olDrect.max, olDrect.min), subpt(dr.max, dr.min)), dr.min); if(l->visible==Some && f->b){ freeimage(f->b); frclear(f, 0); } f->b = 0; if(l->visible!=None) frclear(f, 0); } if(!rectclip(&r, dr)) panic("flresize"); if(r.max.x-r.min.x<100) r.min.x = dr.min.x; if(r.max.x-r.min.x<100) r.max.x = dr.max.x; if(r.max.y-r.min.y<2*FLMARGIN+f->font->height) r.min.y = dr.min.y; if(r.max.y-r.min.y<2*FLMARGIN+f->font->height) r.max.y = dr.max.y; if(!move) l->visible = None; frsetrects(f, insetrect(flrect(l, r), FLMARGIN), f->b); if(!move && f->b) scrdraw(l, scrtotal(l)); } newvisibilities(1); } int flprepare(Flayer *l) { Frame *f; ulong n; Rune *r; if(l->visible == None) return 0; f = &l->f; if(f->b == 0){ if(l->visible == All) f->b = screen; else if((f->b = allocimage(display, l->entire, screen->chan, 0, 0))==0) return 0; draw(f->b, l->entire, f->cols[BACK], nil, ZP); border(f->b, l->entire, l==llist[0]? FLMARGIN : 1, f->cols[BORD], ZP); n = f->nchars; frinit(f, f->entire, f->font, f->b, 0); f->maxtab = maxtab*stringwidth(f->font, "0"); if(f->maxtab == 0) fontbuggered(f->font->name); r = (*l->textfn)(l, n, &n); frinsert(f, r, r+n, (ulong)0); frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0); flfp0p1(l, &f->p0, &f->p1); frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1); l->lastsr = ZR; scrdraw(l, scrtotal(l)); } return 1; } static int somevis, someinvis, justvis; Vis visibility(Flayer *l) { somevis = someinvis = 0; justvis = 1; flrefresh(l, l->entire, 0); justvis = 0; if(somevis==0) return None; if(someinvis==0) return All; return Some; } void flrefresh(Flayer *l, Rectangle r, int i) { Flayer *t; Rectangle s; Top: if((t=llist[i++]) == l){ if(!justvis) draw(screen, r, l->f.b, nil, r.min); somevis = 1; }else{ if(!rectXrect(t->entire, r)) goto Top; /* avoid stacking unnecessarily */ if(t->entire.min.x>r.min.x){ s = r; s.max.x = t->entire.min.x; flrefresh(l, s, i); r.min.x = t->entire.min.x; } if(t->entire.min.y>r.min.y){ s = r; s.max.y = t->entire.min.y; flrefresh(l, s, i); r.min.y = t->entire.min.y; } if(t->entire.max.xentire.max.x; flrefresh(l, s, i); r.max.x = t->entire.max.x; } if(t->entire.max.yentire.max.y; flrefresh(l, s, i); r.max.y = t->entire.max.y; } /* remaining piece of r is blocked by t; forget about it */ someinvis = 1; } }