<<-- TEditDisplayImpl.mesa, last edit by Paxton on May 24, 1983 9:38 am>> <> <> DIRECTORY Carets USING [pCaretViewer, sCaretViewer, StopCaret], ColorDisplay USING [height], EditSpan USING [afterMoved1, afterMoved2], Graphics USING [black, Box, Context, DrawBox, GetBounds, IsRectangular, SetCP, SetColor, white], GraphicsOps USING [DrawBitmap, MoveDeviceRectangle], NameSymbolTable USING [RopeFromName], NodeStyle USING [ApplyAll, Copy, Create, FontFace, GetBodyIndentI, GetBottomLeadingI, GetFirstIndentI, GetFontFace, GetFontFamily, GetFontSizeI, GetLeadingI, GetLeftIndentI, GetLineFormatting, GetRightIndentI, GetTabStopsI, GetTopIndentI, GetTopLeadingI, Ref], Process USING [Detach], Real USING [RoundI], RopeReader USING [Ref, GetRopeReader, FreeRopeReader, SetPosition, Backwards, Get, GetIndex], TextEdit USING [Offset, RefTextNode, Size], TextNode USING [FirstChild, ForwardClipped, Level, Location, NarrowToTextNode, pZone, Ref, Root, StepForward], TextLooks USING [allLooks], TEditCompile USING [maxLineAscent, minAvgLineLeading], TEditDisplay, TEditDocument USING [LineBreak, LineTable, LineTableRec, maxClip, Selection, SelectionRec, SpinAndLock, SelectionId, TEditDocumentData, Unlock], TEditFormat USING [Bitmap, GetLineBitmap, FormatInfo, FormatInfoRec], TEditImpl, TEditLocks USING [LockDocAndTdd, LockRef, UnlockDocAndTdd, WaitingForWrite], TEditScrolling USING [AutoScroll], TEditSelection USING [AdjustSelStates, FixUpAfterDisplay, InsertionPoint, InvalidateLineCache, pSel, sSel, fSel, SelectionRoot, ShowSelection, TakeDownForRedisplay, TakeSelectionDown], TEditTouchup, VFonts USING [EstablishFont, Font], ViewerClasses USING [PaintProc, PaintRectangle, Viewer], ViewerOps USING [UserToScreenCoords], ViewerSpecs USING [screenH]; TEditDisplayImpl: CEDAR MONITOR IMPORTS Carets, ColorDisplay, EditSpan, Graphics, GraphicsOps, NameSymbolTable, NodeStyle, Process, Real, TextEdit, TextNode, RopeReader, TEditDocument, TEditFormat, TEditLocks, TEditScrolling, TEditSelection, TEditTouchup, VFonts, ViewerOps EXPORTS TEditImpl SHARES VFonts = BEGIN OPEN TEditSelection, TEditDocument, TEditTouchup; PaintTEditDocument: PUBLIC ViewerClasses.PaintProc = BEGIN tdd: TEditDocumentData = NARROW[self.data]; lock: TEditLocks.LockRef; moveDownLines, moveDownDistance: INTEGER _ 0; -- used with scrolling down redisplay, refreshing: BOOL _ FALSE; Cleanup: PROC = { TEditLocks.UnlockDocAndTdd[tdd]; RefreshOver[] }; IF tdd = NIL THEN RETURN; BEGIN ENABLE UNWIND => Cleanup[]; interrupt: BOOL = clear OR whatChanged=fullUpdate OR whatChanged=NIL; typescript: BOOL; IF self.destroyed THEN RETURN; -- the viewer changed while this was forked tdd.invisible _ FALSE; -- or else we wouldn't have been called lock _ TEditLocks.LockDocAndTdd[tdd, "PaintTEditDocument", read, interrupt, TRUE]; IF lock=NIL THEN { IF ~interrupt THEN ERROR; RETURN }; -- someone else will do the display IF Carets.pCaretViewer=self THEN Carets.StopCaret[primary]; IF Carets.sCaretViewer=self THEN Carets.StopCaret[secondary]; IF (self.ch/TEditCompile.minAvgLineLeading)+1 > tdd.lineTable.maxLines THEN BEGIN <<-- make bigger line table>> oldTable: LineTable _ tdd.lineTable; tdd.lineTable _ TextNode.pZone.NEW[LineTableRec[(self.ch/TEditCompile.minAvgLineLeading)+1] _ [lastLine: oldTable.lastLine, lastY: oldTable.lastY, lines: NULL]]; FOR n: INTEGER IN [0..oldTable.lastLine] DO -- copy old data tdd.lineTable[n] _ oldTable[n]; ENDLOOP; END; typescript _ tdd.tsInfo # NIL; refreshing _ whatChanged=refresh; IF clear OR refreshing OR whatChanged=fullUpdate OR whatChanged=NIL THEN BEGIN IF clear THEN AdjustSelStates[self]; IF ~refreshing THEN FOR n: INTEGER IN [0..tdd.lineTable.lastLine] DO -- invalidate all lines tdd.lineTable[n].valid _ FALSE; ENDLOOP; redisplay _ TRUE; END ELSE SELECT whatChanged FROM $TakeDownPSel => TakeSelectionDown[primary, self, context]; $ShowPSel => ShowSelection[primary, self, context]; $TakeDownSSel => TakeSelectionDown[secondary, self, context]; $ShowSSel => ShowSelection[secondary, self, context]; $TakeDownFSel => TakeSelectionDown[feedback, self, context]; $ShowFSel => ShowSelection[feedback, self, context]; ENDCASE => WITH whatChanged SELECT FROM x: REF PreScrollDownRec => { -- move down to make room for new lines at top IF clear THEN ERROR; moveDownLines _ x.lines; moveDownDistance _ x.distance; refreshing _ redisplay _ TRUE }; x: ViewerClasses.PaintRectangle => { -- fix up after Viewer blt IF x.flavor # blt -- or if width has changed -- THEN { <> FOR n: INTEGER IN [0..tdd.lineTable.lastLine] DO -- invalidate all lines tdd.lineTable[n].valid _ FALSE; ENDLOOP } ELSE { -- height has changed n: INTEGER _ 0; h: INTEGER _ ViewerOps.UserToScreenCoords[self, 0, self.ch].sy-x.y; -- new height UNTIL n > tdd.lineTable.lastLine OR tdd.lineTable[n].yOffset+tdd.lineTable[n].descent >= h DO n _ n+1; ENDLOOP; n _ MIN[n, tdd.lineTable.lastLine]; -- make sure invalidate at least one line UNTIL n > tdd.lineTable.lastLine DO tdd.lineTable[n].valid _ FALSE; n _ n+1; ENDLOOP }; refreshing _ redisplay _ TRUE }; ENDCASE => ERROR; IF redisplay THEN BEGIN TakeDownForRedisplay[primary, self, context]; TakeDownForRedisplay[secondary, self, context]; TakeDownForRedisplay[feedback, self, context]; IF typescript AND self.ch < tdd.lineTable[tdd.lineTable.lastLine].yOffset THEN { <> delta: INTEGER = tdd.lineTable[tdd.lineTable.lastLine].yOffset - self.ch; n: INTEGER _ 0; UNTIL n=tdd.lineTable.lastLine OR tdd.lineTable[n].yOffset > delta DO n _ n+1; ENDLOOP; tdd.lineTable[0] _ tdd.lineTable[MIN[tdd.lineTable.lastLine, n+1]]; tdd.lineTable[0].valid _ FALSE }; IF ~tdd.lineTable[0].valid THEN { -- check start location startLoc: TextNode.Location; node: TextEdit.RefTextNode; CheckLocNode: PROC [loc: TextNode.Location] RETURNS [BOOL] = { startLoc _ loc; RETURN [ loc.node # NIL AND TextNode.Root[loc.node] = tdd.text ] }; IF CheckLocNode[tdd.lineTable[0].pos] THEN NULL ELSE { IF CheckLocNode[TEditSelection.InsertionPoint[]] OR CheckLocNode[EditSpan.afterMoved1] OR CheckLocNode[EditSpan.afterMoved2] THEN tdd.fixStart _ TRUE ELSE startLoc _ [TextNode.FirstChild[tdd.text],0] }; IF (node _ TextNode.NarrowToTextNode[startLoc.node]) # NIL THEN { size: TextEdit.Offset = TextEdit.Size[node]; IF startLoc.where NOT IN [0..size) THEN { tdd.lineTable[0].valid _ FALSE; tdd.fixStart _ TRUE; startLoc.where _ MAX[MIN[size-1,startLoc.where],0] }}; IF tdd.fixStart AND node # NIL AND startLoc.where > 0 THEN { OPEN RopeReader; <<-- move startLoc to after CR or at start of node>> rdr: Ref = GetRopeReader[]; cnt: INTEGER _ 0; foundBreak: BOOL _ FALSE; SetPosition[rdr,node.rope,startLoc.where]; FOR i:INT _ 0, i+1 DO char: CHAR; IF i=startLoc.where THEN { foundBreak _ TRUE; EXIT }; -- out of chars IF cnt > 200 THEN EXIT; -- give up char _ Backwards[rdr]; IF char = 15C THEN { foundBreak _ TRUE; [] _ Get[rdr]; EXIT }; cnt _ cnt+1; ENDLOOP; IF foundBreak THEN startLoc.where _ GetIndex[rdr]; FreeRopeReader[rdr]; }; tdd.fixStart _ FALSE; tdd.lineTable[0].pos _ startLoc; IF tdd.clipLevel < maxClip THEN -- check level of startLoc tdd.clipLevel _ MAX[tdd.clipLevel, TextNode.Level[startLoc.node]]; }; InvalidateLineCache; -- since selection impl keeps accelerators RefreshViewer[self, tdd, context, clear, (refreshing AND ~typescript), lock, moveDownLines, moveDownDistance]; END; IF whatChanged=$TakeDownPSel OR whatChanged=$TakeDownSSel OR whatChanged=$TakeDownFSel THEN NULL ELSE IF refreshing AND ~typescript AND TEditLocks.WaitingForWrite[lock] THEN NULL ELSE { --fix up the selections FixUpAfterDisplay[primary, self, context, ~tdd.readOnly]; FixUpAfterDisplay[secondary, self, context, TRUE]; FixUpAfterDisplay[feedback, self, context, FALSE] }; IF ~tdd.dirty THEN SELECT tdd.scroll FROM no => NULL; endofdoc => { TRUSTED {Process.Detach[FORK AutoScroll[self, tdd.scrollGlitch, TRUE, primary]]}; tdd.scroll _ no }; endofsel => { sel: Selection = SELECT tdd.scrollSelectionId FROM primary => pSel, secondary => sSel, feedback => fSel, ENDCASE => ERROR; IF TEditSelection.SelectionRoot[sel]=tdd.text THEN TRUSTED {Process.Detach[FORK AutoScroll[self, tdd.scrollGlitch, FALSE, tdd.scrollSelectionId]]} }; ENDCASE => ERROR; Cleanup[]; END; END; -- of unwind and PaintProc AutoScroll: PROC [ self: ViewerClasses.Viewer, tryToGlitch, toEndOfDoc: BOOL, id: SelectionId] = { ENABLE ABORTED => GOTO Quit; tdd: TEditDocumentData _ NARROW[self.data]; IF tdd=NIL THEN RETURN; TEditScrolling.AutoScroll[self, tryToGlitch, toEndOfDoc, id]; [] _ TEditDocument.SpinAndLock[tdd, "TEditDisplayImplAutoScroll"]; IF ~tdd.dirty THEN tdd.scroll _ no; TEditDocument.Unlock[tdd]; RefreshOver[]; EXITS Quit => NULL }; RefreshViewer: PROCEDURE [viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, dc: Graphics.Context, displayClear, refresh: BOOL, lock: TEditLocks.LockRef, moveDownLines, moveDownDistance: INTEGER] = { n: INTEGER _ 0; start, end: INTEGER; lines: TEditDocument.LineTable _ tdd.lineTable; UNTIL n > lines.lastLine DO -- search for an invalid line IF lines[n].valid THEN { n _ n+1; LOOP }; start _ n; UNTIL n+1 > lines.lastLine DO IF lines[n+1].valid THEN EXIT; n _ n+1; ENDLOOP; end _ n; IF start > 0 AND lines[start].pos.node # lines[start-1].pos.node THEN { <<-- need to reinitialize the start pos; may have deleted start node>> lines[start].pos.where _ 0; IF (lines[start].pos.node _ TextNode.ForwardClipped[lines[start-1].pos.node,tdd.clipLevel].nx) = 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 IF (refresh AND TEditLocks.WaitingForWrite[lock]) OR tdd.interrupt > 0 THEN { tdd.dirty _ TRUE; RETURN }; ENDLOOP; tdd.movedIn _ tdd.movedOut _ tdd.dirty _ FALSE; }; <> WhileInPosRange: ENTRY PROCEDURE [viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, dc: Graphics.Context, start, end: INTEGER, displayClear, refresh: BOOL, lock: TEditLocks.LockRef, moveDownLines, moveDownDistance: INTEGER] RETURNS [line: INTEGER] = BEGIN OPEN Graphics; ENABLE UNWIND => NULL; lines: TEditDocument.LineTable _ tdd.lineTable; node: TextEdit.RefTextNode _ TextNode.NarrowToTextNode[lines[start].pos.node]; pos: TextEdit.Offset _ lines[start].pos.where; leading, topLeading, bottomLeading, nodeLeading: INTEGER _ 0; knowBottomLeading: BOOL _ start=0; -- otherwise need to get it oldBottomY: INTEGER; y: INTEGER _ 0; bitmap: TEditFormat.Bitmap; nodeSize: TextEdit.Offset _ TextEdit.Size[node]; styleInit, yMatchRun: BOOL _ TRUE; yMatch, pasteLine, eraseToEnd: BOOL _ FALSE; GetFont: PROC [style: NodeStyle.Ref] RETURNS [font: VFonts.Font] = INLINE BEGIN face: NodeStyle.FontFace _ NodeStyle.GetFontFace[style]; font _ VFonts.EstablishFont[ NameSymbolTable.RopeFromName[NodeStyle.GetFontFamily[style]], NodeStyle.GetFontSizeI[style], face=Bold OR face=BoldItalic, face=Italic OR face=BoldItalic ]; END; styleInfoNode: TextNode.Ref; -- node for which we were last called to get style info <> GetStyleInfo: PROC [node: TextNode.Ref] = BEGIN OPEN formatInfo; IF node=styleInfoNode THEN RETURN; -- already have the style info styleInfoNode _ node; IF ~knowBottomLeading AND pos=0 THEN { -- get it from previous node NodeStyle.ApplyAll[nodeStyle, lines[line-1].pos.node]; bottomLeading _ NodeStyle.GetBottomLeadingI[nodeStyle] }; NodeStyle.ApplyAll[nodeStyle, node]; NodeStyle.Copy[charStyle, nodeStyle]; font _ GetFont[charStyle]; lineFormatting _ NodeStyle.GetLineFormatting[nodeStyle]; leftIndent _ NodeStyle.GetLeftIndentI[nodeStyle]; bodyIndent _ NodeStyle.GetBodyIndentI[nodeStyle]; firstIndent _ NodeStyle.GetFirstIndentI[nodeStyle]; lineMeasure _ viewer.cw - NodeStyle.GetRightIndentI[nodeStyle] - leftIndent; savedLooks _ TextLooks.allLooks; -- force init in formatter leading _ NodeStyle.GetLeadingI[nodeStyle]; topLeading _ MAX[bottomLeading, NodeStyle.GetTopLeadingI[nodeStyle]]; <> nodeLeading _ MAX[bottomLeading, topLeading]; -- use previous value of bottomLeading bottomLeading _ NodeStyle.GetBottomLeadingI[nodeStyle]; knowBottomLeading _ TRUE; tabWidth _ MAX[NodeStyle.GetTabStopsI[nodeStyle],1]; -- to avoid divide by 0 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; found: INTEGER _ 0; -- set by FindBelow TryMoveUp: PROC RETURNS [ok: BOOL] = { <> to, from, num, dist, bottom, diff: INTEGER; FindBelow: PROC RETURNS [ok: BOOL] = INLINE { -- returns true if finds the desired line <> FOR n: INTEGER IN [line..lines.lastLine] DO IF lines[n].valid AND lines[n].pos=[node,pos] THEN { found _ n; RETURN [lines[n].yOffset-lines[n].ascent > oldBottomY] }; ENDLOOP; RETURN [FALSE] }; IF ~checkedVisible THEN fullyVisible _ FullyVisible[]; IF ~fullyVisible OR ~FindBelow[] THEN RETURN [FALSE]; IF found=lines.lastLine AND lines[found].yOffset+lines[found].descent >= viewer.ch THEN RETURN [FALSE]; -- bottom is clipped to _ y-lines[found].ascent; -- top of new line is destination for blt from _ lines[found].yOffset-lines[found].ascent; -- uppermost source scan line IF to < 0 THEN { -- top of line is clipped from _ from-to; -- move source down by amount to be clipped to _ 0 }; dist _ from-to; -- the distance to move IF dist < 0 THEN RETURN [FALSE]; -- skip this bizarre case bottom _ MIN[viewer.ch, lines.lastY]; num _ bottom-from; -- number of scan lines to move IF num <= 0 THEN RETURN [FALSE]; ClearLine[oldBottomY, to]; -- clear gap above destination MoveScanLines[to, from, num]; -- move the good scan lines up ClearLine[bottom-dist, bottom]; -- clear the space at the bottom lines.lastY _ lines.lastY-dist; -- lastY tells last used scan line in viewer diff _ found-line; -- number of lines skipped IF diff # 0 THEN { -- update the line table info FOR n: INTEGER IN [line..lines.lastLine-diff] DO lines[n] _ lines[n+diff]; -- move all the info up to new location ENDLOOP; ClearEntries[lines.lastLine-diff+1, lines.lastLine]; -- these lines no longer in use lines.lastLine _ lines.lastLine-diff; end _ end-diff }; FOR n: INTEGER IN [line..lines.lastLine] DO -- adjust the baselines lines[n].yOffset _ lines[n].yOffset-dist; ENDLOOP; lines[lines.lastLine].valid _ FALSE; <> RETURN [TRUE] }; ClearEntries: PROC [from, to: INTEGER] = { FOR n: INTEGER IN [from..to] DO lines[n] _ [ pos: [NIL, 0], valid: TRUE, yOffset: 0, xOffset: 0, width: 0, ascent: 0, descent: 0]; ENDLOOP }; MoveScanLines: PROC [to, from, num: INTEGER] = BEGIN dY, sY, vY, vX: INTEGER; w: INTEGER _ viewer.cw; IF num=0 THEN RETURN; [vX, vY] _ ViewerOps.UserToScreenCoords[viewer, 0, viewer.ch]; <> vY _ (IF viewer.column=color THEN ColorDisplay.height ELSE ViewerSpecs.screenH)-vY; dY _ vY+to; -- destination y coord sY _ vY+from; -- source y coord GraphicsOps.MoveDeviceRectangle[ self: dc, width: w, -- full width of viewer height: num, -- height of source is num fromX: vX, fromY: sY, toX: vX, toY: dY ]; END; TryMoveDown: PROC [nchars: INTEGER, end: TEditDocument.LineBreak] = { <> <> next: TextNode.Location; from, lead, dist: INTEGER; IF ~checkedVisible THEN fullyVisible _ FullyVisible[]; IF ~fullyVisible OR ~lines[line].valid THEN RETURN; from _ lines[line].yOffset-lines[line].ascent; IF oldBottomY > from THEN RETURN; -- overwritten it already next _ IF end=eon THEN [NextNode[],0] ELSE [node,pos+nchars]; IF lines[line].pos # next THEN RETURN; IF end=eon THEN { -- need to get leading info for the next node GetStyleInfo[next.node]; lead _ nodeLeading } ELSE lead _ leading; dist _ y+lead-lines[line].yOffset; -- how far down to move it MoveLinesDown[line, 1, from, dist, TRUE] }; MoveLinesDown: PROC [startLine, numLines, from, dist: INTEGER, clear: BOOL] = { to, num, bottom: INTEGER; visible: BOOL _ FALSE; IF dist <= 0 THEN RETURN; -- skip this weird case to _ from+dist; bottom _ MIN[viewer.ch, lines.lastY+dist]; -- new bottom after blt num _ bottom-to; -- the number of scan lines to blt IF num <= 0 THEN RETURN; -- stuff to move down won't be visible MoveScanLines[from+dist, from, num]; IF clear THEN ClearLine[from, to]; -- clear the gap FOR n: INTEGER DECREASING IN [startLine..lines.lastLine] DO newOffset: INTEGER; IF n+numLines >= lines.maxLines THEN LOOP; -- avoid bounds fault newOffset _ lines[n].yOffset+dist; -- the new baseline IF newOffset-lines[n].ascent > viewer.ch THEN LOOP; -- off the bottom of viewer IF ~visible AND newOffset+lines[n].descent <= viewer.ch THEN { -- first fully visible line visible _ TRUE; lines.lastLine _ n+numLines; lines[n].valid _ FALSE; -- force repaint at bottom of viewer lines.lastY _ newOffset+lines[n].descent; ClearLine[lines.lastY, viewer.ch] }; lines[n+numLines] _ lines[n]; -- move all the info down by numLines lines[n+numLines].yOffset _ newOffset; -- adjust the baseline IF ~visible THEN { -- line must be partly visible at bottom of screen lines.lastLine _ n+numLines; lines.lastY _ newOffset+lines[n].descent; lines[n+numLines].valid _ FALSE }; ENDLOOP }; level: INTEGER _ 0; -- in case we are doing level clipping maxLevel: INTEGER = tdd.clipLevel; levelClipping: BOOL = maxLevel < maxClip; last, next: TextNode.Ref; NextNode: PROC RETURNS [TextNode.Ref] = { -- returns next node to display <> IF node=last THEN RETURN [next]; -- 1 entry cache last _ node; IF levelClipping THEN [next,level] _ TextNode.ForwardClipped[node,maxLevel,level] ELSE next _ TextNode.StepForward[node]; RETURN [next] }; 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; IF (line _ start) > lines.lastLine OR end < 0 THEN RETURN; -- update not on screen IF moveDownLines > 0 AND (fullyVisible _ FullyVisible[]) THEN <> MoveLinesDown[0, moveDownLines, 0, moveDownDistance, FALSE]; <> UNTIL line>=lines.maxLines OR (line>end AND lines[line].valid AND lines[line].pos=[node, pos] AND yMatch AND line<=lines.lastLine) DO IF (refresh AND TEditLocks.WaitingForWrite[lock]) OR tdd.interrupt > 0 THEN { <> lines.lastLine _ MAX[line, lines.lastLine]; lines[line].pos _ [node, pos]; lines[line].valid _ FALSE; RETURN}; IF pos >= nodeSize THEN BEGIN <> DO -- stay in this loop until have a non-empty node or have reached end of document IF (pos > 0 AND (line=0 OR lines[line-1].end=cr)) OR (nodeSize=0 AND node#tdd.text) THEN -- show blank line BEGIN OPEN formatInfo; <> GetStyleInfo[node]; IF line=start THEN y _ IF line=0 THEN 0 ELSE lines[line-1].yOffset; -- initialize y y _ y + (IF line=0 THEN NodeStyle.GetTopIndentI[nodeStyle] ELSE IF pos=0 THEN nodeLeading ELSE leading); IF y>=viewer.ch AND line>0 THEN {node _ NIL; EXIT}; <> oldBottomY _ IF line=0 THEN 0 ELSE lines[line-1].yOffset+lines[line-1].descent; IF ~displayClear AND TryMoveUp[] THEN NULL ELSE { lineStart, lineWidth, xoffset: INTEGER; IF pos=0 THEN { lineStart _ leftIndent + firstIndent; lineWidth _ lineMeasure - firstIndent } ELSE { lineStart _ leftIndent + bodyIndent; lineWidth _ lineMeasure - bodyIndent }; xoffset _ SELECT lineFormatting FROM FlushLeft, Justified => lineStart, FlushRight => lineStart + lineWidth, Centered => lineStart + lineWidth/2, ENDCASE => ERROR; IF ~displayClear THEN TryMoveDown[0, eon]; <> lines[line] _ [ valid: TRUE, pos: [node, pos], nChars: 0, end: eon, yOffset: y, xOffset: xoffset, width: 0, ascent: font.ascent, descent: font.height-font.ascent ]; IF ~displayClear THEN ClearLine[oldBottomY, y+lines[line].descent] }; line _ line+1; END; pos _ 0; <> node _ TextNode.NarrowToTextNode[NextNode[]]; IF node=NIL OR (nodeSize_TextEdit.Size[node]) > 0 THEN EXIT; ENDLOOP; IF node#NIL THEN {styleInit _ TRUE; LOOP} -- reapply termination test ELSE {eraseToEnd _ TRUE; EXIT}; -- off end of text END ELSE IF styleInit THEN {GetStyleInfo[node]; styleInit _ FALSE}; -- first time for this node y --baseline-- _ IF line=0 THEN NodeStyle.GetTopIndentI[formatInfo.nodeStyle] ELSE (IF line#start THEN y ELSE lines[line-1].yOffset) -- baseline -- + (IF pos=0 THEN nodeLeading ELSE leading) -- leading --; IF y>=viewer.ch AND line>0 THEN {eraseToEnd _ TRUE; EXIT}; <> yMatch _ y=lines[line].yOffset; <> oldBottomY _ IF line=0 THEN 0 ELSE lines[line-1].yOffset+lines[line-1].descent; <> IF ~displayClear AND TryMoveUp[] THEN NULL ELSE { pasteLine _ ~displayClear AND yMatch AND yMatchRun AND (y-oldBottomY)<=TEditCompile.maxLineAscent; <> <> bitmap _ TEditFormat.GetLineBitmap[viewer, tdd, node, pos, formatInfo, pasteLine]; <> IF ~displayClear THEN TryMoveDown[bitmap.chars, bitmap.break]; <> IF pasteLine AND lines[line].xOffset=bitmap.leftIndent THEN BEGIN extraTop: INTEGER = MAX[0, (IF line=start THEN lines[line].ascent ELSE y-oldBottomY)-bitmap.ascent]; Graphics.SetCP[dc, bitmap.leftIndent, viewer.ch-y+bitmap.ascent+extraTop]; GraphicsOps.DrawBitmap[ self: dc, bitmap: bitmap.bits, w: MAX[bitmap.width, lines[line].width], h: bitmap.ascent+extraTop+bitmap.descent, y: bitmap.yOffset-extraTop, yorigin: bitmap.yOffset-extraTop ]; END ELSE BEGIN -- must clear out old line contents first IF ~displayClear THEN ClearLine[oldBottomY, y+bitmap.descent]; Graphics.SetCP[dc, bitmap.leftIndent, viewer.ch-y+bitmap.ascent]; GraphicsOps.DrawBitmap[ self: dc, bitmap: bitmap.bits, w: bitmap.width, h: bitmap.ascent+bitmap.descent, y: bitmap.yOffset, yorigin: bitmap.yOffset ]; IF ~yMatch THEN yMatchRun _ FALSE; END; lines[line] _ [ valid: TRUE, pos: [node, pos], nChars: bitmap.chars, end: bitmap.break, yOffset: y, xOffset: bitmap.leftIndent, width: bitmap.width, ascent: bitmap.ascent, descent: bitmap.descent ]; }; pos _ pos + lines[line].nChars; line _ line + 1; ENDLOOP; <> IF eraseToEnd OR line>=lines.maxLines THEN BEGIN <> bottomOfNewLines: INTEGER _ IF line=0 THEN 0 ELSE lines[line-1].yOffset+lines[line-1].descent; lines.lastLine _ MAX[line,1]-1; -- set end mark ClearEntries[lines.lastLine+1, lines.maxLines-1]; IF ~displayClear AND bottomOfNewLines < lines.lastY THEN -- white out old lines ClearLine[bottomOfNewLines, lines.lastY]; lines.lastY _ bottomOfNewLines; END ELSE BEGIN -- take this branch if got back in synch during repaint IF y>viewer.ch OR displayClear THEN ERROR; lines.lastLine _ MAX[lines.lastLine, MAX[line,1]-1]; lines.lastY _ MAX[lines.lastY, y+lines[lines.lastLine].descent]; END; END; formatInfo: TEditFormat.FormatInfo; Init: ENTRY PROC = { formatInfo _ NEW[TEditFormat.FormatInfoRec _ [ nodeStyle: NodeStyle.Create[], tabStyle: NodeStyle.Create[], charStyle: NodeStyle.Create[] ]]; }; Init; END.