-- ALEText.mesa -- Edited by Sweet, September 29, 1980 3:03 PM DIRECTORY ALEOps, AltoDefs, Ascii, BitBltDefs, Inline, Storage, StreamDefs, String, Table, UserTerminal, Window, WindowFont, WindowOps; ALEText: PROGRAM IMPORTS ALEOps, BitBlt: BitBltDefs, Inline, Storage, StreamDefs, String, Table, UserTerminal, Window, WindowFont, WindowOps EXPORTS ALEOps = BEGIN OPEN ALEOps; -- variables for building the symbol string ssw: PRIVATE Table.Index; -- tables defining the current symbol table hashVec: ARRAY HVIndex OF HTIndex; ptb, ltb: Table.Base; -- used for writing out the file hrb, vrb: Table.Base; htb: Table.Base; ssb: STRING; ht: DESCRIPTOR FOR ARRAY HTIndex OF HTRecord; lbb: Table.Base; initialized: BOOLEAN ← FALSE; pictureChanged: PUBLIC BOOLEAN ← FALSE; UpdateBases: PUBLIC Table.Notifier = BEGIN htb ← base[htType]; ht ← DESCRIPTOR[htb, LENGTH[ht]]; ssb ← LOOPHOLE[base[ssType], STRING]; lbb ← base[lbType]; ptb ← base[ptType]; ltb ← base[ltType]; hrb ← base[hrType]; vrb ← base[vrType]; END; ResetHash, InitHash: PUBLIC PROC = BEGIN IF initialized THEN Finalize[]; hashVec ← ALL[HTNull]; ht ← DESCRIPTOR[NIL, 0]; Table.AddNotify[UpdateBases]; ssw ← Table.Allocate[ssType, SIZE[StringBody]] + SIZE[StringBody]; ssb↑ ← StringBody[length:0, maxlength:0, text:]; IF AllocateHash[] # HTNull THEN ERROR; initialized ← TRUE; END; Finalize: PROC = {initialized ← FALSE; Table.DropNotify[UpdateBases]}; SubString: TYPE = String.SubString; CharsPerWord: PRIVATE CARDINAL = AltoDefs.CharsPerWord; EnterString: PROC [s: SubString] RETURNS [hti: HTIndex] = { OPEN String; hvi: HVIndex = HashValue[s]; desc: String.SubStringDescriptor; offset, length, nw: CARDINAL; ssi: Table.Index; FOR hti ← hashVec[hvi], ht[hti].link UNTIL hti = HTNull DO SubStringForHash[@desc, hti]; IF String.EqualSubStrings[s, @desc] THEN RETURN [hti]; ENDLOOP; offset ← ssb.length; length ← s.length; nw ← (offset+length+(CharsPerWord-1) - ssb.maxlength)/CharsPerWord; IF nw # 0 THEN { IF (ssi ← Table.Allocate[ssType, nw]) # ssw THEN ERROR; ssw ← ssw + nw; ssb↑ ← StringBody[ length: ssb.length, maxlength: ssb.maxlength + nw*CharsPerWord, text: ]}; String.AppendSubString[ssb, s]; hti ← AllocateHash[]; ht[hti].link ← hashVec[hvi]; hashVec[hvi] ← hti; RETURN}; AllocateHash: PRIVATE PROC RETURNS [HTIndex] = { hti: HTIndex = LENGTH[ht]; [] ← Table.Allocate[htType, SIZE[HTRecord]]; ht ← DESCRIPTOR[htb, LENGTH[ht]+1]; ht[hti] ← HTRecord[ anyInternal: FALSE, anyPublic: FALSE, link: HTNull, ssIndex: ssb.length]; RETURN [hti]}; HashBlock: PROC RETURNS [base: POINTER, length: CARDINAL] = { base ← BASE[hashVec]; length ← LENGTH[hashVec]; RETURN}; HashValue: PROC [s: SubString] RETURNS [HVIndex] = { CharBits: PROC [CHARACTER, WORD] RETURNS [WORD] = LOOPHOLE[Inline.BITAND]; Mask: WORD = 337B; -- masks out ASCII case shifts n: CARDINAL = s.length; b: STRING = s.base; v: WORD; v ← CharBits[b[s.offset], Mask]*177B + CharBits[b[s.offset+(n-1)], Mask]; RETURN [Inline.BITXOR[v, n*17B] MOD LENGTH[hashVec]]}; SubStringForHash: PROC [s: SubString, hti: HTIndex] = { s.base ← ssb; IF hti = HTNull THEN s.offset ← s.length ← 0 ELSE s.length ← ht[hti].ssIndex - (s.offset ← ht[hti-1].ssIndex)}; -- labels AllLabels: PUBLIC PROC [action: LabelScan] RETURNS [lb: LBIndex] = BEGIN labelTableSize: CARDINAL = Table.Bounds[lbType].size; FOR lb ← FIRST[LBIndex], lb + SIZE[Label] WHILE LOOPHOLE[lb, CARDINAL] < labelTableSize DO IF ~lbb[lb].free AND action[lb, @lbb[lb]] THEN RETURN; ENDLOOP; RETURN[LBNull] END; AllocateLabel: PROC RETURNS [lb: LBIndex] = BEGIN IF (lb ← header.freeLabel) # LBNull THEN BEGIN header.freeLabel ← lbb[LOOPHOLE[header.freeLabel, FNIndex]].next; RETURN END; lb ← Table.Allocate[lbType, SIZE[Label]]; END; FreeLabel: PROC [lb: LBIndex] = BEGIN lbb[LOOPHOLE[lb, FNIndex]] ← [next: header.freeLabel]; header.freeLabel ← lb; END; DeleteLabel: PUBLIC PROC [lb: LBIndex] = BEGIN Window.InvalidateBox[pictureWindow, BoxForLabel[lb]]; IF lbb[lb].selected THEN UnSelChainLabel[lb]; FreeLabel[lb]; END; PosOfLabel: PUBLIC PROC [lb: LBIndex] RETURNS [APosition] = {RETURN [lbb[lb].pos]}; DrawLabel: PUBLIC PROC [s: STRING, pos: APosition] = BEGIN desc: String.SubStringDescriptor ← [base:s, offset: 0, length: s.length]; lb: LBIndex; hti: HTIndex = EnterString[@desc]; ClearSelections[]; lb ← InsertLabel[hti, pos, state.currentFont, state.currentLabelMode]; header.selectedLabels ← lb; PaintLabel[lb]; END; SSDrawLabel: PUBLIC PROC [ss: String.SubString, pos: APosition, font: FontSize, mode: LabelMode] = BEGIN lb: LBIndex; hti: HTIndex = EnterString[ss]; lb ← InsertLabel[hti, pos, font, mode ]; lbb[lb].selected ← FALSE; PaintLabel[lb]; END; InsertLabel: PUBLIC PROC [hti: HTIndex, pos: APosition, font: FontSize, mode: LabelMode] RETURNS [lb: LBIndex] = BEGIN lb ← AllocateLabel[]; lbb[lb] ← [hti: hti, pos: pos, thread: LBNull, font: font, mode: mode]; END; BoxForLabel: PUBLIC PROC [lb: LBIndex] RETURNS [Window.Box] = BEGIN desc: String.SubStringDescriptor; place: Window.Place = PicturePlace[lbb[lb].pos]; w, h: INTEGER; font: WindowFont.Handle = IF lbb[lb].font = small THEN smallFont ELSE largeFont; SubStringForHash[@desc, lbb[lb].hti]; w ← 0; FOR i: CARDINAL IN [desc.offset..desc.offset+desc.length) DO w ← w + WindowFont.CharWidth[desc.base[i], font]; ENDLOOP; h ← WindowFont.FontHeight[font]; IF lbb[lb].mode = landscape THEN RETURN [[[x: place.x, y: place.y-w], [w: h, h: w]]] ELSE RETURN [[place, [w: w, h: h]]] END; SubStringForLabel: PUBLIC PROC [ss: String.SubString, lb: LBIndex] = BEGIN SubStringForHash[ss, lbb[lb].hti]; END; PaintLabel: PUBLIC PROC [lb: LBIndex] = BEGIN IF lbb[lb].mode = landscape THEN PaintLandscapeLabel[lb] ELSE PaintPortraitLabel[lb]; END; PaintPortraitLabel: PROC [lb: LBIndex] = BEGIN font: WindowFont.Handle = IF lbb[lb].font = small THEN smallFont ELSE largeFont; desc: String.SubStringDescriptor; place: Window.Place = PicturePlace[lbb[lb].pos]; SubStringForHash[@desc, lbb[lb].hti]; [] ← Window.DisplaySubstring[ font: font, window: pictureWindow, ss: @desc, bbop: replace, place: place, source: IF lbb[lb].selected THEN complement ELSE block]; END; PaintLandscapeLabel: PROC [lb: LBIndex] = BEGIN font: WindowFont.Handle = IF lbb[lb].font = small THEN smallFont ELSE largeFont; desc: String.SubStringDescriptor; place: Window.Place ← PicturePlace[lbb[lb].pos]; box: Window.Box = BoxForLabel[lb]; height: INTEGER ← WindowFont.FontHeight[font]; i: CARDINAL ← 0; OneChar: PROC [h: Window.Handle] RETURNS [Window.Box, INTEGER] = BEGIN width: INTEGER; ch: CHARACTER; IF i = desc.length THEN RETURN [Window.NullBox, 0]; ch ← desc.base[desc.offset+i]; IF ch ~IN [font.min..font.max] THEN ch ← font.max+1; width ← font.width[ch]; place.y ← place.y - width; i ← i+1; RETURN [ [place, [w: height, h: width]], (ch-font.min)*height]; END; SubStringForHash[@desc, lbb[lb].hti]; Window.Trajectory[ window: pictureWindow, box: box, proc: OneChar, source: IF font = smallFont THEN smallLandBits ELSE largeLandBits, wpl: IF font = smallFont THEN smallLandRaster ELSE largeLandRaster, bbop: replace, bbsource: IF lbb[lb].selected THEN complement ELSE block]; END; -- I/O stuff Rubout: PUBLIC SIGNAL = CODE; keyStream: StreamDefs.KeyboardHandle = StreamDefs.GetDefaultKey[]; ReadChar: PUBLIC PROC RETURNS [CHARACTER] = {RETURN[keyStream.get[keyStream]]}; textLine: STRING ← [80]; overflow: CARDINAL ← 0; tdPlace: Window.Place; ClearText: PUBLIC PROC = BEGIN textLine.length ← overflow ← 0; Window.DisplayWhite[feedbackWindow, textBox]; tdPlace ← textBox.place; END; PaintText: PUBLIC PROC = BEGIN place: Window.Place; place ← Window.DisplayString[ window: feedbackWindow, s: textLine, place: textBox.place]; IF place.x < textBox.place.x + textBox.dims.w THEN Window.DisplayWhite[ feedbackWindow, [place, [ w: (textBox.place.x + textBox.dims.w) - place.x, h: textBox.dims.h]]]; END; OutChar: PUBLIC PROC [c: CHARACTER] = BEGIN IF textLine.length = textLine.maxlength THEN overflow ← overflow + 1 ELSE BEGIN textLine[textLine.length] ← c; textLine.length ← textLine.length + 1; tdPlace ← Window.DisplayCharacter[ window: feedbackWindow, char: c, place: tdPlace]; END; END; OutString: PUBLIC PROC [s: STRING] = {FOR i: CARDINAL IN [0..s.length) DO OutChar[s[i]]; ENDLOOP}; BackupChar: PROC [c: CHARACTER] = BEGIN IF overflow > 0 THEN overflow ← overflow - 1 ELSE IF textLine.length > 0 THEN BEGIN cw: INTEGER; textLine.length ← textLine.length - 1; tdPlace.x ← tdPlace.x - (cw ← WindowFont.CharWidth[c]); Window.DisplayWhite[ feedbackWindow, [tdPlace, [w: cw, h: textBox.dims.h]]]; END; END; ReadString: PUBLIC PROC [s: STRING] = {ReadMoreString[s, ReadChar[], OutChar, BackupChar]}; labelString: STRING ← [100]; CollectLabel: PUBLIC PROC [c: CHARACTER, pos: APosition] = BEGIN IF state.currentLabelMode = landscape THEN CollectLandscapeLabel[c, pos] ELSE CollectPortraitLabel[c, pos]; END; CollectPortraitLabel: PROC [c: CHARACTER, pos: APosition] = BEGIN ENABLE UNWIND => GiveBackKeys[]; start: Window.Place = PicturePlace[pos]; lbPlace: Window.Place ← start; xMax: INTEGER ← start.x; font: WindowFont.Handle = IF state.currentFont = small THEN smallFont ELSE largeFont; h: INTEGER = WindowFont.FontHeight[font]; Out: PROC [ch: CHARACTER] = BEGIN lbPlace ← Window.DisplayCharacter[ font: font, window: pictureWindow, place: lbPlace, char: ch]; xMax ← MAX[lbPlace.x, xMax]; END; Back: PROC [ch: CHARACTER] = BEGIN cw: INTEGER ← WindowFont.CharWidth[ch, font]; lbPlace.x ← lbPlace.x - cw; Window.DisplayWhite[pictureWindow, [lbPlace, [w: cw, h: h]]]; END; ReadMoreString[labelString, c, Out, Back ! Rubout => {IF xMax # start.x THEN Window.InvalidateBox[ pictureWindow, [start, [w: xMax-start.x, h: h]]]; GO TO done}]; IF xMax # start.x THEN {Window.InvalidateBox[pictureWindow, [start, [w: xMax-start.x, h: h]]]; Window.ValidateTree[pictureWindow]}; IF labelString.length # 0 THEN DrawLabel[labelString, pos]; GO TO done; EXITS done => {ClearText[]; GiveBackKeys[]}; END; CharBox: TYPE = ARRAY [0..32) OF PACKED ARRAY [0..32) OF BOOLEAN; smallLandRaster: CARDINAL; smallLandBits: POINTER; largeLandRaster: CARDINAL; largeLandBits: POINTER; SetupLand: PUBLIC PROC = BEGIN portBM, landBM, whiteBM: POINTER TO CharBox; bbsp1, bbsp2: POINTER; bbP: BitBlt.BBptr; bbP2: BitBlt.BBptr = BitBlt.AlignedBBTable[ bbsp1 ← Storage.Node[ SIZE[BitBlt.BBTableSpace]]]; GetLandChar: PROCEDURE [char: CHARACTER, font: WindowFont.Handle, height: CARDINAL] RETURNS [width: CARDINAL] = BEGIN -- font known to be locked in MDS portBM↑ ← whiteBM↑; width ← font.width[char]; bbP.sbca ← Inline.LowHalf[font.bitmap]; bbP.sbmr ← font.raster; bbP.slx ← WindowOps.XInSegment[char, font]; bbP.dw ← width; bbP.dh ← height; BitBlt.BITBLT[bbP]; landBM↑ ← whiteBM↑; FOR i: CARDINAL IN [0..width) DO FOR j: CARDINAL IN [0..height) DO IF portBM[j][i] THEN landBM[width-i][j] ← TRUE; ENDLOOP; ENDLOOP; END; portBM ← Storage.Node[SIZE[CharBox]]; landBM ← Storage.Node[SIZE[CharBox]]; whiteBM ← Storage.Node[SIZE[CharBox]]; whiteBM↑ ← ALL[ALL[FALSE]]; bbP ← BitBlt.AlignedBBTable[ bbsp2 ← Storage.Node[ SIZE[BitBlt.BBTableSpace]]]; bbP↑ ← [ sourcetype: block, function: replace, unused: 0, dbmr: 2, dlx: 0, dty: 0, dw:, dh:, sbmr:, slx:, sty: 0, dbca: portBM, sbca:, gray0:, gray1:, gray2:, gray3:]; smallLandRaster ← ((smallFont.max-smallFont.min+2)*smallFont.height + 15)/16; smallLandBits ← Storage.Node[smallLandRaster * smallFont.maxWidth]; bbP2↑ ← [ sourcetype: block, function: replace, unused: 0, dbmr: smallLandRaster, dlx: 0, dty: 0, dw: smallFont.height, dh:, sbmr: 2, slx: 0, sty: 0, dbca: smallLandBits, sbca: landBM, gray0:, gray1:, gray2:, gray3:]; FOR c: CHARACTER IN [smallFont.min..smallFont.max + 1] DO width: INTEGER ← GetLandChar[c, smallFont, smallFont.height]; bbP2.dh ← width; IF width # 0 THEN BitBlt.BITBLT[bbP2]; bbP2.dlx ← bbP2.dlx + smallFont.height; ENDLOOP; largeLandRaster ← ((largeFont.max-largeFont.min+2)*largeFont.height + 15)/16; largeLandBits ← Storage.Node[largeLandRaster * largeFont.maxWidth]; bbP2.dbmr ← largeLandRaster; bbP2.dlx ← 0; bbP2.dw ← largeFont.height; bbP2.dbca ← largeLandBits; FOR c: CHARACTER IN [largeFont.min..largeFont.max + 1] DO width: INTEGER ← GetLandChar[c, largeFont, largeFont.height]; bbP2.dh ← width; IF width # 0 THEN BitBlt.BITBLT[bbP2]; bbP2.dlx ← bbP2.dlx + largeFont.height; ENDLOOP; Storage.Free[bbsp1]; Storage.Free[bbsp2]; Storage.Free[portBM]; Storage.Free[landBM]; Storage.Free[whiteBM]; END; CollectLandscapeLabel: PROC [c: CHARACTER, pos: APosition] = BEGIN ENABLE UNWIND => GiveBackKeys[]; start: Window.Place = PicturePlace[pos]; lbPlace: Window.Place ← start; yMin: INTEGER ← start.y; font: WindowFont.Handle = IF state.currentFont = small THEN smallFont ELSE largeFont; bits: POINTER = IF state.currentFont = small THEN smallLandBits ELSE largeLandBits; raster: CARDINAL = IF state.currentFont = small THEN smallLandRaster ELSE largeLandRaster; height: INTEGER = WindowFont.FontHeight[font]; Out: PROC [ch: CHARACTER] = BEGIN width: INTEGER ← WindowFont.CharWidth[ch, font]; offset: INTEGER = IF ch IN [font.min..font.max] THEN ch-font.min ELSE font.max+1-font.min; lbPlace.y ← lbPlace.y - width; Window.DisplayOffsetData[ window: pictureWindow, box: [lbPlace, [w: height, h: width]], data: bits, offset: offset*height, wpl: raster]; yMin ← MIN[lbPlace.y, yMin]; END; Back: PROC [ch: CHARACTER] = BEGIN cw: INTEGER ← WindowFont.CharWidth[ch, font]; Window.DisplayWhite[pictureWindow, [lbPlace, [w: height, h: cw]]]; lbPlace.y ← lbPlace.y + cw; END; ReadMoreString[labelString, c, Out, Back ! Rubout => {IF yMin # start.y THEN Window.InvalidateBox[ pictureWindow, [[x: start.x, y: yMin], [w: height, h: start.y - yMin]]]; GO TO done}]; IF yMin # start.x THEN {Window.InvalidateBox[pictureWindow, [[x: start.x, y: yMin], [w: height, h: start.y - yMin]]]; Window.ValidateTree[pictureWindow]}; IF labelString.length # 0 THEN DrawLabel[labelString, pos]; GO TO done; EXITS done => {ClearText[]; GiveBackKeys[]}; END; ReadMoreString: PROC [s: STRING, c: CHARACTER, out, back: PROC [CHARACTER]] = BEGIN OPEN Ascii; Undraw1: PROC = BEGIN ch: CHARACTER; s.length ← s.length - 1; ch ← s[s.length]; IF s[s.length] < 40C THEN {back['↑]; ch ← 100b+ch}; back[ch]; END; IF c = ESC THEN {FOR i: CARDINAL IN [0..s.length) DO out[s[i]]; ENDLOOP; c ← ReadChar[]} ELSE s.length ← 0; WHILE c # CR DO SELECT c FROM ControlA, ControlH => IF s.length # 0 THEN Undraw1[]; ControlW => BEGIN WHILE s.length > 0 AND s[s.length-1] <= 40C DO Undraw1[]; ENDLOOP; WHILE s.length > 0 AND s[s.length-1] > 40C DO Undraw1[]; ENDLOOP; END; DEL => SIGNAL Rubout; ENDCASE => IF s.length = s.maxlength THEN UserTerminal.BlinkDisplay[] ELSE BEGIN s[s.length] ← c; s.length ← s.length + 1; IF c < 40C THEN {out['↑]; out[100B+c]} ELSE out[c]; END; c ← ReadChar[]; ENDLOOP; END; END.