<> <> DIRECTORY Buttons, HierarchicalDisplays, MJSContainers, UserProfile, ViewerClasses, ViewerOps; HierarchicalDisplaysImpl: CEDAR MONITOR IMPORTS Buttons, MJSContainers, UserProfile, VO:ViewerOps EXPORTS HierarchicalDisplays = BEGIN OPEN HierarchicalDisplays; ParentList: TYPE = LIST OF Parent; Root: TYPE = REF RootRep; RootRep: TYPE = RECORD [ parent: Parent, cw: INTEGER]; nvSep: INTEGER = 2; cSep: INTEGER = 5; vSep: INTEGER = 2; indent: INTEGER _ 10; nvDiffLines: BOOLEAN _ TRUE; SampleHierarchicalDisplaysProfile: UserProfile.ProfileChangedProc --PROC [reason: ProfileChangeReason]-- = BEGIN indent _ UserProfile.Number[key: "HierarchicalDisplays.indent", default: 10]; nvDiffLines _ UserProfile.Boolean[key: "HierarchicalDisplays.nvDiffLines", default: TRUE]; END; debug: BOOLEAN _ FALSE; roots: ParentList _ NIL; CreateRoot: PUBLIC PROC [viewerInit: ViewerRec, paint: BOOLEAN _ TRUE] RETURNS [p: Parent] = BEGIN r: Root; IF viewerInit.parent # NIL THEN viewerInit.wh _ MAX[viewerInit.wh, minH]; p _ NEW [ParentRep _ [container: NIL]]; viewerInit.data _ r _ NEW [RootRep _ [p, 0]]; [] _ MJSContainers.Create[viewerFlavor: $HierarchicalDisplayRoot, info: viewerInit, paint: paint]; roots _ CONS[p, roots]; END; InitRoot: ViewerClasses.InitProc--PROC [self: Viewer]-- = BEGIN r: Root _ NARROW[MJSContainers.GetClientData[self]]; r.parent.container _ self; r.cw _ self.cw; END; HandleSizeChange: PROC [self: Viewer] RETURNS [adjusted: BOOL _ FALSE] --ViewerClasses.AdjustProc-- = BEGIN root: Root _ NARROW[MJSContainers.GetClientData[self]]; News: ENTRY PROC RETURNS [news: BOOLEAN] = {IF news _ root.parent.container.cw # root.cw THEN root.cw _ root.parent.container.cw}; IF adjusted _ (root # NIL AND News[]) THEN Pack[parent: root.parent, paint: FALSE]; END; Pack: PUBLIC PROC [parent: Parent, paint: BOOLEAN _ TRUE] = BEGIN parent.rx _ parent.ty _ parent.by _ 0; FOR c: Child _ parent.firstChild, c.next WHILE c # NIL DO WITH c SELECT FROM in: InternalNode => BEGIN FlushRight[parent.container, in.container]; FlushRight[in.container, in.asParent.container]; Pack[in.asParent, FALSE]; in.container.wh _ MAX[in.nameButton.wy + in.nameButton.wh, in.asParent.container.wy + in.asParent.container.wh] + in.container.wh - in.container.ch; END; x: Leaf => SetLeafSize[x]; ENDCASE => ERROR; Place[c, FALSE]; ENDLOOP; IF parent.container.parent # NIL THEN VO.MoveViewer[viewer: parent.container, x: parent.container.wx, y: parent.container.wy, w: parent.container.ww, h: parent.by + parent.container.wh - parent.container.ch, paint: paint] ELSE IF paint THEN VO.PaintViewer[viewer: parent.container, hint: client]; END; FlushRight: PROC [parent, child: Viewer] = BEGIN temp: INTEGER _ MAX[parent.cw - child.wx, 5]; IF temp # child.ww THEN VO.MoveViewer[viewer: child, x: child.wx, y: child.wy, w: temp, h: child.wh, paint: FALSE]; END; Place: PROC [child: Child, paint: BOOLEAN] = BEGIN parent: Parent _ child.parent; IF child.forceNewline OR parent.rx + child.container.ww + (IF parent.rx > 0 THEN cSep ELSE 0) > parent.container.cw AND parent.rx > 0 THEN BEGIN parent.rx _ 0; parent.ty _ parent.by + vSep; parent.by _ parent.ty; END ELSE IF parent.rx > 0 THEN parent.rx _ parent.rx + cSep; VO.MoveViewer[viewer: child.container, x: parent.rx, y: parent.ty, w: child.container.ww, h: child.container.wh, paint: paint]; parent.by _ MAX[parent.by, child.container.wy + child.container.wh]; parent.rx _ child.container.wx + child.container.ww; END; AddLeaf: PUBLIC PROC [parent: Parent, before: Child, name: ROPE, class: ChildClass, classData, instanceData: REF ANY _ NIL, CreateValue: ValueCreater] RETURNS [leaf: Leaf] = BEGIN paint: BOOLEAN = FALSE; leaf _ NEW [ChildRep[TypeLeaf] _ [ container: MJSContainers.Create[ viewerFlavor: $VanillaMJSContainer, info: [parent: parent.container, wh: minH, ww: parent.container.cw, scrollable: debug, border: debug], paint: FALSE], parent: parent, class: class, classData: classData, instanceData: instanceData, variant: TypeLeaf[value: NIL] ]]; leaf.nameButton _ Buttons.Create[info: [parent: leaf.container, name: name, border: debug], proc: class.buttonProc, clientData: leaf, paint: FALSE]; IF before # NIL THEN {leaf.next _ before; leaf.prev _ before.prev; IF before.parent # parent THEN ERROR} ELSE IF parent.lastChild # NIL THEN {leaf.prev _ parent.lastChild; leaf.next _ NIL} ELSE leaf.next _ leaf.prev _ NIL; IF leaf.next = NIL THEN parent.lastChild _ leaf ELSE leaf.next.prev _ leaf; IF leaf.prev = NIL THEN parent.firstChild _ leaf ELSE leaf.prev.next _ leaf; leaf.value _ CreateValue[leaf]; SetLeafSize[leaf]; IF leaf.next = NIL THEN Place[leaf, paint] ELSE Pack[parent, paint]; END; SetLeafSize: PROC [leaf: Leaf] = { VO.MoveViewer[viewer: leaf.container, x: leaf.container.wx, y: leaf.container.wy, w: MAX[ leaf.nameButton.wx + leaf.nameButton.ww, leaf.value.wx + leaf.value.ww] + leaf.container.ww - leaf.container.cw, h: MAX[ leaf.nameButton.wy + leaf.nameButton.wh, leaf.value.wy + leaf.value.wh] + leaf.container.wh - leaf.container.ch, paint: FALSE]; }; AddInternalNode: PUBLIC PROC [parent: Parent, before: Child, name: ROPE, class: ChildClass, classData, instanceData: REF ANY _ NIL] RETURNS [internal: InternalNode] = BEGIN paint: BOOLEAN = FALSE; lx, ty: INTEGER; internal _ NEW [ChildRep[TypeParent] _ [ container: MJSContainers.Create[viewerFlavor: $VanillaMJSContainer, info: [parent: parent.container, wh: minH, ww: parent.container.cw, scrollable: debug, border: debug], paint: FALSE], parent: parent, class: class, classData: classData, instanceData: instanceData, variant: TypeParent[NIL] ]]; internal.nameButton _ Buttons.Create[info: [parent: internal.container, name: name, border: debug], proc: class.buttonProc, clientData: internal, paint: FALSE]; IF before # NIL THEN {internal.next _ before; internal.prev _ before.prev; IF before.parent # parent THEN ERROR} ELSE IF parent.lastChild # NIL THEN {internal.prev _ parent.lastChild; internal.next _ NIL} ELSE internal.next _ internal.prev _ NIL; IF internal.next = NIL THEN parent.lastChild _ internal ELSE internal.next.prev _ internal; IF internal.prev = NIL THEN parent.firstChild _ internal ELSE internal.prev.next _ internal; IF nvDiffLines THEN BEGIN lx _ indent; ty _ internal.nameButton.wy + internal.nameButton.wh + vSep; END ELSE BEGIN lx _ internal.nameButton.wx + internal.nameButton.ww + nvSep; ty _ 0; END; internal.asParent _ NEW [ParentRep _ [ container: MJSContainers.Create[ viewerFlavor: $VanillaMJSContainer, info: [parent: internal.container, scrollable: debug, wx: lx, wy: ty, ww: MAX[20, internal.container.cw - lx], wh: minH], paint: FALSE], asChild: internal ]]; VO.MoveViewer[ viewer: internal.container, x: internal.container.wx, y: internal.container.wy, w: internal.container.ww, h: MAX[ internal.nameButton.wy + internal.nameButton.wh, internal.asParent.container.wy + internal.asParent.container.wh] + internal.container.wh - internal.container.ch, paint: FALSE]; internal.forceNewline _ TRUE; IF internal.next = NIL THEN Place[internal, paint] ELSE Pack[parent, paint]; END; Remove: PUBLIC PROC [child: Child] = BEGIN paint: BOOLEAN = FALSE; IF child.next = NIL THEN child.parent.lastChild _ child.prev ELSE child.next.prev _ child.prev; IF child.prev = NIL THEN child.parent.firstChild _ child.next ELSE child.prev.next _ child.next; child.class.NotifyOnRemove[child]; WITH child SELECT FROM in: InternalNode => RemoveChildren[in.asParent]; leaf: Leaf => NULL; ENDCASE => ERROR; VO.DestroyViewer[viewer: child.container, paint: paint]; END; RemoveChildren: PROC [parent: Parent] = {FOR c: Child _ parent.firstChild, c.next WHILE c # NIL DO Remove[c] ENDLOOP}; EnumerateChildren: PUBLIC PROC [to: ChildNotifyProc, from: Parent _ NIL, changedOnly: BOOLEAN _ FALSE, leaves, internals: BOOLEAN _ TRUE] = BEGIN Survey: PROC [p: Parent] = BEGIN FOR c: Child _ p.firstChild, c.next WHILE c # NIL DO WITH c SELECT FROM in: InternalNode => BEGIN IF internals THEN to[c]; Survey[in.asParent]; END; leaf: Leaf => IF leaves AND (IF changedOnly THEN leaf.value.newVersion ELSE TRUE) THEN to[leaf]; ENDCASE => ERROR; ENDLOOP; END; IF from # NIL THEN Survey[from] ELSE FOR ps: ParentList _ roots, ps.rest WHILE ps # NIL DO Survey[ps.first]; ENDLOOP; END; Setup: PROC = BEGIN UserProfile.CallWhenProfileChanges[SampleHierarchicalDisplaysProfile]; MJSContainers.RegisterClass[viewerFlavor: $HierarchicalDisplayRoot, class: NEW [MJSContainers.MJSContainerClassRep _ [init: InitRoot, adjust: HandleSizeChange]]]; END; Setup[]; END.