OldTEditFormatImpl.mesa, Edited by McGregor, January 14, 1983 11:24 am
Edited by Paxton, December 20, 1982 4:38 pm
Edited by Plass, February 1, 1983 10:10 am
DIRECTORY
Environment USING [Long],
LooksReader USING [Create, Get, Ref, SetPosition],
NameSymbolTable USING [RopeFromName],
NodeStyle USING [ApplyLooks, Copy, Create, FontFace, FontUnderlining, GetBodyIndentI, GetFirstIndentI, GetFontFace, GetLeadingI, GetLeftIndentI, GetRightIndentI, GetFontFamily, GetFontSizeI, GetLineFormatting, GetStrikeout, GetTabLeaderSpacingI, GetTabRuleWeightI, GetTabRuleVShiftI, RulesTabCount, RulesTabInfoI, GetTabLocI, GetTabStopsI, GetTopLeadingI, GetUnderlining, GetVShiftI, LineFormatting, Ref, TabAlign, TabStop],
RopeEdit USING [AlphaNumericChar],
RopeReader USING [Create, Get, ReadOffEnd, Ref, SetPosition],
TextEdit USING [GetRope, GetRuns, Offset, RefTextNode, Size],
TextLooks USING [Looks, allLooks, noLooks],
TextNode USING [ForwardClipped, Location, Ref, StepForward],
TEditCompile USING [bitmapWPL, maxLineAscent, maxLineDescent, maxLineHeight],
TEditCompile USING [maxCharsPerLine],
TEditDocument USING [fatalTiogaError, maxClip],
TEditFormat,
UnifiedFonts;
TEditFormatImpl: MONITOR
IMPORTS LooksReader, NameSymbolTable, NodeStyle, RopeEdit, RopeReader, TEditDocument, TextEdit, TextNode
EXPORTS TEditFormat =
BEGIN OPEN TEditFormat;
Lengthen:
PROCEDURE [integer:
INTEGER]
RETURNS [Dimension] =
INLINE {
long: Environment.Long ← [any[low: 0, high: integer]];
RETURN[[long.li]]
};
FormatLine:
PUBLIC
ENTRY
PROC [tdd: TEditDocumentData,
node: TextEdit.RefTextNode,
startOffset: TextEdit.Offset,
nodeStyle: NodeStyle.Ref, lineWidth: Dimension,
buildBitmap, trimEnd:
BOOL ←
FALSE]
RETURNS [TEditFormat.LineInfo ← lineInfo] = BEGIN
font: UnifiedFonts.FONT;
fontWidths: UnifiedFonts.WidthArray;
savedLooks, prevTabLooks: TextLooks.Looks ← TextLooks.allLooks;
looks, tabCharLooks: TextLooks.Looks ← TextLooks.noLooks;
char: CHARACTER;
width: Dimension;
tabStop: NodeStyle.TabStop;
tabNumber: CARDINAL; -- tells us which tab stop to use
tabLoc, tabStart, tabWidth, tabTextStart: CARDINAL;
tabAlignment: NodeStyle.TabAlign;
tabAlignmentChar: CHAR;
endPtr: LONG POINTER TO TEditFormat.CharInfo; -- init below
blankPtr, tabPtr: LONG POINTER TO TEditFormat.CharInfo ← NIL;
offEnd: Dimension = [LAST[INT]];
endOffset, blankOffset: TextEdit.Offset ← startOffset;
currentX: Dimension; -- init below
blankX: Dimension ← offEnd;
extraIndent: Dimension = UnifiedFonts.RealToFixed[(
IF endOffset = 0
THEN NodeStyle.GetFirstIndent[nodeStyle]
ELSE NodeStyle.GetBodyIndent[nodeStyle])];
leftIndent: Dimension = UnifiedFonts.RealToFixed[NodeStyle.GetLeftIndent[nodeStyle] + (
IF endOffset = 0
THEN NodeStyle.GetFirstIndent[nodeStyle]
ELSE NodeStyle.GetBodyIndent[nodeStyle])];
doingTab: BOOL ← FALSE;
level: INTEGER ← 0; -- in case we are doing level clipping
maxLevel: INTEGER = tdd.clipLevel;
levelClipping: BOOL ← maxLevel < TEditDocument.maxClip;
lineFormatting: NodeStyle.LineFormatting;
underlining, strikeout: NodeStyle.FontUnderlining;
vShift: Dimension;
Init:
PROC = {
Initialises variables used in both format and display
tabNumber ← 0;
endPtr ← @(lineInfo.chars[0]);
currentX ← 0;
};
NewFormattingLooks:
PROC = {
NodeStyle.Copy[dest: charStyle, source: tabStyle];
IF looks#TextLooks.noLooks THEN NodeStyle.ApplyLooks[charStyle, looks];
font ← GetFont[charStyle];
IF buildBitmap
THEN
BEGIN
underlining ← NodeStyle.GetUnderlining[charStyle];
strikeout ← NodeStyle.GetStrikeout[charStyle];
vShift ← RealToFixed[NodeStyle.GetVShift[charStyle]];
END;
savedLooks ← looks;
lineInfo.ascent ← MAX[lineInfo.ascent, RealToFixed[font.fontBoundingBox.ymax]];
lineInfo.descent ← MAX[lineInfo.ascent, RealToFixed[-font.fontBoundingBox.ymin]];
fontWidths ← font.GetWidthArray[minimum: Lengthen[1], undefined: Lengthen[3]];
};
FinishFormattingTab:
PROC = {
textWidth: CARDINAL ← currentX-tabTextStart;
distanceToMove: CARDINAL;
alignmentPoint:
CARDINAL ←
SELECT tabAlignment
FROM
FlushRight, Character => currentX, -- move right end of text to tabLoc
Centered => (currentX+tabTextStart)/2, -- move center of text to tabLoc
ENDCASE => ERROR; -- handle FlushLeft elsewhere
IF alignmentPoint >= tabLoc THEN distanceToMove ← 0 -- too much text to fit; leave it alone
ELSE {
distanceToMove ← MIN[tabLoc-alignmentPoint, lineWidth-currentX];
currentX ← currentX+distanceToMove };
tabPtr.width ← tabWidth ← distanceToMove+tabTextStart-tabStart; -- fix-up tab width
tabPtr.tab ← tabStop; -- fix-up tab info
doingTab ← FALSE };
DisplayTab:
PROC =
BEGIN
TabRule:
PROC [weight, vshift:
INTEGER] = {
bltYOffset:
LONG
POINTER =
LOOPHOLE[oneLineBitmap,
LONG
POINTER] +
((maxLineAscent-vshift-weight+1)*bitmapWPL);
ubbp.width ← endPtr.width;
ubbp.dst ← [LOOPHOLE[bltYOffset+(currentX/16)],,currentX MOD 16];
ubbp.height ← MAX[1,weight];
BitBlt.BITBLT[ubbp];
ubbp.height ← 1 };
IF endPtr.underlining=All THEN Underline[];
IF endPtr.strikeout=All THEN Strikeout[];
IF endPtr.tab#
NIL
THEN
WITH ts:endPtr.tab
SELECT
FROM
blank => NULL;
leaders => {
charWidth: CARDINAL ← font.width[ts.char];
spacing:
INTEGER ←
MAX[charWidth, NodeStyle.GetTabLeaderSpacingI[@ts, nodeStyle]];
leaderStart: INTEGER ← currentX;
leaderQuit: INTEGER ← tabStart+endPtr.width-spacing;
src: CARDINAL ← font.xInSegment[ts.char];
extra: CARDINAL ← (spacing-charWidth)/2;
leaderStart ← leaderStart + extra; -- even out the extra space
leaderQuit ← leaderQuit + extra;
cbbp.flags.dstFunc ← or; -- to take care of kerning
cbbp.width ← charWidth;
cbbp.src ← [font.bitmap+(src/16),,src MOD 16];
FOR xx:
INTEGER ← leaderStart, xx+spacing
UNTIL xx > leaderQuit
DO
cbbp.dst ← [LOOPHOLE[bltYOffset+(xx/16)],,xx MOD 16];
BitBlt.BITBLT[cbbp];
ENDLOOP;
cbbp.flags.dstFunc ← null };
rule => {
weight: INTEGER ← NodeStyle.GetTabRuleWeightI[@ts, nodeStyle];
vshift: INTEGER ← NodeStyle.GetTabRuleVShiftI[@ts, nodeStyle];
TabRule[weight, vshift] };
rules => {
FOR i:
INTEGER
IN [0..NodeStyle.RulesTabCount[@ts])
DO
weight, vshift: INTEGER;
[weight, vshift] ← NodeStyle.RulesTabInfoI[@ts, i];
TabRule[weight, vshift];
ENDLOOP };
ENDCASE => ERROR;
END;
IF startOffset > TextEdit.Size[node] THEN RETURN WITH ERROR TEditDocument.fatalTiogaError;
Pass 1: Format the line and fill in lineInfo fields
Init[];
{indent: Dimension ← RealToFixed[NodeStyle.GetRightIndent[nodeStyle]];
lineWidth ← [MAX[INT[0], INT[lineWidth] - indent - leftIndent]]
};
NodeStyle.Copy[dest: tabStyle, source: nodeStyle];
RopeReader.SetPosition[ropeReader, TextEdit.GetRope[node], endOffset];
LooksReader.SetPosition[looksReader, TextEdit.GetRuns[node], endOffset];
lineInfo.ascent ← lineInfo.descent ← [0];
lineInfo.break ← wrap;
THROUGH [0..TEditCompile.maxCharsPerLine)
DO
char ← RopeReader.Get[ropeReader ! RopeReader.ReadOffEnd => {lineInfo.break ← eon; EXIT}];
IF (looks←LooksReader.Get[looksReader])#savedLooks THEN NewFormattingLooks[];
width ← fontWidth[char];
SELECT char
FROM
40C => {blankPtr ← endPtr; blankOffset ← endOffset; blankX ← currentX};
11C =>
BEGIN
-- tab
tabLooks: TextLooks.Looks;
minSpace: CARDINAL;
IF doingTab THEN FinishFormattingTab[]; -- finish previous tab before start this one
endPtr.tab ← NIL; -- clear out old information
tabNumber ← tabNumber+1;
IF tabNumber > nodeStyle.numTabStops
THEN {
IF (tabStop ← nodeStyle.defaultTabStops) #
NIL
THEN {
tabLoc ←
IF nodeStyle.tabStops=
NIL
THEN 0
ELSE
NodeStyle.GetTabLocI[nodeStyle.tabStops.first, nodeStyle];
tabLoc ← tabLoc + (tabNumber-nodeStyle.numTabStops)*
NodeStyle.GetTabLocI[tabStop, nodeStyle] }
ELSE NULL -- calculate tabLoc after establish looks -- }
ELSE {
-- go down list until find the proper tab stop
tabStops: LIST OF NodeStyle.TabStop ← nodeStyle.tabStops;
FOR i:
CARDINAL ← nodeStyle.numTabStops, i-1
UNTIL i=tabNumber
DO
tabStops ← tabStops.rest; ENDLOOP;
tabStop ← tabStops.first;
tabLoc ← NodeStyle.GetTabLocI[tabStop, nodeStyle] };
IF nodeStyle.fixedTabs THEN tabLoc ← tabLoc-leftIndent;
tabLooks ← IF tabStop = NIL THEN TextLooks.noLooks ELSE tabStop.looks;
IF tabLooks # prevTabLooks
THEN {
NodeStyle.Copy[dest: tabStyle, source: nodeStyle];
IF tabLooks#TextLooks.noLooks THEN NodeStyle.ApplyLooks[tabStyle, tabLooks];
prevTabLooks ← tabLooks;
NewFormattingLooks[] }
ELSE IF looks#savedLooks THEN NewFormattingLooks[];
tabCharLooks ← looks;
minSpace ← font.width[40C]; -- this must come after establish looks
IF tabStop=
NIL
THEN {
-- pick value [spaceWidth..tabWidth+spaceWidth) for tab width
maxSpace, toNextTab: CARDINAL;
tabWidth ← NodeStyle.GetTabStopsI[nodeStyle];
maxSpace ← tabWidth*minSpace;
toNextTab ← maxSpace - ((currentX+extraIndent) MOD maxSpace);
width ←
IF toNextTab<minSpace
THEN toNextTab+maxSpace
ELSE toNextTab;
tabLoc ← currentX+width };
tabStart ← currentX; tabPtr ← endPtr; width ← minSpace;
tabAlignment ← IF tabStop=NIL THEN FlushLeft ELSE tabStop.alignment;
IF currentX+minSpace > tabLoc
THEN {
-- we are beyond the tab stop
IF tabStop=
NIL
OR ~tabStop.breakIfPast
THEN {
-- treat like a space
tabWidth ← minSpace }
ELSE -- force line break -- EXIT}
ELSE
IF tabAlignment=FlushLeft
THEN {
-- simple case; finish now
tabWidth ← width ← tabLoc-currentX }
ELSE {
-- will finish after have the text to be positioned
doingTab ← TRUE;
tabAlignmentChar ←
IF tabAlignment=Character THEN tabStop.alignmentChar ELSE 0C;
tabTextStart ← tabStart+width };
END
ENDCASE =>
BEGIN
IF doingTab AND char=tabAlignmentChar AND tabAlignment=Character THEN FinishFormattingTab[];
IF currentX + width >= lineWidth THEN EXIT;
END;
IF buildBitmap
THEN endPtr^ ← [
width: width,
char: char,
strikeout: strikeout,
underlining: underlining,
vShift: vShift,
font: font]
ELSE endPtr.width ← width;
endPtr ← endPtr+SIZE[TEditFormat.CharInfo];
endOffset ← endOffset+1;
currentX ← currentX+width;
IF char=15C THEN {lineInfo.break ← cr; EXIT};
ENDLOOP;
-- we fell out of the loop because we got to the right edge or because
-- we hit a logical end-of-line (return or end-of-node)
------ see if need to finish a tab
IF doingTab THEN FinishFormattingTab[];
IF lineInfo.break=cr OR lineInfo.break=eon OR blankPtr=NIL THEN endPtr.width ← offEnd
ELSE
BEGIN
(blankPtr ← blankPtr+SIZE[TEditFormat.CharInfo]).width ← offEnd;
endOffset ← blankOffset+1;
END;
IF endOffset>=TextEdit.Size[node]
THEN
BEGIN
n: TextNode.Ref;
IF levelClipping THEN [n,level] ← TextNode.ForwardClipped[node,maxLevel,level]
ELSE n ← TextNode.StepForward[node];
lineInfo.nextPos ← [n, 0];
lineInfo.nChars ← TextEdit.Size[node] - startOffset;
END
ELSE
BEGIN
lineInfo.nextPos ← [node, endOffset];
lineInfo.nChars ← endOffset - startOffset;
END;
lineInfo.leading ←
IF startOffset=0
THEN NodeStyle.GetTopLeadingI[nodeStyle]
ELSE NodeStyle.GetLeadingI[nodeStyle];
lineInfo.width ←
IF lineInfo.break=cr
THEN currentX+width
ELSE IF lineInfo.break=eon OR blankX=offEnd THEN currentX ELSE blankX;
lineInfo.indent ←
SELECT (lineFormatting ← NodeStyle.GetLineFormatting[nodeStyle])
FROM
FlushLeft, Justified => leftIndent,
FlushRight => leftIndent + (lineWidth-lineInfo.width),
Centered => leftIndent + (lineWidth-lineInfo.width)/2,
ENDCASE => ERROR;
End of Pass 1
IF ~buildBitmap THEN {lineInfo.bits ← NIL; RETURN} ELSE lineInfo.bits ← formatBitmapRep;
Pass 2: build the line bitmap from the LineInfoRec
Init[];
ClearLineBuffer[];
font ← NIL;
WHILE (width𡤎ndPtr.width)#offEnd
DO
-- display each formatted character in buffer
IF endPtr.font#font THEN NewDisplayLooks[]; -- sets blt info and font ← endPtr.font
SELECT (char𡤎ndPtr.char)
FROM
40C, 15C =>
BEGIN
-- blanks and returns
IF endPtr.underlining=All THEN Underline[];
IF endPtr.strikeout=All THEN Strikeout[];
END;
11C => DisplayTab[]; -- tabs
ENDCASE =>
BEGIN
-- normal characters
IF
NOT char
IN [font.min..font.max]
THEN
BEGIN
-- illegal char for font
black: CARDINAL ← 177777B;
oldsrcDesc: INTEGER = cbbp.srcDesc.srcBpl;
cbbp.height ← font.ascent;
cbbp.width ← width-2; -- hack some white space between bad chars
cbbp.dst ← [LOOPHOLE[bltYOffset+((currentX+1)/16)],,(currentX+1) MOD 16];
cbbp.flags.gray ← TRUE;
cbbp.srcDesc.gray ← [0, 0, 0, 0];
cbbp.src ← [@black,,0];
BitBlt.BITBLT[cbbp];
cbbp.height ← font.height;
cbbp.flags.gray ← FALSE;
cbbp.srcDesc.srcBpl ← oldsrcDesc;
END
ELSE
BEGIN
src: CARDINAL = font.xInSegment[char];
cbbp.width ← width;
cbbp.src ← [font.bitmap+(src/16),,src MOD 16];
cbbp.dst ← [LOOPHOLE[bltYOffset+(currentX/16)],,currentX MOD 16];
BitBlt.BITBLT[cbbp];
END;
SELECT endPtr.underlining
FROM
All, Visible => Underline[];
LettersAndDigits => IF RopeEdit.AlphaNumericChar[char] THEN Underline[];
ENDCASE;
SELECT endPtr.strikeout
FROM
All, Visible => Strikeout[];
LettersAndDigits => IF RopeEdit.AlphaNumericChar[char] THEN Strikeout[];
ENDCASE;
END;
currentX ← currentX+width;
endPtr ← endPtr+SIZE[TEditFormat.CharInfo];
ENDLOOP;
savedAscent ← lineInfo.ascent;
savedDescent ← lineInfo.descent; -- so can clear line buffer next time
lineInfo.yOffset ← maxLineAscent-savedAscent;
IF trimEnd
AND currentX#width
THEN
TrimLineBuffer[lineInfo.width, currentX, lineInfo.ascent, lineInfo.descent];
clear off broken word at end
End of Pass 2
END;
savedAscent: CARDINAL ← maxLineAscent;
savedDescent: CARDINAL ← maxLineDescent;
ClearLineBuffer:
PROC =
INLINE {
IF savedAscent#0
THEN
BEGIN
[] ← RTMicrocode.
LONGZERO[
LOOPHOLE[oneLineBitmap,
LONG
POINTER
TO
UNSPECIFIED] +
((maxLineAscent-savedAscent)*bitmapWPL), (savedAscent+savedDescent)*bitmapWPL];
savedAscent ← savedDescent ← 0;
END;
};
TrimLineBuffer:
PROC [start, end, ascent, descent:
CARDINAL] =
INLINE {
bltYOffset:
LONG
POINTER =
LOOPHOLE[oneLineBitmap, LONG POINTER] + ((maxLineAscent-ascent)*bitmapWPL);
bbbp.height ← ascent+descent;
bbbp.width ← MIN[bitmapWPL*16, end]-start;
bbbp.dst ← [LOOPHOLE[bltYOffset+(start/16)],,start MOD 16];
BitBlt.BITBLT[bbbp];
};
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;
AllocateBitmap:
PROC [nWords:
CARDINAL]
RETURNS [
REF] =
BEGIN
Words: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF CARDINAL];
RETURN[NEW[Words[nWords]]];
END;
-- Main Program
bbbTable, cbbTable, ubbTable: BitBlt.BBTableSpace;
bbbp: BitBlt.BBptr ← BitBlt.AlignedBBTable[@bbbTable]; -- buffer end trimming
cbbp: BitBlt.BBptr ← BitBlt.AlignedBBTable[@cbbTable]; -- chars to line buffer
ubbp: BitBlt.BBptr ← BitBlt.AlignedBBTable[@ubbTable]; -- underline and strikeout
oneLineBitmap:
REF ← AllocateBitmap[bitmapWPL*maxLineHeight];
-- line buffer; magic storage provided by AllocateBitmap
formatBitmapRep:
REF GraphicsOps.BitmapRep ←
NEW[GraphicsOps.BitmapRep ← [
base: oneLineBitmap,
raster: bitmapWPL,
width: bitmapWPL*16,
height: maxLineHeight
]];
lineInfo: TEditFormat.LineInfo ← NEW[TEditFormat.LineInfoRec];
myWhiteWord: CARDINAL ← 0; -- white for erasing bitmap buffers
myBlackWord: CARDINAL ← 177777B; -- black for underlining and strikeout
-- RopeReader and LooksReader Stuff
ropeReader: RopeReader.Ref ← RopeReader.Create[];
looksReader: LooksReader.Ref ← LooksReader.Create[];
charStyle: NodeStyle.Ref ← NodeStyle.Create[];
tabStyle: NodeStyle.Ref ← NodeStyle.Create[];
bbbp.flags.disjoint ← TRUE;
bbbp.flags.disjointItems ← TRUE;
bbbp.flags.gray ← TRUE;
bbbp.srcDesc.gray ← [0, 0, 0, 0];
bbbp.src ← [@myWhiteWord,,0];
bbbp.dstBpl ← bitmapWPL*16;
cbbp.flags.disjoint ← TRUE;
cbbp.flags.disjointItems ← TRUE;
cbbp.dstBpl ← bitmapWPL*16;
ubbp.flags.disjoint ← TRUE;
ubbp.flags.disjointItems ← TRUE;
ubbp.flags.gray ← TRUE;
ubbp.srcDesc.gray ← [0, 0, 0, 0];
ubbp.src ← [@myBlackWord,,0];
ubbp.dstBpl ← bitmapWPL*16;
ubbp.height ← 1;
savedAscent ← maxLineAscent; -- so will be cleared first time through
savedDescent ← maxLineDescent;
END.