<<-- TiogaPaintImpl.mesa; Written by S. McGregor, August 1983>> <<-- Edited by McGregor on August 30, 1983 3:54 pm>> DIRECTORY Graphics, NodeStyle, Real, TiogaDisplayTable, TiogaDocument, TiogaLocks, TiogaNode, TiogaNodeOps, TiogaPathOps, TiogaPaint, ViewerClasses; TiogaPaintImpl: CEDAR MONITOR IMPORTS Graphics, NodeStyle, Real, TiogaDisplayTable, TiogaNodeOps, TiogaPathOps EXPORTS TiogaPaint = BEGIN OPEN TiogaDocument, Graphics; TiogaPaint: PUBLIC ViewerClasses.PaintProc = BEGIN tdd: TiogaDocumentData _ NARROW[self.data]; dt: TiogaDisplayTable.DisplayTable = tdd.displayTable; lock: TiogaLocks.LockRef; moveDownLines, moveDownDistance: INTEGER _ 0; -- used with scrolling down Cleanup: PROC = { --TiogaLocks.UnlockDocAndTdd[tdd]; RefreshOver[]-- }; IF tdd = NIL THEN RETURN; BEGIN ENABLE UNWIND => Cleanup[]; interrupt: BOOL = clear OR whatChanged=NIL; IF self.destroyed THEN RETURN; -- the viewer changed while this was forked tdd.invisible _ FALSE; -- or else we wouldn't have been called IF clear OR whatChanged=NIL THEN BEGIN <> FOR n: INTEGER IN [0..tdd.displayTable.lastIndexUsed] DO -- invalidate all lines tdd.displayTable.objects[n].valid _ FALSE; ENDLOOP; END ELSE SELECT whatChanged FROM ENDCASE => ERROR; RefreshViewer[self, tdd, context, clear, ~(clear OR whatChanged=NIL), lock, moveDownLines, moveDownDistance]; Cleanup[]; END; END; -- of unwind and PaintProc RefreshViewer: PROCEDURE [viewer: ViewerClasses.Viewer, tdd: TiogaDocumentData, dc: Graphics.Context, displayClear, refresh: BOOL, lock: TiogaLocks.LockRef, moveDownLines, moveDownDistance: INTEGER] = BEGIN n: INTEGER _ 0; start, end: INTEGER; dt: TiogaDisplayTable.DisplayTable _ tdd.displayTable; UNTIL n > dt.lastIndexUsed DO -- search for an invalid rectangle IF dt.objects[n].valid THEN { n _ n+1; LOOP }; start _ n; UNTIL n+1 > dt.lastIndexUsed DO IF dt.objects[n+1].valid THEN EXIT; n _ n+1; ENDLOOP; end _ n; IF start > 0 AND dt.objects[start].startPos.path.node # dt.objects[start-1].startPos.path.node THEN { <<-- need to reinitialize the start pos; may have deleted start node>> dt.objects[start].startPos.where _ 0; IF (dt.objects[start].startPos.path.node _ TiogaPathOps.ForwardClipped[dt.objects[start-1].startPos.path,tdd.clipLevel].nx.node) = NIL THEN start _ start-1 --have deleted end of document-- }; n _ WhileInPosRange[viewer, tdd, dc, start, end, displayClear, refresh, lock, moveDownLines, moveDownDistance]; moveDownLines _ moveDownDistance _ 0; -- only do this the first time < 0 THEN {>> <> ENDLOOP; tdd.movedIn _ tdd.movedOut _ tdd.dirty _ FALSE; END; <> WhileInPosRange: ENTRY PROCEDURE [viewer: ViewerClasses.Viewer,tdd: TiogaDocumentData, dc: Graphics.Context, start, end: INTEGER,displayClear, refresh: BOOL, lock: TiogaLocks.LockRef, moveDownLines, moveDownDistance: INTEGER] RETURNS [line: INTEGER] = BEGIN OPEN Graphics; ENABLE UNWIND => NULL; dt: TiogaDisplayTable.DisplayTable = tdd.displayTable; bottomLeading, leftIndent, bodyIndent, firstIndent, lineMeasure, leading, topLeading, nodeLeading: INTEGER _ 0; lineFormatting: NodeStyle.LineFormatting; knowBottomLeading: BOOL _ start=0; -- otherwise need to get it yBaseline, oldBottomY: INTEGER _ 0; styleInit, yMatchRun: BOOL _ TRUE; yMatch, pasteLine, eraseToEnd: BOOL _ FALSE; nodeSize: INT; <<>> styleInfoNode: TiogaNode.Ref; -- node for which we were last called to get style info <> pos: TiogaNode.Location _ dt.objects[start].startPos; <<>> GetStyleInfo: PROC [node: TiogaNode.Ref] = BEGIN OPEN NodeStyle; IF node=styleInfoNode THEN RETURN; -- already have the style info styleInfoNode _ node; IF ~knowBottomLeading AND pos.where=0 THEN { -- get it from previous node ApplyAll[nodeStyle, dt.objects[line-1].startPos.path]; bottomLeading _ GetBottomLeadingI[nodeStyle] }; ApplyAll[nodeStyle, dt.objects[line].startPos.path]; lineFormatting _ GetLineFormatting[nodeStyle]; leftIndent _ GetLeftIndentI[nodeStyle]; bodyIndent _ GetBodyIndentI[nodeStyle]; firstIndent _ GetFirstIndentI[nodeStyle]; lineMeasure _ viewer.cw - GetRightIndentI[nodeStyle] - leftIndent; leading _ GetLeadingI[nodeStyle]; topLeading _ MAX[bottomLeading, GetTopLeadingI[nodeStyle]]; <> nodeLeading _ MAX[bottomLeading, topLeading]; -- use previous value of bottomLeading bottomLeading _ GetBottomLeadingI[nodeStyle]; knowBottomLeading _ TRUE; END; <<>> FullyVisible: PROC RETURNS [BOOL] = { box: Graphics.Box; width, height: INTEGER; checkedVisible _ TRUE; IF ~Graphics.IsRectangular[dc] THEN RETURN [FALSE]; box _ Graphics.GetBounds[dc]; width _ Real.RoundI[box.xmax-box.xmin]; IF width # viewer.cw THEN RETURN [FALSE]; height _ Real.RoundI[box.ymax-box.ymin]; IF height # viewer.ch THEN RETURN [FALSE]; RETURN [TRUE] }; <<>> checkedVisible, fullyVisible: BOOL _ FALSE; <<>> ClearEntries: PROC [from, to: INTEGER] = { FOR n: INTEGER IN [from..to] DO dt.objects[n] _ [ startPos: TiogaNode.nullLocation, valid: TRUE, yBaseline: 0, xBaseline: 0, topExtent: 0, bottomExtent: 0, rightExtent: 0, leftExtent: 0, end: eon]; ENDLOOP }; ClearLine: PROC [oldBottomY, newBottomY: INTEGER] = BEGIN IF oldBottomY >= newBottomY THEN RETURN; SetColor[dc, white]; DrawBox[dc, [0, viewer.ch-newBottomY, viewer.cw, viewer.ch-oldBottomY]]; SetColor[dc, black]; END; level: INTEGER _ 0; -- in case we are doing level clipping maxLevel: INTEGER = tdd.clipLevel; levelClipping: BOOL = maxLevel < maxClip; <<>> NextNode: PROC = { -- computes next node in tree order IF levelClipping THEN [pos.path, level] _ TiogaPathOps.ForwardClipped[pos.path, maxLevel, level] -- this isn't quite right ELSE pos.path _ TiogaPathOps.StepForwardNode[pos.path]; pos.where _ 0; }; LeafNode: PROC RETURNS [size: INT] = { -- forces pos to a leaf node and computes node size <> DO IF pos.path.node=NIL THEN {size _ 0; EXIT}; WITH pos.path.node SELECT FROM br: TiogaNode.RefBranchNode => NextNode[]; -- expand this to cover empty branches tx: TiogaNode.RefTextNode => {size _ TiogaNodeOps.FetchItemClass[tx.class].length[tx]; EXIT}; bx: TiogaNode.RefBoxNode => {size _ TiogaNodeOps.FetchItemClass[bx.class].length[bx]; EXIT}; ls: TiogaNode.RefListNode => {size _ TiogaNodeOps.FetchItemClass[ls.class].length[ls]; EXIT}; bc: TiogaNode.RefBasicNode => ERROR; -- should see any of these here ENDCASE => ERROR; ENDLOOP; }; yBaseline _ IF line=0 THEN NodeStyle.GetTopIndentI[nodeStyle] ELSE dt.objects[line-1].yBaseline; IF (line _ start) > dt.lastIndexUsed OR end < 0 THEN RETURN; -- update not on screen oldBottomY _ IF line=0 THEN 0 ELSE dt.objects[line-1].yBaseline+dt.objects[line-1].bottomExtent; nodeSize _ LeafNode[]; -- force pos to be a displayable node <> <<>> UNTIL line>=dt.objects.maxRectangle OR (line>end AND dt.objects[line].valid AND dt.objects[line].startPos=pos AND yMatch AND line<=dt.lastIndexUsed) DO < 0 THEN {>> <> <> <> <> <> IF pos.where >= nodeSize THEN BEGIN <> NextNode[]; nodeSize _ LeafNode[]; IF pos.path.node=NIL THEN EXIT; GetStyleInfo[pos.path.node]; IF line=start THEN yBaseline _ IF line=0 THEN 0 ELSE dt.objects[line-1].yBaseline; -- initialize yBaseline IF line=0 THEN yBaseline _ yBaseline + NodeStyle.GetTopIndentI[nodeStyle]; IF yBaseline>=viewer.ch AND line>0 THEN {pos.path.node _ NIL; EXIT}; <> IF pos.path.node#NIL THEN {styleInit _ TRUE; LOOP} -- reapply termination test ELSE {eraseToEnd _ TRUE; EXIT}; -- off end of text END ELSE IF styleInit THEN {GetStyleInfo[pos.path.node]; styleInit _ FALSE}; -- first time for this node yBaseline _ yBaseline + (IF pos.where=0 THEN nodeLeading ELSE leading) -- leading --; IF yBaseline>=viewer.ch AND line>0 THEN {eraseToEnd _ TRUE; EXIT}; <> yMatch _ yBaseline=dt.objects[line].yBaseline; <> IF ~yMatch THEN yMatchRun _ FALSE; IF ~displayClear THEN BEGIN oldBottomY: INTEGER _ IF line=0 THEN 0 ELSE dt.objects[line-1].yBaseline+dt.objects[line-1].bottomExtent; -- bottom of previous line thisBottomY: INTEGER _ 9999; -- wrong! how should this work??? ClearLine[oldBottomY, thisBottomY]; END; BEGIN -- call class display routine (make this a proc to convert to the Imager -SM) item: TiogaNode.RefItemNode _ TiogaNodeOps.NarrowToItemNode[pos.path.node]; end: TiogaNode.Offset; leftExtent, rightExtent, topExtent, bottomExtent: INTEGER _ 0; break: TiogaDisplayTable.Break; mark: Graphics.Mark _ Graphics.Save[dc]; Graphics.SetColor[dc, Graphics.black]; Graphics.Translate[dc, leftIndent, viewer.ch-yBaseline]; -- client assumes 0,0 as baseline [end, leftExtent, rightExtent, topExtent, bottomExtent, break] _ TiogaNodeOps.FetchItemClass[item.class].format[ item, pos.where, leftIndent, lineMeasure, yBaseline-oldBottomY, viewer.ch-yBaseline, display, dc]; pos.where _ end+1; TiogaDisplayTable.StorePageRectangle[dt, line, [ startPos: pos, endPos: [pos.path, end], valid: TRUE, end: break, yBaseline: yBaseline, xBaseline: leftIndent, topExtent: topExtent, bottomExtent: bottomExtent, leftExtent: leftExtent, rightExtent: rightExtent ]]; Graphics.Restore[dc, mark]; oldBottomY _ yBaseline+bottomExtent; END; line _ line + 1; ENDLOOP; <> <<>> IF eraseToEnd OR line>=dt.lastIndexUsed THEN BEGIN <> bottomOfNewLines: INTEGER _ IF line=0 THEN 0 ELSE dt.objects[line-1].yBaseline+dt.objects[line-1].bottomExtent; dt.lastIndexUsed _ MAX[line,1]-1; -- set end mark ClearEntries[dt.lastIndexUsed+1, dt.objects.maxRectangle-1]; IF ~displayClear AND bottomOfNewLines < dt.lastY THEN -- white out old lines ClearLine[bottomOfNewLines, dt.lastY]; dt.lastY _ bottomOfNewLines; END ELSE BEGIN -- take this branch if got back in synch during repaint IF yBaseline>viewer.ch OR displayClear THEN ERROR; dt.lastIndexUsed _ MAX[dt.lastIndexUsed, MAX[line,1]-1]; dt.lastY _ MAX[dt.lastY, yBaseline+dt.objects[dt.lastIndexUsed].bottomExtent]; END; END; nodeStyle: NodeStyle.Ref _ NodeStyle.Create[]; END.