<> <> <> <> <> <> DIRECTORY Ascii, Atom, Basics, Beeps, CharDisplays, IO, IOClasses, InputFocus, Menus, MessageWindow, NodeProps, Rope, TiogaMenuOps, TiogaOps, TIPUser, ViewerClasses, ViewerOps; TiogaDisplays: CEDAR PROGRAM IMPORTS Ascii, Atom, Basics, Beeps, CharDisplays, IO, IOClasses, InputFocus, Rope, Menus, MessageWindow, NodeProps, TiogaMenuOps, TiogaOps, TIPUser, ViewerOps = {OPEN CharDisplays; REFTEXT: TYPE = REF TEXT; Viewer: TYPE = ViewerClasses.Viewer; ViewerClass: TYPE = ViewerClasses.ViewerClass; TiogaDisplay: TYPE = REF TiogaDisplayRep; TiogaDisplayRep: TYPE = RECORD [ destroyed: BOOL _ FALSE, root, logNode, arrayNode: TiogaOps.Ref, eatChars, spitChars: IO.STREAM _ NIL, looks: ROPE _ "f", spaces: ROPE _ NIL, lineS: SEQUENCE length: NAT OF Line ]; Line: TYPE = RECORD [ tiogaCount: NAT, key: ATOM]; tiogaClass, tiogaDisplayViewerClass: ViewerClass _ NIL; tiogaDisplayViewerClassFlavor: ATOM _ $TiogaCharDisplay; cdProp: ATOM _ $CharDisplayFromViewer; tiogaCharDisplayClass: CharDisplayClass _ NEW [CharDisplayClassRep _ [ name: "Tioga", Init: TInit, ChangeDetails: SimplyChange, DeleteChar: DeleteChar, TakeChar: TTakeChar, CursorMove: TCursorMove, Line: TLine, ClearTo: ClearTo, ClearAll: ClearAll, SetEmph: SetEmph, Emphasize: Emphasize, SetFont: SetFont, Beep: Beep, Destroyed: Destroyed]]; InitTiogaDisplayClass: PROC = { tdTIP: TIPUser.TIPTable _ TIPUser.InstantiateNewTIPTable["TiogaDisplay.tip"]; menu: Menus.Menu _ Menus.CreateMenu[]; tiogaClass _ ViewerOps.FetchViewerClass[$Text]; tiogaDisplayViewerClass _ NEW [ViewerClasses.ViewerClassRec _ tiogaClass^]; tiogaDisplayViewerClass.notify _ NotifyTiogaDisplay; tiogaDisplayViewerClass.destroy _ NoteTiogaDestruction; tiogaDisplayViewerClass.icon _ typescript; tdTIP.mouseTicks _ MIN[tdTIP.mouseTicks, tiogaClass.tipTable.mouseTicks]; tdTIP.opaque _ FALSE; tdTIP.link _ tiogaClass.tipTable; tiogaDisplayViewerClass.tipTable _ tdTIP; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Find", Find]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Word", Word]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Def", Def]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Position", Position]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Normalize", Normalize]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["PrevPlace", PrevPlace]]; Menus.AppendMenuEntry[menu, Menus.CreateEntry["Reselect", Reselect]]; tiogaDisplayViewerClass.menu _ menu; ViewerOps.RegisterViewerClass[tiogaDisplayViewerClassFlavor, tiogaDisplayViewerClass]; RegClass[tiogaCharDisplayClass]; }; Find: Menus.MenuProc = { v: Viewer _ NARROW[parent]; [] _ TiogaOps.FindText[viewer: v, whichDir: SearchDir[mouseButton], case: NOT shift]; }; Word: Menus.MenuProc = { v: Viewer _ NARROW[parent]; [] _ TiogaOps.FindWord[viewer: v, whichDir: SearchDir[mouseButton], case: NOT shift]; }; Def: Menus.MenuProc = { v: Viewer _ NARROW[parent]; [] _ TiogaOps.FindDef[viewer: v, whichDir: SearchDir[mouseButton], case: NOT shift]; }; SearchDir: PROC [mb: Menus.MouseButton] RETURNS [sd: TiogaOps.SearchDir] = {sd _ SELECT mb FROM red => forwards, yellow => anywhere, blue => backwards, ENDCASE => ERROR}; Position: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.Position[v]}; Normalize: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.Normalize[v]}; PrevPlace: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.PrevPlace[v]}; Reselect: Menus.MenuProc = { v: Viewer _ NARROW[parent]; TiogaMenuOps.Reselect[v]}; NoteTiogaDestruction: PROC [self: Viewer] --ViewerClasses.DestroyProc-- = { cd: CharDisplay = GetDisplay[self]; td: TiogaDisplay = NARROW[cd.otherInstanceData]; IF cd.viewer.destroyed AND NOT td.destroyed THEN { td.destroyed _ TRUE; td.eatChars.Close[TRUE !IO.Error => CONTINUE]; td.spitChars.Close[TRUE !IO.Error => CONTINUE]; }; tiogaClass.destroy[self]; }; NotifyTiogaDisplay: PROC [self: Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- = { cd: CharDisplay = GetDisplay[self]; td: TiogaDisplay = NARROW[cd.otherInstanceData]; WITH input.first SELECT FROM a: ATOM => SELECT a FROM $TDInput => { r: ROPE; ctl, shift, meta: BOOL _ FALSE; FOR input _ input.rest, input.rest WHILE input # NIL DO WITH input.first SELECT FROM R: ROPE => r _ R; t: REFTEXT => r _ Rope.FromRefText[t]; b: ATOM => SELECT b FROM $Ctl => ctl _ TRUE; $Shift => shift _ TRUE; $Meta => meta _ TRUE; ENDCASE => ERROR; ENDCASE => ERROR; ENDLOOP; FOR i: INT IN [0 .. r.Length[]) DO c: CHAR _ r.Fetch[i]; IF NOT shift THEN c _ Ascii.Lower[c]; IF ctl THEN c _ Control[c]; IF meta THEN c _ c + 128; td.eatChars.PutChar[c]; ENDLOOP; RETURN; }; ENDCASE; r: ROPE => { td.eatChars.PutRope[r]; RETURN; }; r: REFTEXT => { td.eatChars.PutText[r]; RETURN; }; ENDCASE; tiogaClass.notify[self, input]; }; Control: PROC [c: CHAR] RETURNS [cc: CHAR] = { d: NAT _ c - 0C; cd: NAT _ Basics.BITAND[d, 31]; cc _ 0C + cd; }; GetDisplay: PROC [self: Viewer] RETURNS [cd: CharDisplay] = INLINE {cd _ NARROW[ViewerOps.FetchProp[viewer: self, prop: cdProp]]}; TInit: PROC [cd: CharDisplay, initData: REF ANY _ NIL] = { td: TiogaDisplay _ NEW [TiogaDisplayRep[cd.det.lines]]; InitDoc: PROC [root: TiogaOps.Ref] = { IF root # td.root THEN ERROR; TiogaOps.PutProp[ n: td.root, name: $StyleDef, value: NodeProps.DoSpecs[$StyleDef, " BeginStyle (Cedar) AttachStyle (look.v) \"video reverse\" {9 pt backgroundAscent 3 pt backgroundDescent 0 backgroundBrightness 1 0 1 textColor} StyleRule EndStyle "] ]; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, 0]]; TiogaOps.Break[]; td.logNode _ TiogaOps.FirstChild[td.root]; td.arrayNode _ TiogaOps.LastChild[td.root]; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, 0]]; TiogaOps.SetLooks["f", caret]; TiogaOps.InsertChar['\n]; FOR i: NAT IN [0 .. cd.det.columns) DO TiogaOps.InsertChar['0 + (i / 10)]; ENDLOOP; TiogaOps.InsertChar['\n]; TiogaOps.SetLooks["fz", caret]; FOR i: NAT IN [0 .. cd.det.columns) DO TiogaOps.InsertChar['0 + (i MOD 10)]; ENDLOOP; TiogaOps.SetLooks["f", caret]; TiogaOps.InsertChar['\n]; FOR l: NAT IN [0 .. cd.det.lines) DO TiogaOps.InsertChar['\n]; td.lineS[l] _ [ tiogaCount: 0, key: Atom.MakeAtom[IO.PutFR["Before%g", IO.int[l]]] ]; ENDLOOP; }; cd.otherInstanceData _ td; cd.viewer _ ViewerOps.CreateViewer[flavor: tiogaDisplayViewerClassFlavor, info: [name: cd.name]]; cd.viewer.tipTable _ tiogaDisplayViewerClass.tipTable; cd.viewer.menu _ Menus.CopyMenu[tiogaDisplayViewerClass.menu]; ViewerOps.AddProp[viewer: cd.viewer, prop: cdProp, val: cd]; td.root _ TiogaOps.ViewerDoc[cd.viewer]; td.logNode _ TiogaOps.FirstChild[td.root]; td.arrayNode _ TiogaOps.LastChild[td.root]; [push: td.eatChars, pull: td.spitChars] _ IOClasses.CreatePipe[]; cd.fromDisplay _ td.spitChars; td.spaces _ " "; FOR i: INT IN [0 .. cd.det.columns) DO td.spaces _ td.spaces.Cat[" "]; ENDLOOP; TiogaOps.CallWithLocks[InitDoc, td.root]; InputFocus.SetInputFocus[cd.viewer]; SetLines[cd, td]; }; Destruction: PROC [cd: CharDisplay, td: TiogaDisplay] RETURNS [destroyed: BOOL] = { IF destroyed _ cd.viewer.destroyed THEN { IF NOT td.destroyed THEN { td.destroyed _ TRUE; td.eatChars.Close[TRUE !IO.Error => CONTINUE]; td.spitChars.Close[TRUE !IO.Error => CONTINUE]; }; }; }; SetLines: PROC [cd: CharDisplay, td: TiogaDisplay, recursed: BOOL _ FALSE] = { IsBegin: PROC [ci: INT] RETURNS [is: BOOL] = INLINE {is _ ci = 0 OR nr.Fetch[ci-1] = '\n}; nr: ROPE; lineNum, prevCI: INT; td.arrayNode _ TiogaOps.LastChild[td.root]; nr _ TiogaOps.GetRope[td.arrayNode]; lineNum _ cd.det.lines; prevCI _ nr.Length[]; FOR ci: INT _ nr.Length[]-1, ci-1 WHILE lineNum > 0 DO IF ci < 0 THEN { IF recursed THEN ERROR; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, 0]]; FOR i: INT IN [0 .. lineNum] DO TiogaOps.InsertChar['\n] ENDLOOP; SetLines[cd, td, TRUE]; EXIT; }; IF IsBegin[ci] THEN { lineNum _ lineNum - 1; TiogaOps.PutTextKey[node: td.arrayNode, where: ci, key: td.lineS[lineNum].key]; td.lineS[lineNum].tiogaCount _ prevCI - ci - 1; prevCI _ ci; }; ENDLOOP; }; DeleteChar: PROC [cd: CharDisplay] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; base: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where; WithLocks: PROC [root: TiogaOps.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, base+cd.col]]; TiogaOps.DeleteNextCharacter[]; td.lineS[cd.line].tiogaCount _ td.lineS[cd.line].tiogaCount - 1; }; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; IF cd.col >= td.lineS[cd.line].tiogaCount THEN RETURN; TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; }; TTakeChar: PROC [cd: CharDisplay, char: CHAR, insert: BOOL _ FALSE] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; base: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where; wasEmpty: BOOL _ td.lineS[cd.line].tiogaCount = 0; WithLocks: PROC [root: TiogaOps.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, base+MIN[cd.col, td.lineS[cd.line].tiogaCount]]]; IF td.lineS[cd.line].tiogaCount < cd.col THEN { TiogaOps.SetLooks[looks: "f", which: caret]; WHILE td.lineS[cd.line].tiogaCount < cd.col DO TiogaOps.InsertChar[' ]; td.lineS[cd.line].tiogaCount _ td.lineS[cd.line].tiogaCount + 1; ENDLOOP; }; IF (NOT insert) AND td.lineS[cd.line].tiogaCount > cd.col THEN { TiogaOps.DeleteNextCharacter[]; td.lineS[cd.line].tiogaCount _ td.lineS[cd.line].tiogaCount - 1}; TiogaOps.SetLooks[looks: td.looks, which: caret]; TiogaOps.InsertChar[char]; td.lineS[cd.line].tiogaCount _ td.lineS[cd.line].tiogaCount + 1; }; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; IF char = '\n THEN char _ char + 128; TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; IF wasEmpty OR cd.col = 0 THEN TiogaOps.PutTextKey[node: td.arrayNode, where: base, key: td.lineS[cd.line].key]; TCursorMove[cd, 0, 1, TRUE]; }; TCursorMove: PROC [cd: CharDisplay, line, col: INT, relative: BOOL _ FALSE, doLine, doCol: BOOL _ TRUE] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; WithLocks: PROC [root: TiogaOps.Ref] = { colDelta: INT; IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF line < cd.det.lines THEN NULL ELSE IF NOT cd.det.scrolls THEN line _ line MOD cd.det.lines ELSE { delta: INT _ MIN[line - (cd.det.lines-1), cd.det.lines]; arrayEnd: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.det.lines-1].key].where + td.lineS[cd.det.lines-1].tiogaCount + 1; topStart: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[0].key].where; topEnd: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[delta-1].key].where + td.lineS[delta-1].tiogaCount; line _ line - delta; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, arrayEnd]]; FOR i: INT IN [0 .. delta) DO TiogaOps.InsertChar['\n] ENDLOOP; FOR i: INT IN [delta .. cd.det.lines) DO td.lineS[i-delta].tiogaCount _ td.lineS[i].tiogaCount; TiogaOps.PutTextKey[ node: td.arrayNode, where: TiogaOps.GetTextKey[td.arrayNode, td.lineS[i].key].where, key: td.lineS[i-delta].key]; ENDLOOP; FOR i: INT IN [cd.det.lines - delta .. cd.det.lines) DO td.lineS[i].tiogaCount _ 0; TiogaOps.PutTextKey[ node: td.arrayNode, where: arrayEnd + i - (cd.det.lines - delta), key: td.lineS[i].key]; ENDLOOP; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.logNode, TiogaOps.GetRope[td.logNode].Length[]]]; TiogaOps.SetSelection[viewer: cd.viewer, start: [td.arrayNode, topStart], end: [td.arrayNode, topEnd], pendingDelete: TRUE, which: secondary]; TiogaOps.ToPrimary[]; }; cd.line _ line; cd.col _ col; colDelta _ cd.col - td.lineS[cd.line].tiogaCount; TiogaOps.SelectPoint[ viewer: cd.viewer, caret: [ td.arrayNode, TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where + MIN[cd.col, td.lineS[cd.line].tiogaCount]] ]; IF colDelta > 0 THEN { wasEmpty: BOOL _ td.lineS[cd.line].tiogaCount = 0; TiogaOps.SetLooks["f", caret]; TiogaOps.InsertRope[td.spaces.Substr[0, colDelta]]; td.lineS[cd.line].tiogaCount _ td.lineS[cd.line].tiogaCount + colDelta; IF wasEmpty THEN SetLines[cd, td]; }; }; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; IF relative THEN {line _ line + cd.line; col _ col + cd.col}; IF NOT doLine THEN line _ cd.line; IF NOT doCol THEN col _ cd.col; IF cd.det.autoMargins THEN { dl: INT _ col / cd.det.columns; line _ line + dl; col _ col - dl * cd.det.columns; WHILE col < 0 DO col _ col + cd.det.columns; line _ line - 1 ENDLOOP; } ELSE col _ MAX[MIN[col, cd.det.columns-1], 0]; IF line < 0 THEN line _ 0; IF cd.det.scrolls AND line >= cd.det.lines THEN { TiogaOps.LockSel[primary]; TiogaOps.LockSel[secondary]; {ENABLE UNWIND => {TiogaOps.UnlockSel[secondary]; TiogaOps.UnlockSel[primary]}; TiogaOps.CallWithLocks[WithLocks, td.root]; }; TiogaOps.UnlockSel[secondary]; TiogaOps.UnlockSel[primary]; } ELSE TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; }; <<>> TLine: PROC [cd: CharDisplay, insert: BOOL] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; endBase: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.det.lines-1].key].where; endEnd: INT _ endBase + td.lineS[cd.det.lines-1].tiogaCount; base: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where; WithLocks: PROC [root: TiogaOps.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; IF insert THEN { TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, endBase], end: [td.arrayNode, endEnd]]; TiogaOps.Delete[]; TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, base]]; TiogaOps.InsertChar['\n]; } ELSE { TiogaOps.SelectPoint[viewer: cd.viewer, caret: [td.arrayNode, endEnd+1]]; TiogaOps.InsertChar['\n]; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, base], end: [td.arrayNode, base + td.lineS[cd.line].tiogaCount]]; TiogaOps.Delete[]; }; }; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; SetLines[cd, td]; }; ClearTo: PROC [cd: CharDisplay, where: Where] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; lastLine: INT _ SELECT where FROM EndOfLine => cd.line, EndOfScreen => cd.det.lines-1, ENDCASE => ERROR; beginBase: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where; endBase: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[lastLine].key].where; WithLocks: PROC [root: TiogaOps.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, beginBase + MIN[cd.col, td.lineS[cd.line].tiogaCount]], end: [td.arrayNode, endBase + td.lineS[lastLine].tiogaCount], pendingDelete: TRUE]; FOR l: INT IN [cd.line .. lastLine] DO TiogaOps.InsertChar['\n]; ENDLOOP; }; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; SetLines[cd, td]; }; ClearAll: PROC [cd: CharDisplay] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; begin: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[0].key].where; end: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.det.lines-1].key].where + td.lineS[cd.det.lines-1].tiogaCount; WithLocks: PROC [root: TiogaOps.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, begin], end: [td.arrayNode, end], pendingDelete: TRUE]; FOR l: INT IN [0 .. cd.det.lines) DO TiogaOps.InsertChar['\n]; ENDLOOP; }; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; TiogaOps.CallWithLocks[WithLocks, td.root]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; SetLines[cd, td]; }; SetEmph: PROC [cd: CharDisplay, emph: Emph, on: BOOL] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; cd.emphs[emph] _ on; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; td.looks _ "f"; IF cd.emphs[underline] THEN td.looks _ td.looks.Cat["z"]; IF cd.emphs[bold] THEN td.looks _ td.looks.Cat["b"]; IF cd.emphs[italic] THEN td.looks _ td.looks.Cat["i"]; IF cd.emphs[inverse] THEN td.looks _ td.looks.Cat["v"]; }; Emphasize: PROC [cd: CharDisplay, emph: Emph, on: BOOL] = { td: TiogaDisplay _ NARROW[cd.otherInstanceData]; loc: INT _ TiogaOps.GetTextKey[node: td.arrayNode, key: td.lineS[cd.line].key].where + cd.col; looks: ROPE _ SELECT emph FROM underline => "z", bold => "b", italic => "i", inverse => "v", ENDCASE => ERROR; WithLocks: PROC [root: TiogaOps.Ref] = { IF root # td.root THEN ERROR; IF Destruction[cd, td] THEN RETURN; TiogaOps.SetSelection[ viewer: cd.viewer, start: [td.arrayNode, loc], end: [td.arrayNode, loc]]; (IF on THEN TiogaOps.AddLooks ELSE TiogaOps.SubtractLooks)[looks]; }; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; IF cd.col > td.lineS[cd.line].tiogaCount THEN RETURN; TiogaOps.CallWithLocks[WithLocks, td.root]; }; SetFont: PROC [cd: CharDisplay, font: ROPE] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; TiogaOps.PutProp[ n: td.root, name: $Postfix, value: IO.PutFR[ "(look.f) \"fixed-pitch font\" {\"%g\" family} StyleRule", IO.rope[font] ] ]; }; Beep: PROC [cd: CharDisplay] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; IF td.destroyed THEN ERROR DisplayDestroyed[cd]; IF Beeps.can THEN Beeps.Beep[500,300] ELSE { MessageWindow.Append["Beep", TRUE]; MessageWindow.Blink[]; MessageWindow.Clear[]; }; }; Destroyed: PROC [cd: CharDisplay] RETURNS [b: BOOL] = { td: TiogaDisplay = NARROW[cd.otherInstanceData]; b _ td.destroyed; }; InitTiogaDisplayClass[]; }.