MODULEThe VBT structure of a HeaderBox is as follows:HTMLVBTG EXPORTSHTMLVBTG ,HTMLVBTGRep ; IMPORT Axis, BooleanVBT, BorderedVBT, ResourceBundle, Bundle, ButtonVBT, CIText, Color, ColorName, Cursor, FeedbackVBT, Filter, FlexVBT, Fmt, Font, HighlightVBT, HTML, HTMLVBT, HVSplit, Image, Images, Lex, PaintOp, PackSplit, Pixmap, PixmapVBT, Point, Pts, Rd, ReactivityVBT, Rect, RefList, Rsrc, Shadow, ShadowedVBT, Split, SwitchVBT, Text, TextPort, TextRd, TextVBT, TextureVBT, Thread, TSplit, TypeinVBT, VBT, VBTClass, ViewportVBT, Web, Oblet; <* FATAL Split.NotAChild, ColorName.NotFound *> REVEAL T = Private BRANDED OBJECT OVERRIDES init := Init; END; TYPE ImageVBT = RigidPixmapVBT OBJECT ismap: BOOLEAN END; REVEAL ImageInfo = PublicImageInfo BRANDED OBJECT vbt: ImageVBT; OVERRIDES load := LoadImage; END; PROCEDUREInit ( v : T; html : HTML.T; useAlt : BOOLEAN; useZippers: BOOLEAN; VAR list : RefList.T; scrollBar : BOOLEAN): T = VAR split := HVSplit.New(Axis.T.Ver, adjustable := FALSE); BEGIN EVAL HTMLVBT.T.init(v, html); v.baseURL := html.base; v.useAlt := useAlt; v.useZippers := useZippers; v.toLoad := NIL; v.hsplit := NIL; v.headers := NIL; v.ulDepth := 0; v.verbatim := FALSE; IF html.isIndex THEN DisplayIsIndex(v, split) END; IF html.body # NIL THEN v.page := split; WalkSequence(v, split, DefaultState, html.body); IF v.useZippers THEN NullifyEmptyHeaders(v); ExpandTopHeader(v); END; END; Split.AddChild(split, TextureVBT.New()); IF scrollBar THEN WITH rim = BorderedVBT.New(split, size := Pts.ToMM(PageMarginAmt), op := RegularColors.bg), hilite = HighlightVBT.New(rim), viewport = NEW(ViewportVBT.T).init( ch := hilite, axis := Axis.T.Ver, scrollStyle := ViewportVBT.ScrollStyle.VerOnly, shapeStyle := ViewportVBT.ShapeStyle.Related, shadow := RegularShadow) DO EVAL Filter.Replace(v, viewport); END; ELSE EVAL Filter.Replace(v, split); END; list := RefList.ReverseD(v.toLoad); RETURN v END Init; PROCEDUREExpandTopHeader (v: T) = (* look at all the headers. find the min header level. if there's only one at that level, expand it. if there's only one header, nullify its toggler. *) VAR h: HeaderBox; VAR header := v.headers; min := LAST(INTEGER); ct := 0; total := 0; BEGIN WHILE header # NIL DO IF NOT header.empty THEN INC(total); IF header.level = min THEN INC(ct) ELSIF header.level < min THEN h := header; min := header.level; ct := 1; END END; header := header.next; END; IF ct = 1 THEN ExpandHeader(h); IF total = 1 THEN NullifyToggler(h.toggler) END END; ExpandHeaders(0, v.headers, FALSE) END ExpandTopHeader; PROCEDURENullifyEmptyHeaders (v: T) = (* look at each header. if it has no subheadings and it has no contents (other than glue), remove its toggle. *) PROCEDURE NoSubHeaders (h: HeaderBox): BOOLEAN = BEGIN RETURN h.next = NIL OR h.next.level <= h.level END NoSubHeaders; PROCEDURE NoContents (h: HeaderBox): BOOLEAN = VAR ch := Split.Succ(h.contents, NIL); BEGIN WHILE ch # NIL DO TYPECASE (ch) OF | VGlueVBT => ELSE RETURN FALSE END; ch := Split.Succ(h.contents, ch); END; RETURN TRUE; END NoContents; PROCEDURE DeleteContents (h: HeaderBox) = BEGIN WHILE Split.Succ(h.contents, NIL) # NIL DO WITH ch = Split.Succ(h.contents, NIL) DO Split.Delete(h.contents, ch); VBT.Discard(ch); END END END DeleteContents; VAR header, nextHeader: HeaderBox; BEGIN header := v.headers; WHILE header # NIL DO nextHeader := header.next; IF NoContents(header) AND NoSubHeaders(header) THEN DeleteContents(header); NullifyToggler(header.toggler); header.empty := TRUE; END; header := nextHeader; END; END NullifyEmptyHeaders; PROCEDUREEnterHMode (v: T; parent: VBT.Split) = BEGIN IF v.hsplit = NIL THEN IF v.verbatim THEN v.hsplit := HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE); ELSE v.hsplit := PackSplit.New(op := RegularColors.bg, hgap := 1.0, vgap := 0.0); END; Split.AddChild(parent, v.hsplit); END; END EnterHMode; PROCEDUREExitHMode (v: T) = BEGIN IF v.hsplit # NIL THEN IF Split.NumChildren(v.hsplit) = 0 THEN Split.Delete (VBT.Parent(v.hsplit), v.hsplit) ELSE Split.AddChild(v.hsplit, HFill()) END; v.hsplit := NIL; END; END ExitHMode; PROCEDUREWalkSequence (v: T; vsplit: VBT.T; s: State; seq: HTML.Sequence) = VAR savedState: State; PROCEDURE Push () = BEGIN savedState := s END Push; PROCEDURE Pop () = BEGIN s := savedState END Pop; BEGIN WHILE seq # NIL DO TYPECASE seq OF | HTML.Word (word) => DisplayWord(v, vsplit, s, word); | HTML.Image (image) => DisplayImage(v, vsplit, s, image); | HTML.Oblet (oblet) => Oblet.DisplayOblet(v, vsplit, s, oblet); | HTML.Paragraph => ExitHMode(v); Split.AddChild(vsplit, VGlue(ParSkipAmt)); | HTML.LineBreak => ExitHMode(v); | HTML.HorizontalRule => ExitHMode(v); Split.AddChild(vsplit, VGlue(HRPreSkipAmt)); Split.AddChild(vsplit, HBar(HRAmt)); Split.AddChild(vsplit, VGlue(HRPostSkipAmt)); | HTML.Typewriter (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, FontFamily.Fixed, s.size, s.weight, s.slant); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Boldface (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, s.family, s.size, FontWeight.Bold, s.slant); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Italic (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, s.family, s.size, s.weight, FontSlant.Slanted); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Underline (format) => EnterHMode(v, vsplit); WalkSequence(v, vsplit, s, format.content); | HTML.Emphasis (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, s.family, s.size, s.weight, FontSlant.Slanted); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Strong (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, s.family, s.size, FontWeight.Bold, s.slant); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Code (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, FontFamily.Fixed, s.size, s.weight, s.slant); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Keyboard (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, FontFamily.Fixed, s.size, s.weight, s.slant); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Sample (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, FontFamily.Fixed, s.size, s.weight, s.slant); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Definition (format) => EnterHMode(v, vsplit); Push(); ChangeFont( s, s.family, s.size, FontWeight.Bold, FontSlant.Slanted); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Variable (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, FontFamily.Fixed, s.size, s.weight, s.slant); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Citation (format) => EnterHMode(v, vsplit); Push(); ChangeFont(s, s.family, s.size, s.weight, FontSlant.Slanted); WalkSequence(v, vsplit, s, format.content); Pop(); | HTML.Glossary (glossary) => WalkGlossary(v, vsplit, s, glossary); | HTML.List (list) => WalkList(v, vsplit, s, list); | HTML.Preformatted (pre) => ExitHMode(v); Split.AddChild(vsplit, VGlue(ParSkipAmt)); v.verbatim := TRUE; Push(); ChangeFont(s, FontFamily.Fixed, FontSize.Normal, FontWeight.Normal, FontSlant.Normal); WalkSequence(v, vsplit, s, pre.content); Pop(); v.verbatim := FALSE; ExitHMode(v); Split.AddChild(vsplit, VGlue(ParSkipAmt)); | HTML.Anchor (anchor) => WalkAnchor(v, vsplit, s, anchor); | HTML.Heading (heading) => ExitHMode(v); Push(); vsplit := WalkHeading(v, vsplit, s, heading); Pop(); ExitHMode(v); | HTML.Address (addr) => ExitHMode(v); Push(); ChangeFont( s, s.family, s.size, FontWeight.Normal, FontSlant.Slanted); WalkSequence(v, vsplit, s, addr.content); Pop(); ExitHMode(v); | HTML.BlockQuote (quote) => WalkBlockQuote(v, vsplit, s, quote); | HTML.Table (table) => ExitHMode(v); WalkSequence(v, vsplit, s, table.content); | HTML.TableRow (table) => ExitHMode(v); WalkSequence(v, vsplit, s, table.content); ExitHMode(v); ELSE EnterHMode(v, vsplit); Split.AddChild(v.hsplit, TextVBT.New("????", bgFg := ErrorColors)) END; seq := seq.next; END; END WalkSequence; PROCEDUREWalkGlossary (v: T; vsplit: VBT.T; s: State; glossary: HTML.Glossary) = VAR gs := glossary.content; BEGIN ExitHMode(v); Split.AddChild(vsplit, VGlue(ParSkipAmt)); IF glossary.preContent # NIL THEN ExitHMode(v); WalkSequence(v, vsplit, s, glossary.preContent); END; WHILE gs # NIL DO IF gs.term # NIL THEN ExitHMode(v); WalkSequence(v, vsplit, s, gs.term); END; IF gs.definition # NIL THEN ExitHMode(v); WITH hbox = HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE) DO Split.AddChild(vsplit, hbox); Split.AddChild(hbox, HGlue(2.0*IndentAmt)); WITH vbox = HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE) DO Split.AddChild(hbox, vbox); WalkSequence(v, vbox, s, gs.definition) END END END; gs := gs.next; END; ExitHMode(v); END WalkGlossary; VAR Solid := GetPixmap("filledbullet.pbm", ResourceBundle.Get()); Hollow := GetPixmap("hollowbullet.pbm", ResourceBundle.Get()); PROCEDUREWalkList (v: T; vsplit: VBT.T; s: State; list: HTML.List) = VAR item := list.content; listhbox := HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE); listvbox := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE); tick, tickbox, hbox, vbox: VBT.T; bullet: Pixmap.T; chCount: INTEGER; BEGIN IF list.preContent # NIL THEN ExitHMode(v); WalkSequence(v, vsplit, s, list.preContent); END; ExitHMode(v); IF list.kind = HTML.ListKind.Ordered THEN chCount := 1; ELSE chCount := 0; IF v.ulDepth = 0 THEN bullet := Solid ELSE bullet := Hollow END; INC(v.ulDepth); END; Split.AddChild(vsplit, listhbox); Split.AddChild(listhbox, HGlue(IndentAmt)); Split.AddChild(listhbox, listvbox); WHILE item # NIL DO hbox := HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE); Split.AddChild(listvbox, hbox); IF chCount = 0 THEN tick := NEW(RigidPixmapVBT).init(pm:=bullet, op:=s.bgFg.bgFg, bg:=s.bgFg.bg); ELSE tick := NEW(RigidTextVBT).init(Fmt.Pad(Fmt.Int(chCount), 2) & ". ", bgFg:=s.bgFg, fnt:=s.font); INC(chCount); END; tickbox := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE); Split.AddChild(tickbox, NEW(FlexVBT.T).init(tick, FlexVBT.Fixed)); Split.AddChild(tickbox, VFill()); Split.AddChild(hbox, tickbox); vbox := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE); Split.AddChild(hbox, vbox); EnterHMode(v, vbox); (* add either number or bullet *) WalkSequence(v, vbox, s, item.content); ExitHMode(v); item := item.next; END; IF list.kind # HTML.ListKind.Ordered THEN DEC(v.ulDepth); END; END WalkList; PROCEDUREWalkHeading (v: T; vsplit: VBT.T; s: State; heading: HTML.Heading): VBT.T = BEGIN IF v.useZippers THEN RETURN WalkZipperedHeading(v, s, heading) ELSE WITH h = headingInfo[heading.level] DO Split.AddChild(vsplit, VGlue(h.preGlue)); ChangeFont(s, FontFamily.Normal, h.fontSize, FontWeight.Bold, FontSlant.Normal); WalkSequence(v, vsplit, s, heading.content); Split.AddChild(vsplit, VGlue(h.postGlue)); END; RETURN vsplit END END WalkHeading;
(HeaderBoxTSplit empty1 (VBox1 preglue (HBox toggler (VBox2 heading (HideawayTSplit empty2 (ContentsVBox postglue ..stuff..))))))
HideawayTSplit - Like a TSplit, except when the current child is NIL, the shape is empty rather than a VBT's default shape.
TYPE HideawayTSplit = TSplit.T OBJECT
METHODS
set(show: BOOLEAN) := HTSet;
OVERRIDES
shape := HTShape;
END;
PROCEDURE HTSet (v: HideawayTSplit; show: BOOLEAN) =
BEGIN
IF show THEN
TSplit.SetCurrent(v, Split.Succ(v, NIL))
ELSE
TSplit.SetCurrent(v, NIL)
END
END HTSet;
PROCEDURE HTShape (v: HideawayTSplit; ax: Axis.T; n: CARDINAL): VBT.SizeRange
RAISES {} =
BEGIN
IF TSplit.GetCurrent(v) = NIL THEN
RETURN VBT.SizeRange{lo := 0, pref := 0, hi := 1};
ELSE
RETURN TSplit.T.shape(v, ax, n)
END
END HTShape;
REVEAL
HeaderBox = HideawayTSplit BRANDED OBJECT
empty: BOOLEAN; (* contents empty and no subheaders *)
level: INTEGER;
toExpand: BOOLEAN;
toggler : Toggler;
contents: HVSplit.T;
next: HeaderBox;
END;
PROCEDURE WalkZipperedHeading (v: T; s: State; heading: HTML.Heading): VBT.T =
VAR
header := NEW(HeaderBox);
h := headingInfo[heading.level];
tsplit := NEW(HideawayTSplit).init();
feedback := NEW(TogglerFeedback).init();
switch := NEW(SwitchVBT.T).init(feedback);
toggler := NEW(Toggler, header := header, tsplit := tsplit).init(
switch);
vbox1 := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE);
vbox2 := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE);
vsplit := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE);
hbox := HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE);
BEGIN
VAR l := v.headers; BEGIN
IF l = NIL THEN v.headers := header;
ELSE
WHILE l.next # NIL DO l := l.next END;
l.next := header;
END
END;
BooleanVBT.Put(toggler, TRUE);
FeedbackVBT.Normal(feedback);
header.empty := FALSE;
header.level := heading.level;
header.toExpand := FALSE;
header.toggler := toggler;
header.contents := vsplit;
header.next := NIL;
EVAL header.init();
Split.AddChild(header, vbox1);
Split.AddChild(vbox1, VGlue(h.preGlue));
Split.AddChild(vbox1, hbox);
Split.AddChild(hbox, toggler);
Split.AddChild(hbox, vbox2);
ChangeFont(
s, FontFamily.Normal, h.fontSize, FontWeight.Bold, FontSlant.Normal);
WalkSequence(v, vbox2, s, heading.content);
Split.AddChild(vbox2, tsplit);
Split.AddChild(tsplit, vsplit);
Split.AddChild(vsplit, VGlue(h.postGlue));
Split.AddChild(v.page, header);
RETURN vsplit;
END WalkZipperedHeading;
VAR
ExpandIcon := GetPixmap("expandArrow.pbm", ResourceBundle.Get());
ExpandOnIcon := GetPixmap("expandOnArrow.pbm", ResourceBundle.Get());
ContractIcon := GetPixmap("contractArrow.pbm", ResourceBundle.Get());
ContractOnIcon := GetPixmap("contractOnArrow.pbm", ResourceBundle.Get());
ToggleIcons := ARRAY BOOLEAN OF Pixmap.T {ContractIcon, ExpandIcon};
ToggleOnIcons := ARRAY BOOLEAN OF Pixmap.T {ContractOnIcon, ExpandOnIcon};
TYPE
TogglerFeedback = FeedbackVBT.T BRANDED OBJECT
pm: PixmapVBT.T;
METHODS
init (): TogglerFeedback := TogglerFeedbackInit;
OVERRIDES
normal := TogglerFeedbackNormal;
excited := TogglerFeedbackExcited;
END;
PROCEDURE TogglerFeedbackInit (f: TogglerFeedback): TogglerFeedback =
BEGIN
f.pm :=
NEW(RigidPixmapVBT).init(
pm := Pixmap.Solid, op := RegularColors.bgFg, bg := RegularColors.bg,
valign := 0.0, vmargin := 0.0, hmargin := 2.0);
RETURN (FeedbackVBT.T).init(f, f.pm);
END TogglerFeedbackInit;
PROCEDURE TogglerFeedbackNormal (f: TogglerFeedback) =
BEGIN
PixmapVBT.Put(f.pm, ToggleIcons[FeedbackVBT.GetState(f)])
END TogglerFeedbackNormal;
PROCEDURE TogglerFeedbackExcited (f: TogglerFeedback) =
BEGIN
PixmapVBT.Put(f.pm, ToggleOnIcons[FeedbackVBT.GetState(f)])
END TogglerFeedbackExcited;
PROCEDURE NullifyToggler (toggle: Toggler) =
BEGIN
VAR r := NEW(ReactivityVBT.T).init(ch := NIL, colors := RegularShadow); BEGIN
Split.Replace(VBT.Parent(toggle), toggle, r);
EVAL Filter.Replace(r, toggle);
ReactivityVBT.Set(r, ReactivityVBT.State.Vanish, Cursor.DontCare);
END
END NullifyToggler;
TYPE
Toggler = BooleanVBT.T BRANDED OBJECT
header: HeaderBox;
tsplit: HideawayTSplit;
OVERRIDES
callback := TreeToggle;
END;
PROCEDURE TreeToggle (toggle: Toggler; READONLY cd: VBT.MouseRec) =
(* open/contract the immediate logical
children *)
VAR
h := toggle.header;
all := (VBT.Modifier.Shift IN cd.modifiers)
OR (VBT.Modifier.Control IN cd.modifiers);
BEGIN
IF BooleanVBT.Get(toggle) THEN
CollapseHeader(h);
CollapseHeaders(h.level, h.next, all);
ELSE
ExpandHeader(h);
ExpandHeaders(h.level, h.next, all);
END;
END TreeToggle;
PROCEDURE CollapseHeader (h: HeaderBox) =
BEGIN
h.toExpand := FALSE;
h.toggler.tsplit.set(FALSE);
BooleanVBT.Put(h.toggler, TRUE);
FeedbackVBT.Normal(Filter.Child(Filter.Child(h.toggler)));
END CollapseHeader;
PROCEDURE CollapseHeaders (level : INTEGER;
start : HeaderBox;
collapseAll: BOOLEAN ) =
VAR header := start;
BEGIN
WHILE header # NIL DO
IF header.level <= level THEN RETURN END;
header.set(FALSE);
IF collapseAll THEN CollapseHeader(header) END;
header := header.next
END
END CollapseHeaders;
PROCEDURE ExpandHeader (h: HeaderBox) =
BEGIN
h.toExpand := TRUE;
h.toggler.tsplit.set(TRUE);
BooleanVBT.Put(h.toggler, FALSE);
FeedbackVBT.Normal(Filter.Child(Filter.Child(h.toggler)));
END ExpandHeader;
PROCEDURE ExpandHeaders (level : INTEGER;
start : HeaderBox;
expandAll: BOOLEAN ) =
VAR header := start;
BEGIN
WHILE header # NIL DO
IF header.level <= level THEN RETURN END;
header.set(TRUE);
IF expandAll THEN ExpandHeader(header) END;
IF header.toExpand THEN
header := header.next
ELSE (* skip subheaders *)
VAR level := header.level; BEGIN
header := header.next;
WHILE header # NIL AND header.level > level DO
header := header.next
END
END
END
END;
END ExpandHeaders;
PROCEDURE WalkAnchor (v: T; vsplit: VBT.T; s: State; anchor: HTML.Anchor) =
VAR href := anchor.href;
BEGIN
EnterHMode(v, vsplit);
IF href = NIL THEN
(* Probably a NAME anchor -- ignore it. *)
WalkSequence(v, vsplit, s, anchor.content);
RETURN;
END;
VAR pos := Text.FindChar(href, '#', 0);
BEGIN
IF pos = 0 THEN
(* '#' is first char: defines a destination link in current page; ignore it *)
WalkSequence(v, vsplit, s, anchor.content);
RETURN
END;
IF pos > 0 THEN
(* links to a place on href; kill off what's after the '#'. *)
href := Text.Sub(href, 0, pos);
END;
VAR
button: ButtonVBT.T;
vbox := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE);
hbox := v.hsplit;
BEGIN
button :=
NEW(Anchor, v := v, href := href).init(vbox, AnchorAction);
Split.AddChild(v.hsplit, button);
v.hsplit := HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE);
Split.AddChild(vbox, v.hsplit);
ChangeColors(s, AnchorColors);
WalkSequence(v, vbox, s, anchor.content);
IF v.hsplit # NIL THEN v.hsplit := hbox END;
END;
END
END WalkAnchor;
TYPE Anchor = ButtonVBT.T OBJECT
v: T;
href: TEXT
END;
PROCEDURE AnchorAction (self: ButtonVBT.T; READONLY cd: VBT.MouseRec) =
VAR anchor := NARROW(self, Anchor); where: Point.T;
BEGIN
IF IsIsMap(anchor, cd.cp.pt, where) THEN
anchor.v.ismap(anchor.href, where, cd)
ELSE
anchor.v.hotlink(anchor.href, cd)
END
END AnchorAction;
PROCEDURE IsIsMap (anchor: Anchor; READONLY pt: Point.T; VAR where: Point.T):
BOOLEAN =
VAR v := Split.Locate(anchor, pt);
BEGIN
WHILE v # NIL DO
TYPECASE v OF
| ImageVBT (im) =>
IF im.ismap THEN
where := Rect.GlobToLoc(VBT.Domain(im), pt);
RETURN TRUE;
ELSE
RETURN FALSE;
END;
| Split.T => v := Split.Locate(v, pt);
ELSE
RETURN FALSE
END;
END;
RETURN FALSE
END IsIsMap;
PROCEDURE WalkBlockQuote (v: T; vsplit: VBT.T; s: State; quote: HTML.BlockQuote) =
VAR
hbox := HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE);
vbox := HVSplit.New(hv := Axis.T.Ver, adjustable := FALSE);
BEGIN
ExitHMode(v);
Split.AddChild(vsplit, VGlue(ParSkipAmt));
Split.AddChild(vsplit, hbox);
Split.AddChild(hbox, HGlue(IndentAmt));
Split.AddChild(hbox, vbox);
WalkSequence(v, vbox, s, quote.content);
ExitHMode(v);
Split.AddChild(vsplit, VGlue(ParSkipAmt));
END WalkBlockQuote;
PROCEDURE DisplayIsIndex (v: T; vsplit: VBT.T) =
VAR hbox := HVSplit.New(hv := Axis.T.Hor, adjustable := FALSE);
BEGIN
ExitHMode(v);
Split.AddChild(vsplit, HBar(HRAmt));
Split.AddChild(vsplit, VGlue(HRPostSkipAmt));
Split.AddChild(vsplit, hbox);
WITH prompt = NEW(RigidTextVBT).init(
txt := "This is a searchable index. Enter search keywords: ",
hmargin := 0.0, halign := 0.5,
vmargin := 0.0, valign := 0.0,
fnt := DefaultState.font, bgFg := DefaultState.bgFg) DO
Split.AddChild(hbox, prompt)
END;
WITH typein = NEW(IsIndexTypeinVBT, v:=v).init(
expandOnDemand := TRUE, font := IsIndexFont,
colorScheme := IsIndexShadow),
shadow = NEW(ShadowedVBT.T).init(typein, IsIndexShadow, Shadow.Style.Lowered) DO
Split.AddChild(hbox, shadow)
END;
Split.AddChild(vsplit, VGlue(HRPreSkipAmt));
Split.AddChild(vsplit, HBar(HRAmt));
Split.AddChild(vsplit, VGlue(HRPostSkipAmt));
END DisplayIsIndex;
TYPE
IsIndexTypeinVBT = TypeinVBT.T OBJECT
v: T;
OVERRIDES
returnAction := IsIndexAction;
END;
PROCEDURE IsIndexAction ( vbt: IsIndexTypeinVBT;
<*UNUSED*> READONLY cd : VBT.KeyRec) =
<* FATAL Thread.Alerted *>
(* Not sure if fataling Thread.Alerted is appropriate -- najork 8/27/96 *)
BEGIN
vbt.v.isindex(Web.EncodeURL(TextPort.GetText(vbt)))
END IsIndexAction;
PROCEDURE DisplayWord (v: T; vsplit: VBT.T; s: State; word: HTML.Word) =
PROCEDURE AddText (t: TEXT) =
VAR
vbt := NEW(RigidTextVBT).init(
txt := t, hmargin := 0.0, halign := 0.5, vmargin := 0.0,
valign := 0.0, fnt := s.font, bgFg := s.bgFg);
BEGIN
Split.AddChild(v.hsplit, vbt)
END AddText;
CONST
AllChars = SET OF CHAR{FIRST(CHAR).. LAST(CHAR)};
NonBlanks = AllChars - Lex.Blanks;
NL = '\n';
NonNL = AllChars - SET OF CHAR{NL};
<* FATAL Rd.EndOfFile, Rd.Failure, Thread.Alerted *>
(* Not sure if fataling everything is appropriate -- najork 8/27/96 *)
BEGIN
EnterHMode(v, vsplit);
IF v.verbatim THEN
WITH rd = TextRd.New(word.word) DO
IF Rd.GetChar(rd) = NL THEN
ExitHMode(v);
EnterHMode(v, vsplit)
ELSE
Rd.UnGetChar(rd)
END;
WHILE NOT Rd.EOF(rd) DO
AddText(Lex.Scan(rd, NonNL));
IF NOT Rd.EOF(rd) THEN
EVAL Rd.GetChar(rd);
ExitHMode(v);
EnterHMode(v, vsplit)
END
END
END
ELSE
TYPECASE v.hsplit OF
| HVSplit.T => AddText(word.word);
| PackSplit.T =>
WITH rd = TextRd.New(word.word) DO
Lex.Skip(rd);
WHILE NOT Rd.EOF(rd) DO
AddText(Lex.Scan(rd, NonBlanks));
IF NOT Rd.EOF(rd) THEN Lex.Skip(rd) END
END
END
ELSE <*ASSERT FALSE*>
END
END
END DisplayWord;
PROCEDURE DisplayImage (v: T; vsplit: VBT.T; s: State; image: HTML.Image) =
BEGIN
EnterHMode(v, vsplit);
IF v.useAlt OR image.source = NIL THEN
VAR alt := image.alternate;
BEGIN
IF alt = NIL THEN alt := "[image]" END;
WITH vbt = NEW(RigidTextVBT).init(
txt := alt, hmargin := 0.0, halign := 0.5,
vmargin := 0.0, valign := 0.0, fnt := s.font,
bgFg := s.bgFg) DO
Split.AddChild(v.hsplit, vbt)
END
END
ELSE
VAR
vbt := NEW(ImageVBT, ismap := image.ismap).init(
pm := EmptyImage,
op := (* PaintOp.Copy *) RegularColors.bgFg,
bg := RegularColors.bg);
border := BorderedVBT.New(
vbt, size := Pts.ToMM(0.5), op := s.bgFg.fg);
pm : Pixmap.T;
url: TEXT;
BEGIN
IF s.bgFg # AnchorColors THEN BorderedVBT.SetSize(border, 0.0) END;
Split.AddChild(v.hsplit, border);
url := Web.AbsoluteURL(image.source, v.baseURL);
IF Images.FromCache(url, pm) THEN
PixmapVBT.Put(vbt, pm)
ELSE
v.toLoad :=
RefList.Cons(NEW(ImageInfo, url := url, align := image.align,
vbt := vbt), v.toLoad)
END
END
END
END DisplayImage;
PROCEDURE LoadImage (info: ImageInfo; page: Web.Page)
RAISES {Thread.Alerted} =
VAR pm: Pixmap.T;
BEGIN
TRY
IF page.header.contentType # Web.MIMEType.Image THEN
RAISE Images.Error
END;
IF CIText.Equal(page.header.contentSubType, xxgif) THEN
WITH rd = TextRd.New(page.contents),
pic = GifPic.New(rd),
vbt = PicVBT.New(pic)
DO
Split.Replace(VBT.Parent(info.vbt), info.vbt, vbt)
END
ELSE
IF CIText.Equal(page.header.contentSubType, jpeg) THEN
pm := Images.FromJPEG(page.contents);
ELSIF CIText.Equal(page.header.contentSubType, gif) THEN
pm := Images.FromGIF(page.contents);
ELSIF CIText.Equal(page.header.contentSubType, x-xbitmap) THEN
pm := Images.FromXBM(page.contents);
ELSIF CIText.Equal(page.header.contentSubType, ppm) OR
CIText.Equal(page.header.contentSubType, pnm) OR
CIText.Equal(page.header.contentSubType, pbm) OR
CIText.Equal(page.header.contentSubType, pgm) THEN
WITH rd = TextRd.New(page.contents) DO
pm := Image.Unscaled(Image.FromRd(rd));
END;
ELSE
RAISE Images.Error
END;
Images.ToCache(info.url, pm);
LOCK VBT.mu DO PixmapVBT.Put(info.vbt, pm) END
END
EXCEPT GifPic.Error, Images.Error, Rd.Failure =>
LOCK VBT.mu DO
PixmapVBT.Put(info.vbt, ErrorImage);
PixmapVBT.SetColors(
info.vbt, op := ErrorColors.bgFg, bg := ErrorColors.bg)
END
END;
END LoadImage;
PROCEDURELoadImage (info: ImageInfo; page: Web.Page) RAISES {Thread.Alerted} = VAR pm: Pixmap.T; BEGIN TRY IF page.header.contentType # Web.MIMEType.Image THEN RAISE Images.Error END; IF CIText.Equal(page.header.contentSubType, "jpeg") THEN pm := Images.FromJPEG(page.contents); ELSIF CIText.Equal(page.header.contentSubType, "gif") THEN pm := Images.FromGIF(page.contents); ELSIF CIText.Equal(page.header.contentSubType, "x-xbitmap") THEN pm := Images.FromXBM(page.contents); ELSIF CIText.Equal(page.header.contentSubType, "ppm") OR CIText.Equal(page.header.contentSubType, "pnm") OR CIText.Equal(page.header.contentSubType, "pbm") OR CIText.Equal(page.header.contentSubType, "pgm") THEN WITH rd = TextRd.New(page.contents) DO pm := Image.Unscaled(Image.FromRd(rd)); END; ELSE RAISE Images.Error END; Images.ToCache(info.url, pm); LOCK VBT.mu DO PixmapVBT.Put(info.vbt, pm) END EXCEPT Image.Error, Images.Error, Rd.Failure => LOCK VBT.mu DO PixmapVBT.Put(info.vbt, ErrorImage); PixmapVBT.SetColors( info.vbt, op := ErrorColors.bgFg, bg := ErrorColors.bg) END END; END LoadImage; PROCEDUREVFill (): VBT.T = VAR txt := TextureVBT.New(op := RegularColors.bg); BEGIN RETURN FlexVBT.FromAxis(txt, Axis.T.Ver, FlexVBT.StretchyRange) END VFill; TYPE VGlueVBT = FlexVBT.T BRANDED OBJECT END; PROCEDUREVGlue (amt: REAL): VBT.T = VAR txt := TextureVBT.New(op := RegularColors.bg); BEGIN RETURN NEW(VGlueVBT).init( txt, FlexVBT.Shape{FlexVBT.DefaultRange, FlexVBT.RigidRange(Pts.ToMM(amt))}) END VGlue; PROCEDUREHFill (): VBT.T = VAR txt := TextureVBT.New(op := RegularColors.bg); BEGIN RETURN FlexVBT.FromAxis(txt, Axis.T.Hor, FlexVBT.StretchyRange) END HFill; PROCEDUREHGlue (amt: REAL): VBT.T = VAR txt := TextureVBT.New(op := RegularColors.bg); BEGIN RETURN FlexVBT.FromAxis(txt, Axis.T.Hor, FlexVBT.RigidRange(Pts.ToMM(amt))) END HGlue; PROCEDUREHBar (amt: REAL): VBT.T = VAR txt := TextureVBT.New(op := RegularColors.fg); BEGIN RETURN FlexVBT.FromAxis(txt, Axis.T.Ver, FlexVBT.RigidRange(Pts.ToMM(amt))) END HBar; REVEAL RigidPixmapVBT = PixmapVBT.T BRANDED OBJECT OVERRIDES shape := RigidPixmapVBTShape; END; PROCEDURERigidPixmapVBTShape (v: RigidPixmapVBT; ax: Axis.T; n: CARDINAL): VBT.SizeRange RAISES {} = VAR sr := PixmapVBT.T.shape(v, ax, n); BEGIN sr.hi := sr.pref + 1; RETURN sr END RigidPixmapVBTShape; REVEAL RigidTextVBT = TextVBT.T BRANDED OBJECT OVERRIDES shape := RigidTextVBTShape; END; PROCEDURERigidTextVBTShape (v: RigidTextVBT; ax: Axis.T; n: CARDINAL): VBT.SizeRange RAISES {} = VAR sr := TextVBT.T.shape(v, ax, n); BEGIN sr.hi := sr.pref + 1; RETURN sr END RigidTextVBTShape; PROCEDURELookupShadow (bgName, fgName: TEXT): Shadow.T = VAR rgb: Color.T; bg, fg, light, dark: PaintOp.T; BEGIN rgb := ColorNameToRGB(bgName); bg := PaintOp.FromRGB(rgb.r, rgb.g, rgb.b, mode:=PaintOp.Mode.Accurate, bw:=PaintOp.BW.UseBg); rgb := ColorNameToRGB(fgName); fg := PaintOp.FromRGB(rgb.r, rgb.g, rgb.b, mode:=PaintOp.Mode.Accurate, bw:=PaintOp.BW.UseFg); rgb := ColorNameToRGB("Light" & bgName); light := PaintOp.FromRGB(rgb.r, rgb.g, rgb.b, mode:=PaintOp.Mode.Accurate, bw:=PaintOp.BW.UseFg); rgb := ColorNameToRGB("Dark" & bgName); dark := PaintOp.FromRGB(rgb.r, rgb.g, rgb.b, mode:=PaintOp.Mode.Accurate, bw:=PaintOp.BW.UseFg); RETURN Shadow.New(ShadowAmt, bg, fg, light, dark) END LookupShadow; PROCEDURELookupColors (bgName, fgName: TEXT): PaintOp.ColorQuad = VAR bg := ColorNameToRGB(bgName); bgOp := PaintOp.FromRGB(bg.r, bg.g, bg.b, mode:=PaintOp.Mode.Accurate, bw:=PaintOp.BW.UseBg); fg := ColorNameToRGB(fgName); fgOp := PaintOp.FromRGB(fg.r, fg.g, fg.b, mode:=PaintOp.Mode.Accurate, bw:=PaintOp.BW.UseFg); quad := PaintOp.MakeColorQuad(bgOp, fgOp);
TYPE ColorQuint = PaintOp.ColorQuad OBJECT bgBg: PaintOp.T; END;
BEGIN
RETURN NEW(ColorQuint, bg := quad.bg, fg := quad.fg, bgFg := quad.bgFg, transparentFg := quad.transparentFg, bgBg := PaintOp.Pair(bgOp, bgOp))
RETURN quad;
END LookupColors;
PROCEDURE ColorNameToRGB (name: TEXT): Color.T =
(* gross hack here to ensure that the background color matches that of
FormsVBT. *)
BEGIN
IF Text.Equal(name, BackgroundColor) THEN
RETURN Color.T{0.8, 0.8, 0.8}
ELSE
RETURN ColorName.ToRGB(name)
END
END ColorNameToRGB;
PROCEDURE LookupFont (family: FontFamily;
size : FontSize;
weight: FontWeight;
slant : FontSlant ): Font.T =
VAR
base, suffix, name: TEXT;
style : FontStyle;
BEGIN
IF weight = FontWeight.Normal THEN
IF slant = FontSlant.Normal THEN
style := FontStyle.Plain
ELSE
style := FontStyle.Slanted
END
ELSE
IF slant = FontSlant.Normal THEN
style := FontStyle.Bold
ELSE
style := FontStyle.BoldSlanted
END
END;
IF family = FontFamily.Normal THEN
base := NormalFontNames[style];
suffix := NormalFontSizes[size];
ELSE
base := FixedFontNames[style];
suffix := FixedFontSizes[size];
END;
name := base & suffix;
RETURN Font.FromName(ARRAY OF TEXT{name})
END LookupFont;
VAR DefaultState: State;
PROCEDURE ChangeColors (VAR s: State; bgFg: PaintOp.ColorQuad) =
BEGIN
s.bgFg := bgFg;
END ChangeColors;
PROCEDURE ChangeFont (VAR s : State;
family: FontFamily;
size : FontSize;
weight: FontWeight;
slant : FontSlant ) =
BEGIN
s.family := family;
s.size := size;
s.weight := weight;
s.slant := slant;
s.font := LookupFont(family, size, weight, slant);
END ChangeFont;
PROCEDURE GetPixmap (name: TEXT; bundle: Bundle.T): Pixmap.T =
<* FATAL Image.Error, Rsrc.NotFound, Rd.Failure, Thread.Alerted *>
VAR
rd := Rsrc.Open(name, Rsrc.BuildPath(bundle));
pm := Image.Scaled(Image.FromRd(rd));
BEGIN
Rd.Close(rd);
RETURN pm
END GetPixmap;
********* initialization *********
VAR
RegularShadow : Shadow.T;
IsIndexShadow: Shadow.T;
IsIndexFont : Font.T;
BEGIN
EmptyImage := GetPixmap("emptyimage.pbm", ResourceBundle.Get());
ErrorImage := GetPixmap("errorimage.pbm", ResourceBundle.Get());
RegularShadow := LookupShadow(BackgroundColor, RegularColor);
RegularColors := LookupColors(BackgroundColor, RegularColor);
RegularBgColors := LookupColors(BackgroundColor, BackgroundColor);
AnchorColors := LookupColors(BackgroundColor, AnchorColor);
ErrorColors := LookupColors(BackgroundColor, ErrorColor);
IsIndexShadow := LookupShadow(IsIndexBgColor, RegularColor);
IsIndexFont :=
LookupFont(family := FontFamily.Fixed, size := FontSize.Normal,
weight := FontWeight.Normal, slant := FontSlant.Normal);
DefaultState.bgFg := RegularColors;
ChangeFont(
DefaultState, family := FontFamily.Normal, size := FontSize.Normal,
weight := FontWeight.Normal, slant := FontSlant.Normal);
END HTMLVBTG.