DIRECTORY Ascii, Graphics, LooksReader, NameSymbolTable, NodeStyle, Process, Real, RefText, Rope, RopeReader, TiogaDocument, TiogaFormat, TextEdit, TiogaNode, TiogaLooks, UnifiedFonts, Scaled; TiogaFormatImpl: CEDAR MONITOR IMPORTS Graphics, LooksReader, NameSymbolTable, NodeStyle, Process, Real, RefText, RopeReader, TiogaDocument, TextEdit, UnifiedFonts, Scaled EXPORTS TiogaFormat = BEGIN OPEN TiogaFormat; FloorI: PROCEDURE [real: REAL] RETURNS [INTEGER] = INLINE { RETURN[Real.Fix[real-FIRST[INTEGER]]+FIRST[INTEGER]]; }; CeilingI: PROCEDURE [real: REAL] RETURNS [integer: INTEGER] = INLINE { RETURN[Real.Fix[real-LAST[INTEGER]]+LAST[INTEGER]]; }; scratchLineInfo1, scratchLineInfo2: LineInfo _ NIL; Allocate: PUBLIC ENTRY PROC RETURNS [lineInfo: LineInfo] = { IF scratchLineInfo1 # NIL THEN {lineInfo _ scratchLineInfo1; scratchLineInfo1 _ NIL} ELSE IF scratchLineInfo2 # NIL THEN {lineInfo _ scratchLineInfo2; scratchLineInfo2 _ NIL} ELSE { lineInfo _ NEW[LineInfoRec]; lineInfo.charInfo _ NEW[CharInfoRec[125]]; lineInfo.formatInfo _ NEW[FormatInfoRec[12]]; lineInfo.positionInfo _ NEW[PositionInfoRec[126]]; }; }; Release: PUBLIC ENTRY PROC [lineInfo: LineInfo] = { f: FormatInfo _ lineInfo.formatInfo; WHILE f.length>0 DO f.length _ f.length-1; f[f.length].font _ NIL; f[f.length].tab _ NIL; ENDLOOP; IF scratchLineInfo1 = NIL THEN {scratchLineInfo1 _ lineInfo} ELSE scratchLineInfo2 _ lineInfo; }; ExpandChars: PROC [lineInfo: LineInfo] = { oldCharLimit: NAT _ lineInfo.charInfo.maxLength; charLimit: NAT _ MIN[oldCharLimit + oldCharLimit/3 + 1, LAST[CharNumber]-1]; newCharInfo: CharInfo _ NEW[CharInfoRec[charLimit]]; newPositionInfo: PositionInfo _ NEW[PositionInfoRec[charLimit+1]]; IF charLimit <= oldCharLimit THEN ERROR TiogaDocument.fatalTiogaError; FOR i: NAT IN [0..oldCharLimit) DO newCharInfo[i] _ lineInfo.charInfo[i]; newPositionInfo[i] _ lineInfo.positionInfo[i]; ENDLOOP; newPositionInfo[oldCharLimit] _ lineInfo.positionInfo[oldCharLimit]; lineInfo.charInfo _ newCharInfo; lineInfo.positionInfo _ newPositionInfo; }; ExpandFormats: PROC [lineInfo: LineInfo] = { oldFormatLimit: NAT _ lineInfo.formatInfo.maxLength; formatLimit: NAT _ MIN[oldFormatLimit + oldFormatLimit/3 + 1, LAST[FormatNumber]]; newFormatInfo: FormatInfo _ NEW[FormatInfoRec[formatLimit]]; IF formatLimit <= oldFormatLimit THEN ERROR TiogaDocument.fatalTiogaError; FOR i: NAT IN [0..oldFormatLimit) DO newFormatInfo[i] _ lineInfo.formatInfo[i]; ENDLOOP; lineInfo.formatInfo _ newFormatInfo; }; ScratchRefs: TYPE = RECORD [ ropeReader: RopeReader.Ref, looksReader: LooksReader.Ref, charStyle: NodeStyle.Ref, tabStyle: NodeStyle.Ref ]; scratchRefsReleased: CONDITION; scratchRefsAvailable: BOOLEAN _ FALSE; scratchRefs: ScratchRefs; scratchRefsAllocCount: INT _ InitScratchRefs[333]; scratchRefsWaitCount: INT _ 0; -- stats only InitScratchRefs: ENTRY PROC [timeout: Process.Milliseconds] RETURNS [initialCount: INT _ 1] = TRUSTED { scratchRefs _ [RopeReader.Create[], LooksReader.Create[], NodeStyle.Create[], NodeStyle.Create[]]; scratchRefsAvailable _ TRUE; Process.InitializeCondition[@scratchRefsReleased, Process.MsecToTicks[timeout]]; }; AllocScratchRefs: ENTRY PROC RETURNS [allocated: ScratchRefs] = { IF NOT scratchRefsAvailable THEN { scratchRefsWaitCount _ scratchRefsWaitCount + 1; WAIT scratchRefsReleased }; IF scratchRefsAvailable THEN {scratchRefsAvailable _ FALSE; allocated _ scratchRefs; scratchRefs _ [NIL, NIL, NIL, NIL]} ELSE {allocated _ [RopeReader.Create[], LooksReader.Create[], NodeStyle.Create[], NodeStyle.Create[]]; scratchRefsAllocCount _ scratchRefsAllocCount + 1}; }; ReleaseScratchRefs: ENTRY PROC [allocated: ScratchRefs] = { IF NOT scratchRefsAvailable THEN { scratchRefs _ allocated; scratchRefsAvailable _ TRUE; NOTIFY scratchRefsReleased; }; }; GetFont: PROC [style: NodeStyle.Ref] RETURNS [font: UnifiedFonts.FONT] = TRUSTED { face: NodeStyle.FontFace _ NodeStyle.GetFontFace[style]; fontName: REF TEXT _ RefText.ObtainScratch[100]; s: REF TEXT _ RefText.ObtainScratch[60]; NameSymbolTable.FromName[NodeStyle.GetFontFamily[style], s]; RefText.Append[to: fontName, from: "Xerox/PressFonts/"]; RefText.Append[to: fontName, from: s]; s.length _ 0; RefText.Append[to: s, from: "/MRR"]; IF face=Bold OR face=BoldItalic THEN s[1] _ 'B; IF face=Italic OR face=BoldItalic THEN s[2] _ 'I; RefText.Append[to: fontName, from: s]; font _ UnifiedFonts.Create[fontName: fontName, transformation: [scale: NodeStyle.GetFontSize[style]], deviceType: $Screen]; RefText.ReleaseScratch[fontName]; RefText.ReleaseScratch[s]; }; breakpointCount, dummy: INT _ 0; InitFalse: TYPE = BOOLEAN _ FALSE; breakOn: ARRAY [0..5] OF InitFalse; SetBreak: PROC [b: NAT] = { breakOn[b] _ TRUE; breakpointCount _ breakpointCount + 1; }; ClearBreaks: PROC = { breakOn _ [FALSE, FALSE, FALSE, FALSE, FALSE, FALSE]; breakpointCount _ 0; }; BreakPoint: PROC [b: NAT] = { IF breakpointCount>0 THEN { breakpointCount _ breakpointCount - 1; IF breakOn[b] THEN dummy _ dummy + 1; }; }; FormatLine: PUBLIC PROC [ self: LineInfo, tdd: TiogaDocumentData, node: TextEdit.RefTextNode, startOffset: TextEdit.Offset, nodeStyle: NodeStyle.Ref, lineWidth: Scaled.Value, forPaint: BOOLEAN _ FALSE, doLigsAndKern: BOOLEAN _ FALSE ] = {scratch: ScratchRefs _ AllocScratchRefs[]; {OPEN scratch; realExtraIndent: REAL = ( IF startOffset = 0 THEN nodeStyle.GetFirstIndent[] ELSE nodeStyle.GetBodyIndent[] ); extraIndent: Scaled.Value = Scaled.FromReal[realExtraIndent]; leftIndent: Scaled.Value = Scaled.FromReal[ nodeStyle.GetLeftIndent[] + realExtraIndent ]; rightIndent: Scaled.Value = Scaled.FromReal[ nodeStyle.GetRightIndent[] ]; trimmedLineWidth: Scaled.Value = lineWidth.MINUS[leftIndent].MINUS[rightIndent]; fontWidths: UnifiedFonts.WidthArray; char: CHAR; curFormatNumber: FormatNumber; width: Scaled.Value; prevLooks: TiogaLooks.Looks _ TiogaLooks.allLooks; looks: TiogaLooks.Looks _ TiogaLooks.noLooks; breakIndex: CharNumber _ 0; endX, breakX: Scaled.Value _ Scaled.zero; lineFormatting: NodeStyle.LineFormatting; highWaterChars: NAT _ self.charInfo.maxLength; tabStop: NodeStyle.TabStop; tabNumber: NAT _ 0; -- tells us which tab stop to use tabLoc, tabStart, tabWidth, tabTextStart: CARDINAL; prevTabLooks: TiogaLooks.Looks _ TiogaLooks.allLooks; tabCharLooks: TiogaLooks.Looks _ TiogaLooks.noLooks; tabAlignment: NodeStyle.TabAlign; tabAlignmentChar: CHAR; doingTab: BOOL _ FALSE; level: INTEGER _ 0; maxLevel: INTEGER = tdd.clipLevel; levelClipping: BOOL _ maxLevel < TiogaDocument.maxClip; NewFormattingLooks: PROC = { realVShift: REAL; new: FormatInfoEntry; FOR f: FormatNumber IN [0..MIN[5, self.formatInfo.length]) DO IF self.formatInfo[f].looks = looks THEN { curFormatNumber _ f; fontWidths _ self.formatInfo[f].font.GetWidthArray[minimum: Scaled.zero, undefined: Scaled.zero]; prevLooks _ looks; RETURN }; ENDLOOP; NodeStyle.Copy[dest: charStyle, source: tabStyle]; IF looks#TiogaLooks.noLooks THEN NodeStyle.ApplyLooks[charStyle, looks]; new.font _ GetFont[charStyle]; new.looks _ looks; new.underlining _ NodeStyle.GetUnderlining[charStyle]; new.strikeout _ NodeStyle.GetStrikeout[charStyle]; realVShift _ NodeStyle.GetVShift[charStyle]; new.vShift _ Scaled.FromReal[realVShift]; self.ymax _ MAX[self.ymax, CeilingI[new.font.fontBoundingBox.ymax + realVShift]]; self.ymin _ MIN[self.ymin, FloorI[new.font.fontBoundingBox.ymin + realVShift]]; fontWidths _ new.font.GetWidthArray[minimum: Scaled.zero, undefined: Scaled.zero]; prevLooks _ looks; curFormatNumber _ self.formatInfo.length; IF curFormatNumber>=self.formatInfo.maxLength THEN ExpandFormats[self]; self.formatInfo[curFormatNumber] _ new; self.formatInfo.length _ self.formatInfo.length + 1; }; self.startPos _ [node, startOffset]; NodeStyle.Copy[dest: tabStyle, source: nodeStyle]; ropeReader.SetPosition[TextEdit.GetRope[node], startOffset]; looksReader.SetPosition[TextEdit.GetRuns[node], startOffset]; self.ymax _ FIRST[INTEGER]; self.ymin _ LAST[INTEGER]; self.break _ wrap; self.nChars _ LAST[CharNumber]; self.formatInfo.length _ 0; FOR curIndex: CharNumber IN [0..LAST[CharNumber]) DO char _ RopeReader.Get[ropeReader ! RopeReader.ReadOffEnd => {self.break _ eon; self.nChars _ curIndex; EXIT}]; IF curIndex>=highWaterChars THEN {ExpandChars[self]; highWaterChars _ self.charInfo.maxLength}; IF (looks_LooksReader.Get[looksReader])#prevLooks THEN NewFormattingLooks[]; width _ fontWidths[char]; SELECT char FROM Ascii.SP => {breakIndex _ curIndex + 1; breakX _ endX.PLUS[width]}; Ascii.TAB => { }; ENDCASE => { }; self.charInfo[curIndex] _ [char: char, formatNumber: curFormatNumber, width: width]; self.positionInfo[curIndex] _ endX.Round[]; endX _ endX.PLUS[width]; IF char=Ascii.CR THEN {self.break _ cr; self.nChars _ curIndex + 1; EXIT}; IF endX.GREATER[trimmedLineWidth] THEN { IF breakIndex>0 THEN {self.nChars _ breakIndex; endX _ breakX} ELSE IF curIndex>0 THEN {self.nChars _ curIndex; endX _ endX.MINUS[width]} ELSE {self.nChars _ 1}; EXIT; }; ENDLOOP; self.positionInfo[self.nChars] _ self.xmax _ endX.Round[]; self.xmin _ 0; self.xOffset _ SELECT nodeStyle.GetLineFormatting[] FROM FlushLeft, Justified => leftIndent, FlushRight => leftIndent.PLUS[trimmedLineWidth].MINUS[endX], Centered => leftIndent.PLUS[trimmedLineWidth.MINUS[endX].Scale[-1]], ENDCASE => ERROR; self.nextPos _ self.startPos; self.nextPos.where _ self.nextPos.where + self.nChars; }; FOR i: NAT IN [1..self.nChars] DO IF self.positionInfo[i]<=self.positionInfo[i-1] THEN self.positionInfo[i]_self.positionInfo[i-1]+1; ENDLOOP; ReleaseScratchRefs[scratch]; BreakPoint[1]; }; Paint: PUBLIC PROC [lineInfo: LineInfo, context: Graphics.Context, whiten: BOOLEAN] = TRUSTED { i: NAT _ 0; zero: Scaled.Value _ Scaled.zero; x, y: REAL; BreakPoint[2]; [x, y] _ Graphics.GetCP[context]; IF whiten THEN { Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, [x+lineInfo.xmin, y+lineInfo.ymin, x+lineInfo.xmax, y+lineInfo.ymax]]; }; WHILE ilineInfo.nChars THEN {x _ LAST[INTEGER]; width _ 0} ELSE IF i=lineInfo.nChars THEN {x _ lineInfo.positionInfo[i] + lineInfo.xOffset.Round[]; width _ 0} ELSE { x _ lineInfo.positionInfo[i]; width _ lineInfo.positionInfo[i+1] - x; x _ x + lineInfo.xOffset.Round[]; }; }; }; END. :TiogaFormatImpl.mesa, Written by S. McGregor Edited by McGregor, March 8, 1983 4:22 pm Edited by Paxton, December 20, 1982 4:38 pm Edited by Plass, March 2, 1983 11:44 am Avoid having unneeded REFs hanging around. Expands the charInfo and positionInfo sequences by about 34% Expands the formatInfo sequence by about 34% This scratch storage will will normally be allocated only once, but in exceptional circumstances a new batch may be needed; Uses the cached readers if they are available or become available after a short wait; otherwise allocates a new batch. Safe place to set a breakpoint when not world-swap debugging Formatting constants for the line: Basic formatting variables: State info for formatting tabs: Level clipping info: First a quick look through the first few formats to see if it is a common one. Κ ˜Jšœ,™,Jšœ)™)Jšœ+™+Jšœ'™'unitšΟk ˜ JšœΆ˜Ά—šœ ˜Jšœ…˜ŒJšœ ˜Jšœ œ ˜š Οnœ œœœœœ˜;Jš œœœœœ˜5Jšœ˜—š žœ œœœ œœ˜FJš œœœœœ˜3Jšœ˜—Kšœ/œ˜3š žœœœœœ˜šœ"™"šœœ˜Jšœœ˜2Jšœ˜J˜—Jšœ=˜=šœ+˜+Jšœ+˜+J˜—šœ,˜,Jšœ˜J˜—Jšœ+œ œ˜P—™Jšœ$˜$Jšœœ˜ Jšœ˜Jšœ˜Jšœ2˜2Jšœ-˜-Jšœ˜Jšœ)˜)Jšœ)˜)Jšœœ˜.—™J˜Jšœ œŸ!˜5Jšœ*œ˜3Jšœ5˜5Jšœ4˜4Jšœ!˜!Jšœœ˜Jšœ œœ˜—™Jšœœ˜Jšœ œ˜"Jšœœ$˜7—šžœœ˜Jšœ œ˜Jšœ˜J™Nšœœœ˜=šœ"œ˜*Jšœ˜Jšœa˜aJ˜Jš˜Jšœ˜—Jšœ˜—Jšœ2˜2Jšœœ(˜HJšœ˜J˜Jšœ6˜6Jšœ2˜2Jšœ,˜,Jšœ)˜)Jšœ œB˜QJšœ œ@˜OJšœR˜RJ˜Jšœ)˜)Jšœ+œ˜GJšœ'˜'Jšœ4˜4Jšœ˜—Kšœ$˜$Jšœ2˜2Jšœ<˜Jšœœ œ&œ˜JJšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ:˜:Jšœ˜šœœ˜8Jšœ#˜#Jšœœœ˜φ