diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/acme/frame.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/acme/frame.b')
| -rw-r--r-- | appl/acme/frame.b | 1189 |
1 files changed, 1189 insertions, 0 deletions
diff --git a/appl/acme/frame.b b/appl/acme/frame.b new file mode 100644 index 00000000..5ab920a1 --- /dev/null +++ b/appl/acme/frame.b @@ -0,0 +1,1189 @@ +implement Framem; + +include "common.m"; + +sys : Sys; +drawm : Draw; +acme : Acme; +gui : Gui; +graph : Graph; +utils : Utils; +textm : Textm; + +sprint : import sys; +Point, Rect, Font, Image, Pointer : import drawm; +draw, berror, charwidth, strwidth : import graph; +black, white : import gui; + +SLOP : con 25; + +noglyphs := array[4] of { 16rFFFD, 16r80, '?', ' ' }; + +frame : ref Frame; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + drawm = mods.draw; + acme = mods.acme; + gui = mods.gui; + graph = mods.graph; + utils = mods.utils; + textm = mods.textm; + + frame = newframe(); +} + +nullframe : Frame; + +newframe() : ref Frame +{ + f := ref nullframe; + f.cols = array[NCOL] of ref Draw->Image; + return f; +} + +frdump(f : ref Frame) +{ + utils->debug(sprint("nchars=%d\n", f.nchars)); + for (i := 0; i < f.nbox; i++) { + utils->debug(sprint("box %d : ", i)); + fb := f.box[i]; + if (fb.nrune >= 0) + utils->debug(sprint("%d %d %s\n", fb.nrune, len fb.ptr, fb.ptr)); + else + utils->debug(sprint("%d\n", fb.nrune)); + } +} + +# debugcheck(f : ref Frame, n : int) +# { +# if (f.nchars != xfrstrlen(f, 0)) { +# utils->debug(sprint("%d : bad frame nchars\n", n)); +# frdump(f); +# berror(""); +# } +# } + +xfraddbox(f : ref Frame, bn : int, n : int) # add n boxes after bn, shift the rest up, + # * box[bn+n]==box[bn] +{ + i : int; + + if(bn > f.nbox) + berror("xfraddbox"); + # bn = f.nbox has same effect as bn = f.nbox-1 + if(f.nbox+n > f.nalloc) + xfrgrowbox(f, n+SLOP); + for (i=f.nbox; --i > bn; ) { + t := f.box[i+n]; + f.box[i+n] = f.box[i]; + f.box[i] = t; + } + if (bn < f.nbox) + *f.box[bn+n] = *f.box[bn]; + f.nbox+=n; +} + +xfrclosebox(f : ref Frame, n0 : int, n1 : int) # inclusive +{ + i: int; + + if(n0>=f.nbox || n1>=f.nbox || n1<n0) + berror("xfrclosebox"); + n1++; + for(i=n1; i<f.nbox; i++) { + t := f.box[i-(n1-n0)]; + f.box[i-(n1-n0)] = f.box[i]; + f.box[i] = t; + } + f.nbox -= n1-n0; +} + +xfrdelbox(f : ref Frame, n0 : int, n1 : int) # inclusive +{ + if(n0>=f.nbox || n1>=f.nbox || n1<n0) + berror("xfrdelbox"); + xfrfreebox(f, n0, n1); + xfrclosebox(f, n0, n1); +} + +xfrfreebox(f : ref Frame, n0 : int, n1 : int) # inclusive +{ + i : int; + + if(n1<n0) + return; + if(n0>=f.nbox || n1>=f.nbox) + berror("xfrfreebox"); + n1++; + for(i=n0; i<n1; i++) + if(f.box[i].nrune >= 0) { + f.box[i].nrune = 0; + f.box[i].ptr = nil; + } +} + +nilfrbox : Frbox; + +xfrgrowbox(f : ref Frame, delta : int) +{ + ofb := f.box; + f.box = array[f.nalloc+delta] of ref Frbox; + if(f.box == nil) + berror("xfrgrowbox"); + f.box[0:] = ofb[0:f.nalloc]; + for (i := 0; i < delta; i++) + f.box[i+f.nalloc] = ref nilfrbox; + f.nalloc += delta; + ofb = nil; +} + +dupbox(f : ref Frame, bn : int) +{ + if(f.box[bn].nrune < 0) + berror("dupbox"); + xfraddbox(f, bn, 1); + if(f.box[bn].nrune >= 0) { + f.box[bn+1].nrune = f.box[bn].nrune; + f.box[bn+1].ptr = f.box[bn].ptr; + } +} + +truncatebox(f : ref Frame, b : ref Frbox, n : int) # drop last n chars; no allocation done +{ + if(b.nrune<0 || b.nrune<n) + berror("truncatebox"); + b.nrune -= n; + b.ptr = b.ptr[0:b.nrune]; + b.wid = strwidth(f.font, b.ptr); +} + +chopbox(f : ref Frame, b : ref Frbox, n : int) # drop first n chars; no allocation done +{ + if(b.nrune<0 || b.nrune<n) + berror("chopbox"); + b.nrune -= n; + b.ptr = b.ptr[n:]; + b.wid = strwidth(f.font, b.ptr); +} + +xfrsplitbox(f : ref Frame, bn : int, n : int) +{ + dupbox(f, bn); + truncatebox(f, f.box[bn], f.box[bn].nrune-n); + chopbox(f, f.box[bn+1], n); +} + +xfrmergebox(f : ref Frame, bn : int) # merge bn and bn+1 +{ + b0 := f.box[bn]; + b1 := f.box[bn+1]; + b0.ptr += b1.ptr; + b0.wid += b1.wid; + b0.nrune += b1.nrune; + xfrdelbox(f, bn+1, bn+1); +} + +xfrfindbox(f : ref Frame, bn : int, p : int, q : int) : int # find box containing q and put q on a box boundary +{ + nrune : int; + + for( ; bn < f.nbox; bn++) { + nrune = 1; + b := f.box[bn]; +# if (b.nrune >= 0 && len b.ptr != b.nrune) { +# frdump(f); +# berror(sprint("findbox %d %d %d\n", bn, p, q)); +# } + if(b.nrune >= 0) + nrune = b.nrune; + if(p+nrune > q) + break; + p += nrune; + } + if(p != q) + xfrsplitbox(f, bn++, q-p); + return bn; +} + +frdelete(f : ref Frame, p0 : int, p1 : int) : int +{ + pt0, pt1, ppt0 : Point; + n0, n1, n, s : int; + r : Rect; + nn0 : int; + col : ref Image; + + if(p0 >= f.nchars || p0 == p1 || f.b == nil) + return 0; + if(p1 > f.nchars) + p1 = f.nchars; + n0 = xfrfindbox(f, 0, 0, p0); + if(n0 == f.nbox) + berror("off end in frdelete"); + n1 = xfrfindbox(f, n0, p0, p1); + pt0 = xfrptofcharnb(f, p0, n0); + pt1 = frptofchar(f, p1); + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 0); + nn0 = n0; + ppt0 = pt0; + xfrfreebox(f, n0, n1-1); + f.modified = 1; + + # + # Invariants: + # pt0 points to beginning, pt1 points to end + # n0 is box containing beginning of stuff being deleted + # n1, b are box containing beginning of stuff to be kept after deletion + # cn1 is char position of n1 + # f.p0 and f.p1 are not adjusted until after all deletion is done + # region between pt0 and pt1 is clear + # + cn1 := p1; + while(pt1.x!=pt0.x && n1<f.nbox){ + b := f.box[n1]; + pt0 = xfrcklinewrap0(f, pt0, b); + pt1 = xfrcklinewrap(f, pt1, b); + n = xfrcanfit(f, pt0, b); + if(n==0) + berror("xfrcanfit==0"); + r.min = pt0; + r.max = pt0; + r.max.y += f.font.height; + if(b.nrune > 0){ + if(n != b.nrune){ + xfrsplitbox(f, n1, n); + b = f.box[n1]; + } + r.max.x += b.wid; + draw(f.b, r, f.b, nil, pt1); + cn1 += b.nrune; + } + else{ + r.max.x += xfrnewwid0(f, pt0, b); + if(r.max.x > f.r.max.x) + r.max.x = f.r.max.x; + col = f.cols[BACK]; + if(f.p0<=cn1 && cn1<f.p1) + col = f.cols[HIGH]; + draw(f.b, r, col, nil, pt0); + cn1++; + } + pt1 = xfradvance(f, pt1, b); + pt0.x += xfrnewwid(f, pt0, b); + *f.box[n0++] = *f.box[n1++]; + } + if(n1==f.nbox && pt0.x!=pt1.x) # deleting last thing in window; must clean up + frselectpaint(f, pt0, pt1, f.cols[BACK]); + if(pt1.y != pt0.y){ + pt2 : Point; + + pt2 = xfrptofcharptb(f, 32767, pt1, n1); + if(pt2.y > f.r.max.y) + berror("frptofchar in frdelete"); + if(n1 < f.nbox){ + q0, q1, q2 : int; + + q0 = pt0.y+f.font.height; + q1 = pt1.y+f.font.height; + q2 = pt2.y+f.font.height; + # rob: before was just q2 = pt1.y+f.font.height; + # q2 = pt2.y; + if(q2 > f.r.max.y) + q2 = f.r.max.y; + draw(f.b, (pt0, (pt0.x+(f.r.max.x-pt1.x), q0)), + f.b, nil, pt1); + draw(f.b, ((f.r.min.x, q0), (f.r.max.x, q0+(q2-q1))), + f.b, nil, (f.r.min.x, q1)); + frselectpaint(f, (pt2.x, pt2.y-(pt1.y-pt0.y)), pt2, f.cols[BACK]); + }else + frselectpaint(f, pt0, pt2, f.cols[BACK]); + } + xfrclosebox(f, n0, n1-1); + if(nn0>0 && f.box[nn0-1].nrune>=0 && ppt0.x-f.box[nn0-1].wid>=f.r.min.x){ + --nn0; + ppt0.x -= f.box[nn0].wid; + } + s = n0; + if(n0 < f.nbox-1) + s++; + xfrclean(f, ppt0, nn0, s); + if(f.p1 > p1) + f.p1 -= p1-p0; + else if(f.p1 > p0) + f.p1 = p0; + if(f.p0 > p1) + f.p0 -= p1-p0; + else if(f.p0 > p0) + f.p0 = p0; + f.nchars -= p1-p0; + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 1); + pt0 = frptofchar(f, f.nchars); + n = f.nlines; + f.nlines = (pt0.y-f.r.min.y)/f.font.height+(pt0.x>f.r.min.x); + return n - f.nlines; +} + +xfrredraw(f : ref Frame, pt : Point) +{ + nb : int; + + for(nb = 0; nb < f.nbox; nb++) { + b := f.box[nb]; + pt = xfrcklinewrap(f, pt, b); + if(b.nrune >= 0) + graph->stringx(f.b, pt, f.font, b.ptr, f.cols[TEXT]); + pt.x += b.wid; + } +} + +frdrawsel(f : ref Frame, pt : Point, p0 : int, p1 : int, issel : int) +{ + back, text : ref Image; + + if(f.ticked) + frtick(f, frptofchar(f, f.p0), 0); + if(p0 == p1){ + frtick(f, pt, issel); + return; + } + if(issel){ + back = f.cols[HIGH]; + text = f.cols[HTEXT]; + }else{ + back = f.cols[BACK]; + text = f.cols[TEXT]; + } + frdrawsel0(f, pt, p0, p1, back, text); +} + +frdrawsel0(f : ref Frame, pt : Point, p0 : int, p1 : int, back : ref Image, text : ref Image) +{ + b : ref Frbox; + nb, nr, w, x, trim : int; + qt : Point; + p : int; + ptr : string; + + p = 0; + trim = 0; + for(nb=0; nb<f.nbox && p<p1; nb++){ + b = f.box[nb]; + nr = b.nrune; + if(nr < 0) + nr = 1; + if(p+nr <= p0){ + p += nr; + continue; + } + if(p >= p0){ + qt = pt; + pt = xfrcklinewrap(f, pt, b); + if(pt.y > qt.y) + draw(f.b, (qt, (f.r.max.x, pt.y)), back, nil, qt); + } + ptr = b.ptr; + if(p < p0){ # beginning of region: advance into box + ptr = ptr[p0-p:]; + nr -= (p0-p); + p = p0; + } + trim = 0; + if(p+nr > p1){ # end of region: trim box + nr -= (p+nr)-p1; + trim = 1; + } + if(b.nrune<0 || nr==b.nrune) + w = b.wid; + else + w = strwidth(f.font, ptr[0:nr]); + x = pt.x+w; + if(x > f.r.max.x) + x = f.r.max.x; + draw(f.b, (pt, (x, pt.y+f.font.height)), back, nil, pt); + if(b.nrune >= 0) + graph->stringx(f.b, pt, f.font, ptr[0:nr], text); + pt.x += w; + p += nr; + } + # if this is end of last plain text box on wrapped line, fill to end of line + if(p1>p0 && nb>0 && nb<f.nbox && f.box[nb-1].nrune>0 && !trim){ + qt = pt; + pt = xfrcklinewrap(f, pt, f.box[nb]); + if(pt.y > qt.y) + draw(f.b, (qt, (f.r.max.x, pt.y)), back, nil, qt); + } +} + +frtick(f : ref Frame, pt : Point, ticked : int) +{ + r : Rect; + + if(f.ticked==ticked || f.tick==nil || !pt.in(f.r)) + return; + pt.x--; # looks best just left of where requested + r = (pt, (pt.x+FRTICKW, pt.y+f.font.height)); + if(ticked){ + draw(f.tickback, f.tickback.r, f.b, nil, pt); + draw(f.b, r, f.tick, nil, (0, 0)); + }else + draw(f.b, r, f.tickback, nil, (0, 0)); + f.ticked = ticked; +} + +xfrdraw(f : ref Frame, pt : Point) : Point +{ + nb, n : int; + + for(nb=0; nb < f.nbox; nb++){ + b := f.box[nb]; + pt = xfrcklinewrap0(f, pt, b); + if(pt.y == f.r.max.y){ + f.nchars -= xfrstrlen(f, nb); + xfrdelbox(f, nb, f.nbox-1); + break; + } + if(b.nrune > 0){ + n = xfrcanfit(f, pt, b); + if(n == 0) + berror("draw: xfrcanfit==0"); + if(n != b.nrune){ + xfrsplitbox(f, nb, n); + b = f.box[nb]; + } + pt.x += b.wid; + }else{ + if(b.bc == '\n') { + pt.x = f.r.min.x; + pt.y += f.font.height; + } + else + pt.x += xfrnewwid(f, pt, b); + } + } + return pt; +} + +xfrstrlen(f : ref Frame, nb : int) : int +{ + n, nrune : int; + + for(n=0; nb<f.nbox; nb++) { + nrune = f.box[nb].nrune; + if(nrune < 0) + nrune = 1; + n += nrune; + } + return n; +} + +frinit(f : ref Frame, r : Rect, ft : ref Font, b : ref Image, cols : array of ref Draw->Image) +{ + f.font = ft; + f.scroll = 0; + f.maxtab = 8*charwidth(ft, '0'); + f.nbox = 0; + f.nalloc = 0; + f.nchars = 0; + f.nlines = 0; + f.p0 = 0; + f.p1 = 0; + f.box = nil; + f.lastlinefull = 0; + if(cols != nil) + for(i := 0; i < NCOL; i++) + f.cols[i] = cols[i]; + for (i = 0; i < len noglyphs; i++) { + if (charwidth(ft, noglyphs[i]) != 0) { + f.noglyph = noglyphs[i]; + break; + } + } + frsetrects(f, r, b); + if (f.tick==nil && f.cols[BACK] != nil) + frinittick(f); +} + +frinittick(f : ref Frame) +{ + ft : ref Font; + + ft = f.font; + f.tick = nil; + f.tick = graph->balloc(((0, 0), (FRTICKW, ft.height)), (gui->mainwin).chans, Draw->White); + if(f.tick == nil) + return; + f.tickback = graph->balloc(f.tick.r, (gui->mainwin).chans, Draw->White); + if(f.tickback == nil){ + f.tick = nil; + return; + } + # background color + draw(f.tick, f.tick.r, f.cols[BACK], nil, (0, 0)); + # vertical line + draw(f.tick, ((FRTICKW/2, 0), (FRTICKW/2+1, ft.height)), black, nil, (0, 0)); + # box on each end + # draw(f->tick, Rect(0, 0, FRTICKW, FRTICKW), f->cols[TEXT], nil, ZP); + # draw(f->tick, Rect(0, ft->height-FRTICKW, FRTICKW, ft->height), f->cols[TEXT], nil, ZP); +} + +frsetrects(f : ref Frame, r : Rect, b : ref Image) +{ + f.b = b; + f.entire = r; + f.r = r; + f.r.max.y -= (r.max.y-r.min.y)%f.font.height; + f.maxlines = (r.max.y-r.min.y)/f.font.height; +} + +frclear(f : ref Frame, freeall : int) +{ + if(f.nbox) + xfrdelbox(f, 0, f.nbox-1); + for (i := 0; i < f.nalloc; i++) + f.box[i] = nil; + if(freeall) + f.tick = f.tickback = nil; + f.box = nil; + f.ticked = 0; +} + +DELTA : con 25; +TMPSIZE : con 256; + +Plist : adt { + pt0 : Point; + pt1 : Point; +}; + +nalloc : int = 0; +pts : array of Plist; + +bxscan(f : ref Frame, rp : string, l : int, ppt : Point) : (Point, Point) +{ + w, c, nb, delta, nl, nr : int; + sp : int = 0; + + frame.r = f.r; + frame.b = f.b; + frame.font = f.font; + frame.maxtab = f.maxtab; + frame.nbox = 0; + frame.nchars = 0; + for(i := 0; i < NCOL; i++) + frame.cols[i] = f.cols[i]; + frame.noglyph = f.noglyph; + delta = DELTA; + nl = 0; + for(nb=0; sp<l && nl <= f.maxlines; nb++){ + if(nb == frame.nalloc){ + xfrgrowbox(frame, delta); + if(delta < 10000) + delta *= 2; + } + b := frame.box[nb]; + c = rp[sp]; + if(c=='\t' || c=='\n'){ + b.bc = c; + b.wid = 5000; + if(c == '\n') + b.minwid = 0; + else + b.minwid = charwidth(frame.font, ' '); + b.nrune = -1; + if(c=='\n') + nl++; + frame.nchars++; + sp++; + }else{ + nr = 0; + w = 0; + ssp := sp; + nul := 0; + while(sp < l){ + c = rp[sp]; + if(c=='\t' || c=='\n') + break; + if(nr+1 >= TMPSIZE) + break; + if ((cw := charwidth(frame.font, c)) == 0) { # used to be only for c == 0 + c = frame.noglyph; + cw = charwidth(frame.font, c); + nul = 1; + } + w += cw; + sp++; + nr++; + } + b = frame.box[nb]; + b.ptr = rp[ssp:sp]; + b.wid = w; + b.nrune = nr; + frame.nchars += nr; + if (nul) { + for (i = 0; i < nr; i++) + if (charwidth(frame.font, b.ptr[i]) == 0) + b.ptr[i] = frame.noglyph; + } + } + frame.nbox++; + } + ppt = xfrcklinewrap0(f, ppt, frame.box[0]); + return (xfrdraw(frame, ppt), ppt); +} + +chopframe(f : ref Frame, pt : Point, p : int, bn : int) +{ + nb, nrune : int; + + for(nb = bn; ; nb++){ + if(nb >= f.nbox) + berror("endofframe"); + b := f.box[nb]; + pt = xfrcklinewrap(f, pt, b); + if(pt.y >= f.r.max.y) + break; + nrune = b.nrune; + if(nrune < 0) + nrune = 1; + p += nrune; + pt = xfradvance(f, pt, b); + } + f.nchars = p; + f.nlines = f.maxlines; + if (nb < f.nbox) # BUG + xfrdelbox(f, nb, f.nbox-1); +} + +frinsert(f : ref Frame, rp : string, l : int, p0 : int) +{ + pt0, pt1, ppt0, ppt1, pt : Point; + s, n, n0, nn0, y : int; + r : Rect; + npts : int; + col : ref Image; + + if(p0 > f.nchars || l == 0 || f.b == nil) + return; + n0 = xfrfindbox(f, 0, 0, p0); + cn0 := p0; + nn0 = n0; + pt0 = xfrptofcharnb(f, p0, n0); + ppt0 = pt0; + (pt1, ppt0) = bxscan(f, rp, l, ppt0); + ppt1 = pt1; + if(n0 < f.nbox){ + b := f.box[n0]; + pt0 = xfrcklinewrap(f, pt0, b); # for frdrawsel() + ppt1 = xfrcklinewrap0(f, ppt1, b); + } + f.modified = 1; + # + # ppt0 and ppt1 are start and end of insertion as they will appear when + # insertion is complete. pt0 is current location of insertion position + # (p0); pt1 is terminal point (without line wrap) of insertion. + # + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 0); + + # + # Find point where old and new x's line up + # Invariants: + # pt0 is where the next box (b, n0) is now + # pt1 is where it will be after then insertion + # If pt1 goes off the rectangle, we can toss everything from there on + # + + for(npts=0; pt1.x!= pt0.x && pt1.y!=f.r.max.y && n0<f.nbox; npts++){ + b := f.box[n0]; + pt0 = xfrcklinewrap(f, pt0, b); + pt1 = xfrcklinewrap0(f, pt1, b); + if(b.nrune > 0){ + n = xfrcanfit(f, pt1, b); + if(n == 0) + berror("xfrcanfit==0"); + if(n != b.nrune){ + xfrsplitbox(f, n0, n); + b = f.box[n0]; + } + } + if(npts == nalloc){ + opts := pts; + pts = array[npts+DELTA] of Plist; + pts[0:] = opts[0:npts]; + for (k := 0; k < DELTA; k++) + pts[k+npts].pt0 = pts[k+npts].pt1 = (0, 0); + opts = nil; + nalloc += DELTA; + b = f.box[n0]; + } + pts[npts].pt0 = pt0; + pts[npts].pt1 = pt1; + # has a text box overflowed off the frame? + if(pt1.y == f.r.max.y) + break; + pt0 = xfradvance(f, pt0, b); + pt1.x += xfrnewwid(f, pt1, b); + n0++; + nrune := b.nrune; + if(nrune < 0) + nrune = 1; + cn0 += nrune; + } + if(pt1.y > f.r.max.y) + berror("frinsert pt1 too far"); + if(pt1.y==f.r.max.y && n0<f.nbox){ + f.nchars -= xfrstrlen(f, n0); + xfrdelbox(f, n0, f.nbox-1); + } + if(n0 == f.nbox) + f.nlines = (pt1.y-f.r.min.y)/f.font.height+(pt1.x>f.r.min.x); + else if(pt1.y!=pt0.y){ + q0, q1 : int; + + y = f.r.max.y; + q0 = pt0.y+f.font.height; + q1 = pt1.y+f.font.height; + f.nlines += (q1-q0)/f.font.height; + if(f.nlines > f.maxlines) + chopframe(f, ppt1, p0, nn0); + if(pt1.y < y){ + r = f.r; + r.min.y = q1; + r.max.y = y; + if(q1 < y) + draw(f.b, r, f.b, nil, (f.r.min.x, q0)); + r.min = pt1; + r.max.x = pt1.x+(f.r.max.x-pt0.x); + r.max.y = q1; + draw(f.b, r, f.b, nil, pt0); + } + } + # + # Move the old stuff down to make room. The loop will move the stuff + # between the insertion and the point where the x's lined up. + # The draws above moved everything down after the point they lined up. + # + y = 0; + if(pt1.y == f.r.max.y) + y = pt1.y; + for(j := n0-1; --npts >= 0; --j){ + pt = pts[npts].pt1; + b := f.box[j]; + if(b.nrune > 0){ + r.min = pt; + r.max = r.min; + r.max.x += b.wid; + r.max.y += f.font.height; + draw(f.b, r, f.b, nil, pts[npts].pt0); + if(pt.y < y){ # clear bit hanging off right + r.min = pt; + r.max = pt; + r.min.x += b.wid; + r.max.x = f.r.max.x; + r.max.y += f.font.height; + if(f.p0<=cn0 && cn0<f.p1) # b+1 is inside selection + col = f.cols[HIGH]; + else + col = f.cols[BACK]; + draw(f.b, r, col, nil, r.min); + } + y = pt.y; + cn0 -= b.nrune; + }else{ + r.min = pt; + r.max = pt; + r.max.x += b.wid; + r.max.y += f.font.height; + if(r.max.x >= f.r.max.x) + r.max.x = f.r.max.x; + cn0--; + if(f.p0<=cn0 && cn0<f.p1) # b is inside selection + col = f.cols[HIGH]; + else + col = f.cols[BACK]; + draw(f.b, r, col, nil, r.min); + y = 0; + if(pt.x == f.r.min.x) + y = pt.y; + } + } + # insertion can extend the selection, so the condition here is different + if(f.p0<p0 && p0<=f.p1) + col = f.cols[HIGH]; + else + col = f.cols[BACK]; + frselectpaint(f, ppt0, ppt1, col); + xfrredraw(frame, ppt0); + xfraddbox(f, nn0, frame.nbox); + for(n=0; n<frame.nbox; n++) + *f.box[nn0+n] = *frame.box[n]; + if(nn0>0 && f.box[nn0-1].nrune>=0 && ppt0.x-f.box[nn0-1].wid>=f.r.min.x){ + --nn0; + ppt0.x -= f.box[nn0].wid; + } + n0 += frame.nbox; + s = n0; + if(n0 < f.nbox-1) + s++; + xfrclean(f, ppt0, nn0, s); + f.nchars += frame.nchars; + if(f.p0 >= p0) + f.p0 += frame.nchars; + if(f.p0 > f.nchars) + f.p0 = f.nchars; + if(f.p1 >= p0) + f.p1 += frame.nchars; + if(f.p1 > f.nchars) + f.p1 = f.nchars; + if(f.p0 == f.p1) + frtick(f, frptofchar(f, f.p0), 1); +} + +xfrptofcharptb(f : ref Frame, p : int, pt : Point, bn : int) : Point +{ + s : int; + l : int; + r : int; + + for( ; bn < f.nbox; bn++){ + b := f.box[bn]; + pt = xfrcklinewrap(f, pt, b); + l = b.nrune; + if(l < 0) + l = 1; + if(p < l){ + if(b.nrune > 0) + for(s = 0; p > 0; s++){ + r = b.ptr[s]; + pt.x += charwidth(f.font, r); + if(r==0 || pt.x>f.r.max.x) + berror("frptofchar"); + p--; + } + break; + } + p -= l; + pt = xfradvance(f, pt, b); + } + return pt; +} + +frptofchar(f : ref Frame, p : int) : Point +{ + return xfrptofcharptb(f, p, f.r.min, 0); +} + +xfrptofcharnb(f : ref Frame, p : int, nb : int) : Point # doesn't do final xfradvance to next line +{ + pt : Point; + nbox : int; + + nbox = f.nbox; + f.nbox = nb; + pt = xfrptofcharptb(f, p, f.r.min, 0); + f.nbox = nbox; + return pt; +} + +xfrgrid(f : ref Frame, p: Point) : Point +{ + p.y -= f.r.min.y; + p.y -= p.y%f.font.height; + p.y += f.r.min.y; + if(p.x > f.r.max.x) + p.x = f.r.max.x; + return p; +} + +frcharofpt(f : ref Frame, pt : Point) : int +{ + qt : Point; + bn, nrune : int; + s : int; + p : int; + r : int; + + pt = xfrgrid(f, pt); + qt = f.r.min; + + bn=0; + for(p=0; bn<f.nbox && qt.y<pt.y; bn++){ + b := f.box[bn]; + qt = xfrcklinewrap(f, qt, b); + if(qt.y >= pt.y) + break; + qt = xfradvance(f, qt, b); + nrune = b.nrune; + if(nrune < 0) + nrune = 1; + p += nrune; + } + + for(; bn<f.nbox && qt.x<=pt.x; bn++){ + b := f.box[bn]; + qt = xfrcklinewrap(f, qt, b); + if(qt.y > pt.y) + break; + if(qt.x+b.wid > pt.x){ + if(b.nrune < 0) + qt = xfradvance(f, qt, b); + else{ + s = 0; + for(;;){ + r = b.ptr[s++]; + qt.x += charwidth(f.font, r); + if(qt.x > pt.x) + break; + p++; + } + } + }else{ + nrune = b.nrune; + if(nrune < 0) + nrune = 1; + p += nrune; + qt = xfradvance(f, qt, b); + } + } + return p; +} + +region(a, b : int) : int +{ + if(a < b) + return -1; + if(a == b) + return 0; + return 1; +} + +frselect(f : ref Frame, m : ref Pointer) # when called, button 1 is down +{ + p0, p1, q : int; + mp, pt0, pt1, qt : Point; + b, scrled, reg : int; + + mp = m.xy; + b = m.buttons; + + f.modified = 0; + frdrawsel(f, frptofchar(f, f.p0), f.p0, f.p1, 0); + p0 = p1 = frcharofpt(f, mp); + f.p0 = p0; + f.p1 = p1; + pt0 = frptofchar(f, p0); + pt1 = frptofchar(f, p1); + frdrawsel(f, pt0, p0, p1, 1); + do{ + scrled = 0; + if(f.scroll){ + if(m.xy.y < f.r.min.y){ + textm->framescroll(f, -(f.r.min.y-m.xy.y)/f.font.height-1); + p0 = f.p1; + p1 = f.p0; + scrled = 1; + }else if(m.xy.y > f.r.max.y){ + textm->framescroll(f, (m.xy.y-f.r.max.y)/f.font.height+1); + p0 = f.p0; + p1 = f.p1; + scrled = 1; + } + if(scrled){ + pt0 = frptofchar(f, p0); + pt1 = frptofchar(f, p1); + reg = region(p1, p0); + } + } + q = frcharofpt(f, m.xy); + if(p1 != q){ + if(reg != region(q, p0)){ # crossed starting point; reset + if(reg > 0) + frdrawsel(f, pt0, p0, p1, 0); + else if(reg < 0) + frdrawsel(f, pt1, p1, p0, 0); + p1 = p0; + pt1 = pt0; + reg = region(q, p0); + if(reg == 0) + frdrawsel(f, pt0, p0, p1, 1); + } + qt = frptofchar(f, q); + if(reg > 0){ + if(q > p1) + frdrawsel(f, pt1, p1, q, 1); + else if(q < p1) + frdrawsel(f, qt, q, p1, 0); + }else if(reg < 0){ + if(q > p1) + frdrawsel(f, pt1, p1, q, 0); + else + frdrawsel(f, qt, q, p1, 1); + } + p1 = q; + pt1 = qt; + } + f.modified = 0; + if(p0 < p1) { + f.p0 = p0; + f.p1 = p1; + } + else { + f.p0 = p1; + f.p1 = p0; + } + if(scrled) + textm->framescroll(f, 0); + graph->bflush(); + if(!scrled) + acme->frgetmouse(); + }while(m.buttons == b); +} + +frselectpaint(f : ref Frame, p0 : Point, p1 : Point, col : ref Image) +{ + n : int; + q0, q1 : Point; + + q0 = p0; + q1 = p1; + q0.y += f.font.height; + q1.y += f.font.height; + n = (p1.y-p0.y)/f.font.height; + if(f.b == nil) + berror("frselectpaint b==0"); + if(p0.y == f.r.max.y) + return; + if(n == 0) + draw(f.b, (p0, q1), col, nil, (0, 0)); + else{ + if(p0.x >= f.r.max.x) + p0.x = f.r.max.x-1; + draw(f.b, ((p0.x, p0.y), (f.r.max.x, q0.y)), col, nil, (0, 0)); + if(n > 1) + draw(f.b, ((f.r.min.x, q0.y), (f.r.max.x, p1.y)), + col, nil, (0, 0)); + draw(f.b, ((f.r.min.x, p1.y), (q1.x, q1.y)), + col, nil, (0, 0)); + } +} + +xfrcanfit(f : ref Frame, pt : Point, b : ref Frbox) : int +{ + left, nr : int; + p : int; + r : int; + + left = f.r.max.x-pt.x; + if(b.nrune < 0) + return b.minwid <= left; + if(left >= b.wid) + return b.nrune; + nr = 0; + for(p = 0; p < len b.ptr; p++){ + r = b.ptr[p]; + left -= charwidth(f.font, r); + if(left < 0) + return nr; + nr++; + } + berror("xfrcanfit can't"); + return 0; +} + +xfrcklinewrap(f : ref Frame, p : Point, b : ref Frbox) : Point +{ + wid : int; + + if(b.nrune < 0) + wid = b.minwid; + else + wid = b.wid; + + if(wid > f.r.max.x-p.x){ + p.x = f.r.min.x; + p.y += f.font.height; + } + return p; +} + +xfrcklinewrap0(f : ref Frame, p : Point, b : ref Frbox) : Point +{ + if(xfrcanfit(f, p, b) == 0){ + p.x = f.r.min.x; + p.y += f.font.height; + } + return p; +} + +xfrcklinewrap1(f : ref Frame, p : Point, wid : int) : Point +{ + if(wid > f.r.max.x-p.x){ + p.x = f.r.min.x; + p.y += f.font.height; + } + return p; +} + +xfradvance(f : ref Frame, p : Point, b : ref Frbox) : Point +{ + if(b.nrune<0 && b.bc=='\n'){ + p.x = f.r.min.x; + p.y += f.font.height; + }else + p.x += b.wid; + return p; +} + +xfrnewwid(f : ref Frame, pt : Point, b : ref Frbox) : int +{ + b.wid = xfrnewwid0(f, pt, b); + return b.wid; +} + +xfrnewwid0(f : ref Frame, pt : Point, b : ref Frbox) : int +{ + c, x : int; + + c = f.r.max.x; + x = pt.x; + if(b.nrune >= 0 || b.bc != '\t') + return b.wid; + if(x+b.minwid > c) + x = pt.x = f.r.min.x; + x += f.maxtab; + x -= (x-f.r.min.x)%f.maxtab; + if(x-pt.x<b.minwid || x>c) + x = pt.x+b.minwid; + return x-pt.x; +} + +xfrclean(f : ref Frame, pt : Point, n0 : int, n1 : int) # look for mergeable boxes +{ + nb, c : int; + + c = f.r.max.x; + for(nb=n0; nb<n1-1; nb++){ + b0 := f.box[nb]; + b1 := f.box[nb+1]; + pt = xfrcklinewrap(f, pt, b0); + while(b0.nrune>=0 && nb<n1-1 && b1.nrune>=0 && pt.x+b0.wid+b1.wid<c){ + xfrmergebox(f, nb); + n1--; + b0 = f.box[nb]; + b1 = f.box[nb+1]; + } + pt = xfradvance(f, pt, f.box[nb]); + } + for(; nb<f.nbox; nb++){ + b := f.box[nb]; + pt = xfrcklinewrap(f, pt, b); + pt = xfradvance(f, pt, f.box[nb]); + } + f.lastlinefull = 0; + if(pt.y >= f.r.max.y) + f.lastlinefull = 1; +} |
