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
IF clear THEN AdjustSelStates[self];
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
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
This is used to avoid redoing GetStyleInfo after have peeked at next node style as part of trying to move lines down.
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]];
use value of bottomLeading left from previous node
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
This is too simple as written. It only works right for the case where each branch node contains a displayable leaf. In the case of empty branch nodes, this proc should leave blank space of the appropriate leading, and make an entry into the displayTable. Until this is modified, empty branches will be invisible.
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
paint lines until the line we would paint is valid, past the range we were told to paint, matches the line table offsets (i.e. no ripple going forward), and matches in the Y coord. An exit for end of text and off end of viewer are included below.
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
IF (refresh AND TiogaLocks.WaitingForWrite[lock]) OR tdd.interrupt > 0 THEN {
Either an edit is pending or a complete repaint is pending. In either case, we quit.
dt.lastIndexUsed ← MAX[line, dt.lastIndexUsed];
dt.objects[line].startPos ← pos;
dt.objects[line].valid ← FALSE;
RETURN};
IF pos.where >= nodeSize
THEN
BEGIN
nothing more to display in this node. pos is where this line should start, so we are done with the node.
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 baseline would be off bottom of viewer and not the first line, don't show it.
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};
if baseline would be off bottom of viewer and not the first line, don't show it.
yMatch ← yBaseline=dt.objects[line].yBaseline;
true if the new line is baseline aligned with the previous bits on the screen
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;
clear former bottom lines if we run off end of text or viewer (partial line must be clipped)
IF eraseToEnd
OR line>=dt.lastIndexUsed
THEN
BEGIN
eraseToEnd starts false. set true if reach end of document or stop with line that would have baseline below bottom of viewer.
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;