DIRECTORY Carets USING [pCaretViewer, sCaretViewer, StopCaret], EditSpan USING [afterMoved1, afterMoved2], Imager, NodeStyle, Process USING [Detach], RopeReader USING [Ref, GetRopeReader, FreeRopeReader, SetPosition, Backwards, Get, GetIndex], Scaled, TextEdit USING [Offset, RefTextNode, Size], TextNode USING [FirstChild, ForwardClipped, Level, Location, NarrowToTextNode, pZone, Ref, Root, StepForward], TEditCompile USING [minAvgLineLeading], TEditDisplay, TEditDocument USING [LineBreak, LineTable, LineTableRec, maxClip, Selection, SelectionRec, SpinAndLock, SelectionId, TEditDocumentData, Unlock], TEditFormat, TEditImpl, TEditLocks USING [LockDocAndTdd, LockRef, UnlockDocAndTdd, WaitingForWrite], TEditScrolling USING [AutoScroll], TEditSelection USING [AdjustSelStates, FixUpAfterDisplay, InsertionPoint, InvalidateLineCache, pSel, sSel, fSel, SelectionRoot, ShowSelection, TakeDownForRedisplay, TakeSelectionDown], TEditTouchup, ViewerClasses USING [PaintProc, Viewer, PaintRectangle], ViewerOps USING [UserToScreenCoords]; TEditDisplayImpl: CEDAR PROGRAM IMPORTS Carets, EditSpan, Imager, NodeStyle, Process, Scaled, TextEdit, TextNode, RopeReader, TEditDocument, TEditFormat, TEditLocks, TEditScrolling, TEditSelection, TEditTouchup, ViewerOps EXPORTS TEditImpl = BEGIN PaintTEditDocument: PUBLIC ViewerClasses.PaintProc = BEGIN tdd: TEditDocument.TEditDocumentData = NARROW[self.data]; lock: TEditLocks.LockRef; moveDownLines, moveDownDistance: INTEGER _ 0; -- used with scrolling down redisplay, refreshing: BOOL _ FALSE; Cleanup: PROC = { TEditLocks.UnlockDocAndTdd[tdd]; TEditTouchup.RefreshOver[] }; BEGIN ENABLE UNWIND => Cleanup[]; interrupt: BOOL = clear OR whatChanged=TEditTouchup.fullUpdate OR whatChanged=NIL; typescript: BOOL; IF self.destroyed OR tdd=NIL 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 NOT 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 oldTable: TEditDocument.LineTable _ tdd.lineTable; tdd.lineTable _ TextNode.pZone.NEW[TEditDocument.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=TEditTouchup.refresh; IF clear OR refreshing OR whatChanged=TEditTouchup.fullUpdate OR whatChanged=NIL THEN { IF clear THEN TEditSelection.AdjustSelStates[self]; IF NOT refreshing THEN FOR n: INTEGER IN [0..tdd.lineTable.lastLine] DO -- invalidate all lines tdd.lineTable[n].valid _ FALSE; ENDLOOP; redisplay _ TRUE; } ELSE { SELECT whatChanged FROM $TakeDownPSel => TEditSelection.TakeSelectionDown[primary, self, context]; $ShowPSel => TEditSelection.ShowSelection[primary, self, context]; $TakeDownSSel => TEditSelection.TakeSelectionDown[secondary, self, context]; $ShowSSel => TEditSelection.ShowSelection[secondary, self, context]; $TakeDownFSel => TEditSelection.TakeSelectionDown[feedback, self, context]; $ShowFSel => TEditSelection.ShowSelection[feedback, self, context]; ENDCASE => WITH whatChanged SELECT FROM x: REF TEditTouchup.PreScrollDownRec => { 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 { TEditSelection.TakeDownForRedisplay[primary, self, context]; TEditSelection.TakeDownForRedisplay[secondary, self, context]; TEditSelection.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 NOT 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; 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 < TEditDocument.maxClip THEN -- check level of startLoc tdd.clipLevel _ MAX[tdd.clipLevel, TextNode.Level[startLoc.node]]; }; TEditSelection.InvalidateLineCache; -- since selection impl keeps accelerators RefreshViewer[self, tdd, context, clear, (whatChanged=TEditTouchup.refresh AND NOT typescript), lock, moveDownLines, moveDownDistance]; }; IF whatChanged=$TakeDownPSel OR whatChanged=$TakeDownSSel OR whatChanged=$TakeDownFSel THEN NULL ELSE IF whatChanged=TEditTouchup.refresh AND NOT typescript AND TEditLocks.WaitingForWrite[lock] THEN NULL ELSE { --fix up the selections TEditSelection.FixUpAfterDisplay[primary, self, context, NOT tdd.readOnly]; TEditSelection.FixUpAfterDisplay[secondary, self, context, TRUE]; TEditSelection.FixUpAfterDisplay[feedback, self, context, FALSE] }; IF NOT 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: TEditDocument.Selection = SELECT tdd.scrollSelectionId FROM primary => TEditSelection.pSel, secondary => TEditSelection.sSel, feedback => TEditSelection.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: TEditDocument.SelectionId] = { ENABLE ABORTED => GOTO Quit; tdd: TEditDocument.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]; TEditTouchup.RefreshOver[]; EXITS Quit => NULL }; RefreshViewer: PROCEDURE [viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, dc: Imager.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 { 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: PROCEDURE [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, dc: Imager.Context, start, end: INTEGER, displayClear, refresh: BOOLEAN, lock: TEditLocks.LockRef, moveDownLines, moveDownDistance: INTEGER ] RETURNS [line: INTEGER] = { lines: TEditDocument.LineTable _ tdd.lineTable; oldBottomY: INTEGER _ ComputeInitialBottomY[]; ComputeInitialBottomY: PROC RETURNS [y: INTEGER] = { y _ MAX[0, lines[start].yOffset - lines[start].ascent]; WHILE start>0 AND lines[start-1].yOffset + lines[start-1].descent > y DO start _ start-1; lines[start].valid _ FALSE; ENDLOOP; }; 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 y: INTEGER _ 0; nodeSize: TextEdit.Offset _ TextEdit.Size[node]; styleInit, yMatchRun: BOOL _ TRUE; yMatch, eraseToEnd: BOOL _ FALSE; lineInfo: TEditFormat.LineInfo _ TEditFormat.Allocate[]; nodeStyle: NodeStyle.Ref _ NodeStyle.Alloc[]; Cleanup: PROC = {lineInfo.Release[]; lineInfo _ NIL; nodeStyle.Free[]; nodeStyle _ NIL}; styleInfoNode: TextNode.Ref; -- node for which we were last called to get style info GetStyleInfo: PROC [node: TextNode.Ref] = { IF node = styleInfoNode THEN RETURN; -- already have the style info styleInfoNode _ node; IF NOT 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]; 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; }; FullyVisible: PROC RETURNS [BOOL] = INLINE { IF NOT checkedVisible THEN { fullyVisible _ dc.TestRectangle[x: 0, y: 0, w: viewer.cw, h: viewer.ch] = visible; checkedVisible _ TRUE; }; RETURN [fullyVisible] }; checkedVisible, fullyVisible: BOOL _ FALSE; found: INTEGER _ 0; -- set by FindBelow TryMoveUp: PROC RETURNS [ok: BOOL] = { to, from, num, dist, bottom, diff: INTEGER; dirtyScanlinesInFirstTextline: NAT _ 0; 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 NOT FullyVisible[] OR NOT FindBelow[] THEN RETURN [FALSE]; IF found=lines.lastLine AND lines[found].yOffset+lines[found].descent >= viewer.ch THEN RETURN [FALSE]; -- bottom is clipped IF found>0 AND ( dirtyScanlinesInFirstTextline _ MAX[0, lines[found-1].yOffset+lines[found-1].descent - lines[found].yOffset-lines[found].ascent ] ) > 0 THEN lines[found].valid _ FALSE; to _ y-lines[found].ascent + dirtyScanlinesInFirstTextline; from _ lines[found].yOffset-lines[found].ascent + dirtyScanlinesInFirstTextline; 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 oldBottomY _ 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 bounds: Imager.IntRectangle _ Imager.GetViewBounds[dc]; yBase: INTEGER _ bounds.h + bounds.y - num; IF num > 0 THEN Imager.MoveSurfaceRectangle[context: dc, source: [x: bounds.x, y: yBase - from, w: bounds.w, h: num], dest: [x: bounds.x, y: yBase - to] ]; END; TryMoveDown: PROC [nchars: INTEGER, end: TEditDocument.LineBreak] = { next: TextNode.Location; from, lead, dist: INTEGER; IF NOT FullyVisible[] OR NOT 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] }; MoveLinesDown: PROC [startLine, numLines, from, dist: INTEGER] = { to, num, bottom: INTEGER; visible: BOOL _ FALSE; dirtyScanlinesInFirstTextline: INTEGER; 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 startLine > 0 AND ( dirtyScanlinesInFirstTextline _ lines[startLine-1].yOffset+lines[startLine-1].descent - lines[startLine].yOffset-lines[startLine].ascent ) > 0 THEN { lines[startLine].valid _ FALSE; ClearLine[from, to + dirtyScanlinesInFirstTextline]; } ELSE 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 NOT 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 NOT 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 < TEditDocument.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] = { IF oldBottomY >= newBottomY THEN RETURN; Imager.SetColor[dc, Imager.white]; Imager.MaskRectangle[dc, 0, viewer.ch-newBottomY, viewer.cw, newBottomY-oldBottomY]; Imager.SetColor[dc, Imager.black]; }; IF (line _ start) > lines.lastLine OR end < 0 THEN {Cleanup[]; RETURN}; IF moveDownLines > 0 AND FullyVisible[] THEN { MoveLinesDown[0, moveDownLines, 0, moveDownDistance]; }; 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; Cleanup[]; RETURN }; IF pos >= nodeSize THEN { 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 { 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}; IF NOT displayClear AND TryMoveUp[] THEN NULL ELSE { indent: INTEGER _ IF pos=0 THEN nodeStyle.GetFirstIndentI[] ELSE nodeStyle.GetBodyIndentI[]; lineStart: INTEGER _ nodeStyle.GetLeftIndentI[] + indent; lineWidth: INTEGER _ viewer.cw - nodeStyle.GetRightIndentI[] - indent; xoffset: INTEGER _ SELECT nodeStyle.GetLineFormatting[] FROM FlushLeft, Justified => lineStart, FlushRight => lineStart + lineWidth, Centered => lineStart + lineWidth/2, ENDCASE => ERROR; IF NOT displayClear THEN TryMoveDown[0, eon]; lines[line] _ [ valid: TRUE, pos: [node, pos], nChars: 0, end: eon, yOffset: y, xOffset: xoffset, width: 0, ascent: 1, descent: 0 ]; }; line _ line+1; }; pos _ 0; node _ TextNode.NarrowToTextNode[NextNode[]]; IF node=NIL OR (nodeSize_TextEdit.Size[node]) > 0 THEN EXIT; ENDLOOP; IF NOT displayClear THEN {ClearLine[oldBottomY, y]; oldBottomY _ y}; IF node#NIL THEN {styleInit _ TRUE; LOOP} -- reapply termination test ELSE {eraseToEnd _ TRUE; EXIT}; -- off end of text } ELSE IF styleInit THEN {GetStyleInfo[node]; styleInit _ FALSE}; -- first time for this node y --baseline-- _ IF line=0 THEN NodeStyle.GetTopIndentI[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 NOT displayClear AND TryMoveUp[] THEN NULL ELSE { TEditFormat.FormatLine[ self: lineInfo, tdd: tdd, node: node, startOffset: pos, nodeStyle: nodeStyle, lineWidth: Scaled.FromInt[viewer.cw], forPaint: TRUE ]; IF NOT displayClear THEN TryMoveDown[lineInfo.nChars, lineInfo.break]; Imager.SetXY[dc, [lineInfo.xOffset.Float[], viewer.ch-y]]; IF NOT displayClear THEN ClearLine[oldBottomY, y-lineInfo.ymin]; oldBottomY _ y-lineInfo.ymin; TEditFormat.Paint[lineInfo, dc]; lines[line] _ [ valid: TRUE, pos: [node, pos], nChars: lineInfo.nChars, end: lineInfo.break, yOffset: y, xOffset: lineInfo.xOffset.Round[], width: lineInfo.xmax-lineInfo.xmin, ascent: MIN[511, MAX[0, lineInfo.ymax]], descent: MIN[127, MAX[0, -lineInfo.ymin]] ]; }; pos _ pos + lines[line].nChars; line _ line + 1; ENDLOOP; IF eraseToEnd OR line>=lines.maxLines THEN { 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 NOT displayClear AND bottomOfNewLines < lines.lastY THEN -- white out old lines ClearLine[bottomOfNewLines, lines.lastY]; lines.lastY _ bottomOfNewLines; } ELSE { 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]; }; Cleanup[]; }; END. ¾TEditDisplayImpl.mesa, last edit by Paxton on January 27, 1983 3:31 pm Last Edit by Maxwell, January 6, 1983 10:36 am Last Edit by Plass, August 12, 1983 4:06 pm Last Edit by Wyatt, October 27, 1983 2:36 pm -- make bigger line table move down to make room for new lines at top Repaint all when width changes. Could try to be smart here, but probably better not to. Would have to worry about right indent and line formatting (e.g., centered lines). If a typescript viewer gets smaller such that bottom line is no longer visible, automatically scroll it up to keep bottom line on screen. Move startLoc to after CR or at start of node -- need to reinitialize the start pos; may have deleted start node The following procedure is passed two document line positions; the meaning is to paint all the lines marked as invalid and then continue painting within each node until the ripple has subsided. Returns line where stopped. Has to be done before node, pos, etc. are computed. This is used to avoid redoing GetStyleInfo after have peeked at next node style as part of trying to move lines down. use value of bottomLeading left from previous node Returns true if found the desired line at a lower location on the screen. Blts the line and all following lines, clears out screen below them, then updates line table. Looks for valid line at or below current line with desired start location. Assumes oldBottomY has been set to bottom of previous line, so if found line starts below that it will be ok on the screen still. If finds such a line, stores its index in "found" and returns true. top of clean part of new line is destination for blt uppermost source scan line Set the last line invalid to force repaint to fill in gap at bottom of viewer. We are about to put [node, pos] in line. It will contain nchars with break specified by end. This procedure looks to see if we will overwrite what we will next want to put in line+1. If so, move it down now. This happens whenever we insert a single line. This proc may need to call GetStyleInfo, so use all the style info you're interested in before you call it. Repaint the top line if it got dirtied by descenders. For future reference, TEditScrollingImpl.ScrollTEditDocument also has code to calculate next node for display to handle scrolling up in 1 line viewers. update not on screen Move things down before start repainting. This accelerates scrolling down. Paint lines until the line we would paint is: (1) valid, (2) past the range we were told to paint, (3) matches the line table offsets (i.e. no ripple going forward), and (4) matches in the Y coord. An exit for end of text and off end of viewer are included below. Either an edit is pending or a complete repaint is pending. In either case, we quit. Nothing more to display in this node; pos is where this line should start, so we are done with the node. Show blank line Line 0 starts at end of node, or CR at end of node on previous line, or empty node other than root node. If baseline would be off bottom of viewer and not the first line, don't show it. Need to finish with style info before call this. step to the next node in the tree. if baseline would be off bottom of viewer and not the first line, don't show it. true if the new line is baseline aligned with the previous bits on the screen bottom of the previous line Need to finish with style info before call this. May call GetStyleInfo for next node. Clear former bottom lines if we run off end of text or viewer (partial line must be clipped) eraseToEnd starts false. set true if reach end of document or stop with line that would have baseline below bottom of viewer. Take this branch if got back in synch during repaint Michael Plass, August 9, 1983 4:17 pm: Conversion to Imager. ʘJšœF™FJšœ.™.J™+J™,J™šÏk ˜ Jšœœ)˜5Jšœ œ˜*Jšœ˜Jšœ ˜ Jšœœ ˜Jšœ œM˜]J˜Jšœ œ˜+Jšœ œ`˜nJšœ œ˜'J˜ Jšœœ}˜Jšœ ˜ J˜ Jšœ œ<˜LJšœœ˜"Jšœœ¤˜¸J˜ Jšœœ%˜8Jšœ œ˜%J˜—Jšœœ˜J˜Jšœ¶˜½Jšœ ˜J˜Jš˜J˜šœœ˜4Jš˜Jšœ'œ ˜9J˜Jšœ!œÏc˜IJšœœœ˜$J˜šÏnœœC˜PJ˜—Jšœœœ˜!Jš œ œ œ%œ œ˜RJšœ œ˜J˜Jš œœœœœž+˜UJšœœž'˜>J˜JšœLœ˜RJšœœœœœ œœœž#˜]J˜Jšœœ˜;Jšœœ˜=J˜šœEœ˜QJšž™Jšœ2˜2šœœI˜kJšœ<œ˜C—š œœœœž˜˜>Jšœ=˜=šœ œ9œ˜PJ™‰Jšœœ;˜IJšœœ˜šœœ"˜EJšœ œ˜—Jšœ!œ˜CJšœœ˜!—šœœœž˜J˜Jšœœœ%˜@Jšœ˜—Jšœ$œ˜/šœ˜šœ.˜0Jšœ#˜%Jšœ$œ˜>—Jšœ-˜1Jšœ˜—šœ5œœ˜AJ˜,šœœœ œ˜)Jšœœ˜Jšœœ˜Jšœœœ˜2Jšœ˜—Jšœ˜—š œœœœœœ ˜MJšœ-™-J˜Jšœœ˜Jšœ œœ˜J˜*šœœ ˜Jšœœ˜ Jš œœœœž˜EJšœ œœž ˜"J˜Jšœ œœœ˜?J˜ Jšœ˜—Jšœ œ ˜2J˜J˜—Jšœœ˜J˜ šœ'œž˜HJšœœ2˜E——Jšœ$ž*˜NšœKœœ ˜_Jšœ'˜'—Jšœ˜—šœ˜Jšœ˜Jšœœ˜#—š œœ"œœ ˜?Jšœ!œ˜*—šœž˜Jšœ9œ˜KJšœ;œ˜AJšœ:œ˜@Jšœ˜—š œœ œœ ˜,Jšœœ˜ ˜ Jšœœ$œ ˜QJšœ˜—˜ šœœ˜@Jšœ˜Jšœ!˜!Jšœ ˜ Jšœœ˜—šœ,˜2Jšœœ$œ˜b——Jšœœ˜—Jšœ ˜ Jšœœž˜$J˜—šŸ œœ˜Jšœ5œ$˜]Jšœœœ˜Jšœ'œ ˜9Jšœœœœ˜Jšœ=˜=JšœB˜BJšœ œ˜#Jšœ˜Jšœ˜Jšœ ˜Jšœ˜J˜—šŸ œ œ˜7Jšœ:˜:Jšœœ˜6Jšœ!œ˜-Jšœœ˜Jšœ œ˜Jšœ/˜/šœœž˜9Jšœœ œ˜)J˜ šœ˜Jšœœœ˜J˜Jšœ˜—J˜šœ œ1œ˜GJšžB™BJ˜šœ˜šœEœ˜MJšœž œ˜3———šœ>˜>Jšœ0˜0—Jšœ&ž˜Dšœ œ#œœ˜MJšœ œœ˜—Jšœ˜—Jšœ)œ˜/J˜—J˜JšœÞ™ÞJ˜šŸœ œ˜Jšœ˜Jšœ%˜%Jšœ˜Jšœ œ˜Jšœœ˜Jšœ˜Jšœ!˜(Jšœœœ˜Jšœ/˜/Jšœ œ˜.šŸœœœœ˜4J™3Jšœœ0˜7šœ œ5˜HJšœ˜Jšœœ˜Jšœ˜—Jšœ˜—J˜NJ˜.Jšœ1œ˜=Jšœœ ž˜>Jšœœ˜J˜0Jšœœœ˜"Jšœœœ˜!Jšœ8˜8Jšœ-˜-JšŸœœ#œ œ˜XJ˜šœž7˜TJ™u—J˜šŸ œœ˜+Jšœœœž˜CJ˜š œœœœž˜FJ˜6Jšœ6˜6Jšœ˜—J˜$J˜+šœ œ5˜EJšž2™2—Jšœœž&˜TJ˜7Jšœœ˜Jšœ˜—J˜š Ÿ œœœœœ˜,šœœœ˜JšœR˜RJšœœ˜Jšœ˜—Jšœ˜Jšœ˜—J˜Jšœœœ˜+J˜Jšœœž˜'šŸ œœœœ˜&J™¨Jšœ#œ˜+Jšœœ˜'š Ÿ œœœœœž)˜WJšœ’™’šœœœ˜+šœœœ˜4Jšœ ˜ Jšœ0˜6Jšœ˜—Jšœ˜—Jšœœ˜Jšœ˜—Jšœœœœ œœœ˜=šœœ7˜RJšœœœž˜)—šœ œ˜šœ˜šœ˜JšœX˜XJšœ˜——Jšœœœ˜&—šœ;˜;Jšœ4™4—šœP˜PJšœ™—šœœž˜*Jšœž+˜;J˜ —Jšœž˜'Jš œ œœœž˜:Jšœ œ˜%Jšœž˜2Jšœ œœœ˜ Jšœž˜9Jšœž˜