ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/charon/paginate.b/
implement Paginate;
include "common.m";
include "print.m";
include "paginate.m";
sys: Sys;
print: Print;
L: Layout;
D: Draw;
Frame, Lay, Line, Control: import Layout;
Docinfo: import Build;
Image, Display, Rect, Point: import D;
Item: import Build;
MaskedImage: import CharonUtils;
disp: ref Display;
p0: Point;
nulimg: ref Image;
DPI: con 110;
init(layout: Layout, draw: Draw, display: ref Draw->Display): string
{
sys = load Sys Sys->PATH;
L = layout;
D = draw;
disp = display;
if (L == nil || D == nil || disp == nil)
return "bad args";
print = load Print Print->PATH;
if (print == nil)
return sys->sprint("cannot load module %s: %r", Print->PATH);
print->init();
#nullfd := sys->open("/dev/null", Sys->OWRITE);
#print->set_printfd(nullfd);
p0 = Point(0, 0);
nulimg = disp.newimage(((0, 0), (1, 1)), 3, 0, 0);
return nil;
}
paginate(frame: ref Layout->Frame, orient: int, pnums, cancel: chan of int, result: chan of (string, ref Pageset))
{
pidc := chan of int;
spawn watchdog(pidc, cancel, nil, sys->pctl(0, nil));
watchpid := <- pidc;
if (frame.kids != nil) {
result <-= ("cannot print frameset", nil);
kill(watchpid);
return;
}
defp := print->get_defprinter();
if (defp == nil) {
result <-= ("no default printer", nil);
kill(watchpid);
return;
}
# assuming printer's X & Y resolution are the same
if (orient == PORTRAIT)
defp.popt.orientation = Print->PORTRAIT;
else
defp.popt.orientation = Print->LANDSCAPE;
(dpi, pagew, pageh) := print->get_size(defp);
pagew = (DPI * pagew)/dpi;
pageh = (DPI * pageh)/dpi;
pfr := copyframe(frame);
pr := Rect(p0, (pagew, pageh));
pfr.r = pr;
pfr.cr = pr;
pfr.viewr = pr;
l := pfr.layout;
L->relayout(pfr, l, pagew, l.just);
maxy := l.height + l.margin; # don't include bottom margin
prctxt := ref Layout->Printcontext;
pfr.prctxt = prctxt;
pfr.cim = nulimg;
pnum := 1;
startys : list of int;
for (y := 0; y < maxy;) {
startys = y :: startys;
pnums <-= pnum++;
endy := y + pageh;
prctxt.endy = pageh;
pfr.viewr.min.y = y;
pfr.viewr.max.y = endy;
L->drawall(pfr);
y += prctxt.endy;
}
# startys are in reverse order
ys : list of int;
for (; startys != nil; startys = tl startys)
ys = hd startys :: ys;
pageset := ref Pageset(defp, pfr, ys);
result <-= (nil, pageset);
kill(watchpid);
}
printpageset(pset: ref Pageset, pnums, cancel: chan of int)
{
pidc := chan of int;
stopdog := chan of int;
spawn watchdog(pidc, cancel, stopdog, sys->pctl(0, nil));
watchpid := <- pidc;
frame := pset.frame;
pageh := frame.cr.dy();
white := disp.rgb2cmap(255, 255, 255);
prctxt := frame.prctxt;
l := frame.layout;
maxy := l.height + l.margin; # don't include bottom margin
maxy = max(maxy, pageh);
pnum := 1;
for (pages := pset.pages; pages != nil; pages = tl pages) {
y := hd pages;
if (y + pageh > maxy)
pageh = maxy - y;
frame.cr.max.y = pageh;
frame.cim = disp.newimage(frame.cr, 3, 0, white);
if (frame.cim == nil) {
pnums <-= -1;
kill(watchpid);
return;
}
pnums <-= pnum++;
endy := y + pageh;
prctxt.endy = pageh;
frame.viewr.min.y = y;
frame.viewr.max.y = endy;
L->drawall(frame);
stopdog <-= 1;
#start := sys->millisec();
if (print->print_image(pset.printer, disp, frame.cim, 100, cancel) == -1) {
# cancelled
kill(watchpid);
return;
}
stopdog <-= 1;
#sys->print("PAGE %d: %dms\n", pnum -1, sys->millisec()-start);
}
pnums <-= -1;
kill(watchpid);
}
watchdog(pidc, cancel, pause: chan of int, pid: int)
{
pidc <-= sys->pctl(0, nil);
if (pause == nil)
pause = chan of int;
for (;;) alt {
<- cancel =>
kill(pid);
return;
<- pause =>
<- pause;
}
}
kill(pid: int)
{
sys->fprint(sys->open("/prog/" + string pid +"/ctl", Sys->OWRITE), "kill");
}
killgrp(pid: int)
{
sys->fprint(sys->open("/prog/" + string pid +"/ctl", Sys->OWRITE), "killgrp");
}
max(a, b: int): int
{
if (a > b)
return a;
return b;
}
copyframe(f: ref Frame): ref Frame
{
zr := Draw->Rect(p0, p0);
newf := ref Frame(
-1, # id
nil, # doc
nil, # src
" PRINT FRAME ", # name
f.marginw, # marginw
f.marginh, # marginh
0, # framebd
Build->FRnoscroll, # flags
nil, # layout - filled in below, needs this frame ref
nil, # sublays - filled in by geometry code
0, # sublayid
nil, # controls - filled in below, needs this frame ref
0, # controlid - filled in below
nil, # cim
zr, # r
zr, # cr
zr, # totalr
zr, # viewr
nil, # vscr
nil, # hscr
nil, # parent
nil, # kids
0, # animpid
nil # prctxt
);
newf.doc = copydoc(f, newf, f.doc);
controls := array [len f.controls] of ref Control;
for (i := 0; i < len controls; i++)
controls[i] = copycontrol(f, newf, f.controls[i]);
newf.layout = copylay(f, newf, f.layout);
newf.controls = controls;
newf.controlid = len controls;
return newf;
}
copysublay(oldf, f: ref Frame, oldid:int): int
{
if (oldid < 0)
return -1;
if (f.sublayid >= len f.sublays)
f.sublays = (array [len f.sublays + 30] of ref Lay)[:] = f.sublays;
id := f.sublayid++;
lay := copylay(oldf, f, oldf.sublays[oldid]);
f.sublays[id] = lay;
return id;
}
copydoc(oldf, f : ref Frame, doc: ref Build->Docinfo): ref Docinfo
{
background := copybackground(oldf, f, doc.background);
newdoc := ref Build->Docinfo(
nil, #src
nil, #base
nil, #referrer
nil, #doctitle
background,
nil, #backgrounditem
doc.text, doc.link, doc.vlink, doc.alink,
nil, #target
nil, #refresh
nil, #chset
nil, #lastModified
0, #scripttype
0, #hasscripts
nil, #events
0, #evmask
nil, #kidinfo
0, #frameid
nil, #anchors
nil, #dests
nil, #forms
nil, #tables
nil, #maps
nil #images
);
return newdoc;
}
copylay(oldf, f: ref Frame, l: ref Lay): ref Lay
{
start := copyline(oldf, f, nil, l.start);
end := start;
for (line := l.start.next; line != nil; line = line.next)
end = copyline(oldf, f, end, line);
newl := ref Lay(
start,
end,
l.targetwidth, # targetwidth
l.width, # width
l.height, # height
l.margin, # margin
nil, # floats - filled in by geometry code
copybackground(oldf, f, l.background),
l.just,
Layout->Lchanged
);
start.flags = end.flags = byte 0;
return newl;
}
copycontrol(oldf, f: ref Frame, ctl: ref Control): ref Control
{
if (ctl == nil)
return nil;
pick c := ctl {
Cbutton =>
return ref Control.Cbutton(f, nil, c.r, c.flags, nil, c.pic, c.picmask, c.dpic, c.dpicmask, c.label, c.dorelief);
Centry =>
scr := copycontrol(oldf, f, c.scr);
return ref Control.Centry(f, nil, c.r, c.flags, nil, scr, c.s, c.sel, c.left, c.linewrap, 0);
Ccheckbox=>
return ref Control.Ccheckbox(f, nil, c.r, c.flags, nil, c.isradio);
Cselect =>
scr := copycontrol(oldf, f, c.scr);
options := (array [len c.options] of Build->Option)[:] = c.options;
return ref Control.Cselect(f, nil, c.r, c.flags, nil, nil, scr, c.nvis, c.first, options);
Clistbox =>
hscr := copycontrol(oldf, f, c.hscr);
vscr := copycontrol(oldf, f, c.vscr);
options := (array [len c.options] of Build->Option)[:] = c.options;
return ref Control.Clistbox(f, nil, c.r, c.flags, nil, hscr, vscr, c.nvis, c.first, c.start, c.maxcol, options, nil);
Cscrollbar =>
# do not copy ctl as this is set by those associated controls
return ref Control.Cscrollbar(f, nil, c.r, c.flags, nil, c.top, c.bot, c.mindelta, c.deltaval, nil, c.holdstate);
Canimimage =>
bg := copybackground(oldf, f, c.bg);
return ref Control.Canimimage(f, nil, c.r, c.flags, nil, c.cim, 0, 0, big 0, bg);
Clabel =>
return ref Control.Clabel(f, nil, c.r, c.flags, nil, c.s);
* =>
return nil;
}
}
copyline(oldf, f: ref Frame, prev, l: ref Line): ref Line
{
if (l == nil)
return nil;
cp := ref *l;
items := copyitems(oldf, f, l.items);
newl := ref Line (items, nil, prev, l.pos, l.width, l.height, l.ascent, Layout->Lchanged);
if (prev != nil)
prev.next = newl;
return newl;
}
copyitems(oldf, f: ref Frame, items: ref Item): ref Item
{
if (items == nil)
return nil;
item := copyitem(oldf, f, items);
end := item;
for (items = items.next; items != nil; items = items.next) {
end.next = copyitem(oldf, f, items);
end = end.next;
}
return item;
}
copyitem(oldf, f : ref Frame, item: ref Item): ref Item
{
if (item == nil)
return nil;
pick it := item {
Itext =>
return ref Item.Itext(
nil, it.width, it.height, it.ascent, 0, it.state, nil,
it.s, it.fnt, it.fg, it.voff, it.ul);
Irule =>
return ref Item.Irule(
nil, it.width, it.height, it.ascent, 0, it.state, nil,
it.align, it.noshade, it.size, it.wspec);
Iimage =>
# need to copy the image to prevent
# ongoing image fetches from messing up our layout
ci := copycimage(it.ci);
return ref Item.Iimage(
nil, it.width, it.height, it.ascent, 0, it.state, nil,
it.imageid, ci, it.imwidth, it.imheight, it.altrep,
nil, it.name, -1, it.align, it.hspace, it.vspace, it.border);
Iformfield =>
return ref Item.Iformfield(
nil, it.width, it.height, it.ascent, 0, it.state, nil,
copyformfield(oldf, f, it.formfield)
);
Itable =>
return ref Item.Itable(
nil, it.width, it.height, it.ascent, 0, it.state, nil,
copytable(oldf, f, it.table));
Ifloat =>
items := copyitem(oldf, f, it.item);
return ref Item.Ifloat(
nil, it.width, it.height, it.ascent, 0, it.state, nil,
items, it.x, it.y, it.side, byte 0);
Ispacer =>
return ref Item.Ispacer(
nil, it.width, it.height, it.ascent, 0, it.state, nil,
it.spkind, it.fnt);
* =>
return nil;
}
}
copycimage(ci: ref CharonUtils->CImage): ref CharonUtils->CImage
{
if (ci == nil)
return nil;
mims : array of ref MaskedImage;
if (len ci.mims > 0)
# if len> 1 then animated, but we only want first frame
mims = array [1] of {0 => ci.mims[0]};
return ref CharonUtils->CImage(nil, nil, nil, 0, ci.width, ci.height, nil, mims, 0);
}
copyformfield(oldf, f: ref Frame, ff: ref Build->Formfield): ref Build->Formfield
{
image := copyitem(oldf, f, ff.image);
# should be safe to reference Option list
newff := ref Build->Formfield(
ff.ftype, 0, nil, ff.name, ff.value, ff.size, ff.maxlength, ff.rows,
ff.cols, ff.flags, ff.options, image, ff.ctlid, nil, 0
);
return newff;
}
copytable(oldf, f: ref Frame, tbl: ref Build->Table): ref Build->Table
{
nrow := tbl.nrow;
ncol := tbl.ncol;
caption_lay := copysublay(oldf, f, tbl.caption_lay);
cols := (array [ncol] of Build->Tablecol)[:] = tbl.cols;
rows := array [nrow] of ref Build->Tablerow;
for (i := 0; i < nrow; i++) {
r := tbl.rows[i];
rows[i] = ref Build->Tablerow(nil, r.height, r.ascent, r.align, r.background, r.pos, r.flags);
}
cells : list of ref Build->Tablecell;
grid := array [nrow] of {* => array [ncol] of ref Build->Tablecell};
for (rix := 0; rix < nrow; rix++) {
rowcells: list of ref Build->Tablecell = nil;
for (colix := 0; colix < ncol; colix++) {
cell := copytablecell(oldf, f, tbl.grid[rix][colix]);
if (cell == nil)
continue;
grid[rix][colix] = cell;
cells = cell :: cells;
rowcells = cell :: rowcells;
}
# reverse the row cells;
rcells : list of ref Build->Tablecell = nil;
for (; rowcells != nil; rowcells = tl rowcells)
rcells = hd rowcells :: rcells;
rows[rix].cells = rcells;
}
# reverse the cells
sllec: list of ref Build->Tablecell;
for (; cells != nil; cells = tl cells)
sllec = hd cells :: sllec;
cells = sllec;
return ref Build->Table(
tbl.tableid, # tableid
nrow, # nrow
ncol, # ncol
len cells, # ncell
tbl.align, # align
tbl.width, # width
tbl.border, # border
tbl.cellspacing, # cellspacing
tbl.cellpadding, # cellpadding
tbl.background, # background
nil, # caption
tbl.caption_place, # caption_place
caption_lay, # caption_lay
nil, # currows
cols, # cols
rows, # rows
cells, # cells
tbl.totw, # totw
tbl.toth, # toth
tbl.caph, # caph
tbl.availw, # availw
grid, # grid
nil, # tabletok
Layout->Lchanged # flags
);
}
copytablecell(oldf, f: ref Frame, cell: ref Build->Tablecell): ref Build->Tablecell
{
if (cell == nil)
return nil;
layid := copysublay(oldf, f, cell.layid);
background := copybackground(oldf, f, cell.background);
newcell := ref Build->Tablecell(
cell.cellid,
nil, # content
layid,
cell.rowspan, cell.colspan, cell.align,
cell.flags, cell.wspec, cell.hspec,
background, cell.minw, cell.maxw,
cell.ascent, cell.row, cell.col, cell.pos);
return newcell;
}
copybackground(oldf, f: ref Frame, bg: Build->Background): Build->Background
{
img := copyitem(oldf, f, bg.image);
if (img != nil) {
pick i := img {
Iimage =>
bg.image = i;
}
}
return bg;
}