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/lib/winplace.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/winplace.b')
| -rw-r--r-- | appl/lib/winplace.b | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/appl/lib/winplace.b b/appl/lib/winplace.b new file mode 100644 index 00000000..ed454737 --- /dev/null +++ b/appl/lib/winplace.b @@ -0,0 +1,359 @@ +implement Winplace; + +# +# Copyright © 2003 Vita Nuova Holdings Limited +# + +include "sys.m"; + sys: Sys; +include "draw.m"; + draw: Draw; + Rect, Point: import draw; +include "winplace.m"; + +Delta: adt { + d: int; # +1 or -1 + wid: int; # index into wr + coord: int; # x/y coord +}; + +EW, NS: con iota; +Lay: adt { + d: int; + x: fn(l: self Lay, p: Point): int; + y: fn(l: self Lay, p: Point): int; + mkr: fn(l: self Lay, r: Rect): Rect; +}; + +init() +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; +} + +place(wins: list of Rect, scr, lastrect: Rect, minsize: Point): Rect +{ + found := find(wins, scr); + if(found != nil){ + # first look for any spaces big enough to hold minsize; + # choose top-left of those available. + (ok, best) := findfit(found, minsize); + if (ok){ + if(minsize.x == 0) + return best; + return (best.min, best.min.add(minsize)); + } + if(minsize.x == 0) + minsize = scr.size().div(2); + } + # no big enough space; try to avoid covering titlebars + tfound := find(titlebarrects(wins), scr); + (ok, best) := findfit(tfound, minsize); + if (ok) + return (best.min, best.min.add(minsize)); + tfound = nil; + + # no areas available - just find somewhere. + if(found == nil) + return somespace(scr, lastrect, minsize); + + # no big enough space found; find the largest area available + # that will fit within minsize + best = clipsize(hd found, minsize); + area := best.dx() * best.dy(); + for (fl := tl found; fl != nil; fl = tl fl) { + r := clipsize(hd fl, minsize); + rarea := r.dx() * r.dy(); + if (rarea > area || (rarea == area && better(r, best))) + (area, best) = (rarea, r); + } + best.max = best.min.add(minsize); + return checkrect(best, scr); +} + +findfit(found: list of Rect, minsize: Point): (int, Rect) +{ + best: Rect; + ok := 0; + for (fl := found; fl != nil; fl = tl fl) { + r := hd fl; + if (r.dx() < minsize.x || r.dy() < minsize.y) + continue; + if (!ok || better(r, best)) { + best = r; + ok++; + } + } + return (ok, best); +} + +TBARWIDTH: con 100; +TBARHEIGHT: con 20; +titlebarrects(rl: list of Rect): list of Rect +{ + nl: list of Rect; + for (; rl != nil; rl = tl rl) { + r := hd rl; + tr := Rect((r.max.x - TBARWIDTH, r.min.y), + (r.max.x, r.min.y + TBARHEIGHT)); + if (tr.min.x < r.min.x) + tr.min.x = r.min.x; + if (tr.max.y > r.max.y) + tr.max.y = r.max.y; + nl = tr :: nl; + } + return nl; +} + +somespace(scr, lastrect: Rect, minsize: Point): Rect +{ + r := Rect(lastrect.min, lastrect.min.add(minsize)).addpt((20, 20)); + if (r.max.x > scr.max.x || r.max.y > scr.max.y) + r = Rect(scr.min, scr.min.add(minsize)); + return r; +} + +checkrect(r, scr: Rect): Rect +{ + # make sure it's all on screen + if (r.max.x > scr.max.x) { + dx := r.max.x - scr.max.x; + r.max.x -= dx; + r.min.x -= dx; + } + if (r.max.y > scr.max.y) { + dy := r.max.y - scr.max.y; + r.max.y -= dy; + r.min.y -= dy; + } + + # make sure origin is on screen. + off := r.min.sub(scr.min); + if (off.x > 0) + off.x = 0; + if (off.y > 0) + off.y = 0; + r = r.subpt(off); + return r; +} + +# return true if r1 is ``better'' placed than r2, all other things +# being equal. +# currently we choose top-most, left-most, in that order. +better(r1, r2: Rect): int +{ + return r1.min.y < r2.min.y || + (r1.min.y == r2.min.y && r1.min.x < r2.min.x); +} + +clipsize(r: Rect, size: Point): Rect +{ + if (r.dx() > size.x) + r.max.x = r.min.x + size.x; + if (r.dy() > size.y) + r.max.y = r.min.y + size.y; + return r; +} + +find(wins: list of Rect, scr: Rect): list of Rect +{ + + n := len wins + 4; + wr := array[n] of Rect; + for (; wins != nil; wins = tl wins) + wr[--n] = hd wins; + scr2 := scr.inset(-1); + # border sentinels + wr[3] = Rect((scr.min.x,scr2.min.y), (scr.max.x, scr.min.y)); # top + wr[2] = Rect((scr2.min.x, scr2.min.y), (scr.min.x, scr2.max.y)); # left + wr[1] = Rect((scr.min.x, scr.max.y), (scr.max.x, scr2.max.y)); # bottom + wr[0] = Rect((scr.max.x, scr2.min.y), (scr2.max.x, scr2.max.y)); # right + found := sweep(wr, Lay(EW), nil); + return sweep(wr, Lay(NS), found); +} + +sweep(wr: array of Rect, lay: Lay, found: list of Rect): list of Rect +{ + # sweep through in the direction of lay, + # adding and removing end points of rectangles + # as we pass them, and maintaining list of current viable rectangles. + maj := sortcoords(wr, lay); + (cr, ncr) := (array[len wr * 2] of Delta, 0); + rl: list of Rect; # ordered by lay.y(min) + for (i := 0; i < len maj; i++) { + wid := maj[i].wid; + if (maj[i].d > 0) + ncr = addwin(cr, ncr, wid, lay.y(wr[wid].min), lay.y(wr[wid].max)); + else + ncr = removewin(cr, ncr, wid, lay.y(wr[wid].min), lay.y(wr[wid].max)); + nrl: list of Rect = nil; + count := 0; + for (j := 0; j < ncr - 1; j++) { + count += cr[j].d; + (start, end) := (cr[j].coord, cr[j+1].coord); + if (count == 0 && end > start) { + nf: list of Rect; + (rl, nrl, nf) = select(rl, nrl, maj[i].coord, start, end); + for (; nf != nil; nf = tl nf) + found = addfound(found, lay.mkr(hd nf)); + } + } + for (; rl != nil; rl = tl rl) { + r := hd rl; + r.max.x = maj[i].coord; + found = addfound(found, lay.mkr(r)); + } + for (; nrl != nil; nrl = tl nrl) + rl = hd nrl :: rl; + nrl = nil; + } + return found; +} + +addfound(found: list of Rect, r: Rect): list of Rect +{ + if (r.max.x - r.min.x < 1 || + r.max.y - r.min.y < 1) + return found; + return r :: found; +} + +select(rl, nrl: list of Rect, xcoord, start, end: int): (list of Rect, list of Rect, list of Rect) +{ + found: list of Rect; + made := 0; + while (rl != nil) { + r := hd rl; + r.max.x = xcoord; + (rstart, rend) := (r.min.y, r.max.y); + if (rstart >= end) + break; + addit := 1; + if (rstart == start && rend == end) { + made = 1; + } else { + if (!made && rstart > start) { + nrl = ((xcoord, start), (xcoord, end)) :: nrl; + made = 1; + } + if (rend > end || rstart < start) { + found = r :: found; + if (rend > end) + rend = end; + if (rstart < start) + rstart = start; + if (rstart >= rend) + addit = 0; + (r.min.y, r.max.y) = (rstart, rend); + } + } + if (addit) + nrl = r :: nrl; + rl = tl rl; + } + if (!made) + nrl = ((xcoord, start), (xcoord, end)) :: nrl; + return (rl, nrl, found); +} + +removewin(d: array of Delta, nd: int, wid: int, min, max: int): int +{ + minidx := finddelta(d, nd, Delta(+1, wid, min)); + maxidx := finddelta(d, nd, Delta(-1, wid, max)); + if (minidx == -1 || maxidx == -1 || minidx == maxidx) { + sys->fprint(sys->fildes(2), + "bad delta find; minidx: %d; maxidx: %d; wid: %d; min: %d; max: %d\n", + minidx, maxidx, wid, min, max); + raise "panic"; + } + d[minidx:] = d[minidx + 1:maxidx]; + d[maxidx - 1:] = d[maxidx + 1:nd]; + return nd - 2; +} + +addwin(d: array of Delta, nd: int, wid: int, min, max: int): int +{ + (minidx, maxidx) := (findcoord(d, nd, min), findcoord(d, nd, max)); + d[maxidx + 2:] = d[maxidx:nd]; + d[maxidx + 1] = Delta(-1, wid, max); + d[minidx + 1:] = d[minidx:maxidx]; + d[minidx] = Delta(+1, wid, min); + return nd + 2; +} + +finddelta(d: array of Delta, nd: int, df: Delta): int +{ + idx := findcoord(d, nd, df.coord); + for (i := idx; i < nd && d[i].coord == df.coord; i++) + if (d[i].wid == df.wid && d[i].d == df.d) + return i; + for (i = idx - 1; i >= 0 && d[i].coord == df.coord; i--) + if (d[i].wid == df.wid && d[i].d == df.d) + return i; + return -1; +} + +findcoord(d: array of Delta, nd: int, coord: int): int +{ + (lo, hi) := (0, nd - 1); + while (lo <= hi) { + mid := (lo + hi) / 2; + if (coord < d[mid].coord) + hi = mid - 1; + else if (coord > d[mid].coord) + lo = mid + 1; + else + return mid; + } + return lo; +} + +sortcoords(wr: array of Rect, lay: Lay): array of Delta +{ + a := array[len wr * 2] of Delta; + j := 0; + for (i := 0; i < len wr; i++) { + a[j++] = (+1, i, lay.x(wr[i].min)); + a[j++] = (-1, i, lay.x(wr[i].max)); + } + sortdelta(a); + return a; +} + +sortdelta(a: array of Delta) +{ + n := len a; + for(m := n; m > 1; ) { + if(m < 5) + m = 1; + else + m = (5*m-1)/11; + for(i := n-m-1; i >= 0; i--) { + tmp := a[i]; + for(j := i+m; j <= n-1 && tmp.coord > a[j].coord; j += m) + a[j-m] = a[j]; + a[j-m] = tmp; + } + } +} + +Lay.x(l: self Lay, p: Point): int +{ + if (l.d == EW) + return p.x; + return p.y; +} + +Lay.y(l: self Lay, p: Point): int +{ + if (l.d == EW) + return p.y; + return p.x; +} + +Lay.mkr(l: self Lay, r: Rect): Rect +{ + if (l.d == EW) + return r; + return ((r.min.y, r.min.x), (r.max.y, r.max.x)); +} |
