DIRECTORY Ascii, Font, Imager, ImagerTransform, LooksReader, NameSymbolTable, NodeStyle, Process, Real, RefText, Rope, RopeReader, TEditDocument, TEditFormat, TextEdit, TextNode, TextLooks, Scaled; TEditFormatImpl: CEDAR MONITOR IMPORTS Font, Imager, ImagerTransform, LooksReader, NameSymbolTable, NodeStyle, Process, Real, RefText, Rope, RopeReader, TEditDocument, TextEdit, TextNode, Scaled EXPORTS TEditFormat = BEGIN OPEN TEditFormat; 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 TEditDocument.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 TEditDocument.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: Font.FONT] = TRUSTED { face: NodeStyle.FontFace _ NodeStyle.GetFontFace[style]; fontName: REF TEXT _ RefText.ObtainScratch[100]; s: REF TEXT _ RefText.ObtainScratch[60]; size: REAL _ NodeStyle.GetFontSize[style]; 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 _ Font.Create[fontName: fontName, transformation: ImagerTransform.Scale[size, size], deviceType: $Screen]; RefText.ReleaseScratch[fontName]; RefText.ReleaseScratch[s]; }; FormatLine: PUBLIC PROC [ self: LineInfo, tdd: TEditDocumentData, 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: Font.WidthArray; char: CHAR; curFormatNumber: FormatNumber; width: Scaled.Value; prevLooks: TextLooks.Looks _ TextLooks.allLooks; looks: TextLooks.Looks _ TextLooks.noLooks; breakIndex: CharNumber _ 0; endX, breakX: Scaled.Value _ Scaled.zero; highWaterChars: NAT _ self.charInfo.maxLength; 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#TextLooks.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]; }; Paint: PUBLIC PROC [lineInfo: LineInfo, context: Imager.Context, whiten: BOOLEAN] = { i: NAT _ 0; Whiten: PROC = { Imager.Move[context]; Imager.MaskRectangle[context, lineInfo.xmin, lineInfo.ymin, lineInfo.xmax-lineInfo.xmin, lineInfo.ymax-lineInfo.ymin]; }; IF whiten THEN Imager.DoSaveAll[context, Whiten]; 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. *TEditFormatImpl.mesa, Edited by McGregor, January 14, 1983 11:24 am Edited by Paxton, December 20, 1982 4:38 pm Edited by Plass, August 12, 1983 10:52 am Edited by Wyatt, October 24, 1983 4:19 pm 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. Formatting constants for the line: Basic formatting variables: lineFormatting: NodeStyle.LineFormatting; State info for formatting tabs: tabStop: NodeStyle.TabStop; tabNumber: NAT _ 0; -- tells us which tab stop to use tabLoc, tabStart, tabWidth, tabTextStart: CARDINAL; prevTabLooks: TextLooks.Looks _ TextLooks.allLooks; tabCharLooks: TextLooks.Looks _ TextLooks.noLooks; tabAlignment: NodeStyle.TabAlign; tabAlignmentChar: CHAR; doingTab: BOOL _ FALSE; Level clipping info: level: INTEGER _ 0; maxLevel: INTEGER = tdd.clipLevel; levelClipping: BOOL _ maxLevel < TEditDocument.maxClip; First a quick look through the first few formats to see if it is a common one. Michael Plass, August 9, 1983 4:42 pm: Converted to Imager. Κ P˜JšœC™CJšœ+™+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šœ0˜0Jšœ+˜+Jšœ˜Jšœ)˜)Jšœ)™)Jšœœ˜.—™J™Jšœ œŸ!™5Jšœ*œ™3Jšœ3™3Jšœ2™2Jšœ!™!Jšœœ™Jšœ œœ™—™Jšœœ™Jšœ œ™"Jšœœ$™7—šžœœ˜Jšœ œ˜Jšœ˜J™Nšœœœ˜=šœ"œ˜*Jšœ˜Jšœa˜aJ˜Jš˜Jšœ˜—Jšœ˜—Jšœ2˜2Jšœœ(˜GJšœ˜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šœœœ˜