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, 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 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 }; 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; 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, (whatChanged=refresh AND ~typescript), lock, moveDownLines, moveDownDistance]; END; IF whatChanged=$TakeDownPSel OR whatChanged=$TakeDownSSel OR whatChanged=$TakeDownFSel THEN NULL ELSE IF whatChanged=refresh 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 { 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. "-- TEditDisplayImpl.mesa, last edit by Paxton on January 27, 1983 3:31 pm Last Edited by: Maxwell, January 6, 1983 10:36 am Last Edited by: Plass, March 8, 1983 1:46 pm -- make bigger line table 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. Monitored since has reusable bitmaps. 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. Set the last line invalid to force repaint to fill in gap at bottom of viewer. screen coords for upper left 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 insert a single line. This proc may need to call GetStyleInfo, so use all the style info your're interested in before you call it. For future reference, TEditScrollingImpl.ScrollTEditDocument also has code to calculate next node for display to handle scrolling up in 1 line viewers. move things down before start repainting. This accelerates scrolling down. 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. 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. 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 if pasteLine is true, we can do a single BITBLT to clear the old bits and put up the new ones. yMatchRun starts true and goes false if yMatch is ever false, so that once we no longer are aligning on baselines during a paint we will continue to conservatively whiten the entire area we are painting into. pasteLine is passed to be sure to whiten past the last word that fit. 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. ʦ˜JšœI™IJšœ1™1Jšœ,™,šÏk ˜ Jšœœ)˜5Jšœ œ ˜Jšœ œ˜*Jšœ œR˜`Jšœ œ#˜4Jšœœ˜%Jšœ œõ˜„Jšœœ ˜Jšœœ ˜Jšœ œM˜]Jšœ œ˜+Jšœ œ`˜nJšœ œ ˜Jšœ œ$˜6J˜ Jšœœ}˜Jšœ œ4˜EJ˜ Jšœ œ<˜LJšœœ˜"Jšœœ¤˜¸J˜ Jšœœ˜#Jšœœ˜(Jšœ œ˜%Jšœ œ ˜J˜—Jšœ ˜J˜Jšœê˜ñJšœ ˜Jšœ ˜J˜Jšœœ-˜7J˜šœœ˜4Jš˜Jšœœ ˜+J˜Jšœ!œÏc˜IJšœœœ˜$J˜JšÏnœœ6˜CJšœœœœ˜Jšœœœ˜!Jš œ œ œœ œ˜EJšœ œ˜J˜Jšœœœž+˜JJšœœž'˜>J˜JšœLœ˜RJšœœœœ œœœž#˜ZJ˜Jšœœ˜;Jšœœ˜=J˜šœEœ˜QJšž™J˜$šœœ;˜]Jšœ<œ˜C—š œœœœž˜J˜Jšœœœ)˜D—Jšœ$œ˜/šœ˜šœ.˜0Jšœ#˜%Jšœ$œ˜>—Jšœ0˜4—šœ5œœ˜AJ˜,šœœœ œ˜)Jšœœ˜Jšœœ˜Jšœœœ˜6——š œœœœœœ ˜MJšž0™0J˜Jšœœ˜Jšœ œœ˜J˜*šœœ ˜Jšœœ˜ Jš œœœœž˜EJšœ œœž ˜"J˜Jšœ œœœ˜?J˜ Jšœ˜—Jšœ œ ˜2J˜J˜—Jšœœ˜J˜ šœœž˜:Jšœœ2˜E——Jšœž*˜?šœ>œ˜OJšœ'˜'—Jšœ˜J˜—šœ˜Jšœ˜Jšœœ˜#—šœœœ ˜/Jšœ!œ˜*—šœž˜Jšœ9˜9Jšœ,œ˜2Jšœ+œ˜4—šœ œœ ˜)Jšœœ˜ ˜ Jšœœ$œ ˜QJšœ˜—˜ šœœ˜2Jšœ˜J˜J˜Jšœœ˜—šœ,˜2Jšœœ$œ˜b——Jšœœ˜—J˜Jšœ ˜ Jšœœž˜$J˜—šŸ œœ˜Jšœ5œ˜OJšœœœ˜Jšœœ ˜+Jšœœœœ˜Jšœ=˜=JšœB˜BJšœ œ˜#Jšœ˜Jšœ˜Jšœ œ˜J˜—šŸ œ œ˜7J˜˜>Jšœ0˜0—Jšœ&ž˜Dšœ œ#œœ˜MJšœ œœ˜—Jšœ˜J˜—Jšœ)œ˜/J˜J˜—Jšœ…™…J˜šŸœœ œ˜?JšœHœ˜PJšœœ˜6Jšœ!œ˜)Jšœœœœ ˜.Jšœœœ˜J˜J˜/J˜NJ˜.Jšœ1œ˜=Jšœœ ž˜>Jšœ œ˜Jšœœ˜J˜J˜0Jšœœœ˜"Jšœœœ˜,J˜š Ÿœœœœ˜OJ˜8˜J˜=J˜Jšœ œ˜Jšœ œ˜J˜—Jšœ˜J˜—šœž7˜TJ™u—J˜šŸ œœœœ ˜@Jšœœœž˜AJ˜šœœœž˜CJ˜6Jšœ9˜9—J˜$J˜%J˜J˜8J˜1J˜1J˜3J˜LJšœ!ž˜;J˜+šœ œ5˜EJšž2™2—Jšœœž&˜TJ˜7Jšœœ˜Jšœ œ'ž˜LJšœ˜—J˜šŸ œœœœ˜%J˜Jšœœ˜Jšœœ˜Jšœœœœ˜3J˜J˜'Jšœœœœ˜)J˜(Jšœœœœ˜*Jšœœ˜—J˜šœœœ˜+J˜—Jšœœž˜'šŸ œœœœ˜&J™¨Jšœ#œ˜+š Ÿ œœœœœž)˜WJšœ’™’šœœœ˜+šœœœ˜4Jšœ œ3˜D—Jšœ˜—Jšœœ˜—Jšœœ˜6Jš œœœœœ˜5šœœ7˜RJšœœœž˜)—Jšœž)˜EJšœ1ž˜Nšœœž˜*Jšœž+˜;J˜ —Jšœž˜'Jš œ œœœž˜:Jšœ œ˜%Jšœž˜2Jšœ œœœ˜ Jšœž˜9Jšœž˜˜>Jšœ&™&—Jšœœœœ˜SJšœ ž˜#Jšœž˜ šœ ˜ Jšœ ˜ Jšœ ž˜!Jšœ ž˜'Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜—Jšœ˜J˜—šŸ œœ œ#˜EJšœÿ™ÿJ™lJ˜Jšœœ˜Jšœœ˜6Jšœœœœ˜3Jšœ.˜.Jšœœœž˜;Jšœœ œœ˜=Jšœœœ˜&šœ œž-˜?J˜-—Jšœ˜Jšœ#ž˜=Jšœ#œ˜+J˜—šŸ œœ#œ œ˜OJšœœ˜Jšœ œœ˜Jšœ œœž˜1J˜Jšœ œž˜BJšœž"˜3Jšœ œœž&˜?J˜$Jšœœž˜3š œœ œœ˜;Jšœ œ˜Jšœœœž˜@Jšœ#ž˜6Jšœ'œœž˜Ošœ œ)œž˜ZJšœ œ˜Jšœ˜Jšœœž$˜J™V—J˜šœ œ'œ˜Ašœ œœ˜Jšœœ œœ˜I—J˜J˜J˜ J˜Jšœœ"˜(J˜)J˜J˜ J˜—Jš˜J˜—šœœž)˜4Jšœœ)˜>J˜A˜J˜ J˜J˜J˜ J˜J˜J˜—Jšœ œ œ˜"Jšœ˜J˜—˜Jšœœ˜ J˜J˜J˜J˜ J˜J˜J˜J˜J˜J˜—J˜J˜—J˜J˜J˜šœ˜J˜——Jšž\™\J˜šœ œœ˜0Jšœ}™}šœœ˜Jšœœœ-˜B—Jšœœ ž˜/Jšœ1˜1J˜šœœ œž˜OJ˜)J˜—J˜Jš˜J˜—šœœž7˜BJšœ œœœ˜*Jšœœœ ˜4Jšœœ/˜@Jšœ˜J˜—Jšœ˜J˜—J˜#J˜šŸœœœ˜šœ œ˜.J˜J˜J˜J˜—J˜—J˜J˜Jšœ˜J˜—…—U"zê