<> <> DIRECTORY ImagerFont, Imager, ImagerTransformation, ImagerColor USING [ColorFromAtom], ImagerBackdoor USING [MakeStipple], Commander, Convert, ViewerOps, Menus, PopUpMenu USING [RequestSelection], MessageWindow USING [Append, Blink], ViewerTools, ViewerClasses, Rope, Real USING [RoundI], TIPUser, Icons, InputFocus, MathExpr, MathDisplayExpr, MathDB, MathBox, MathTypes, MathConstructors, ViewExprOps, ViewExpr; ViewExprImpl: CEDAR PROGRAM IMPORTS ViewerOps, TIPUser, Menus, MathDisplayExpr, MathBox, MathConstructors, PopUpMenu, MathExpr, MathDB, Convert, MessageWindow, ViewerTools, Rope, Real, InputFocus, ViewExprOps, ImagerColor, ImagerBackdoor, Icons EXPORTS ViewExpr ~ BEGIN <> ROPE: TYPE ~ Rope.ROPE; BOX: TYPE ~ MathBox.BOX; Viewer: TYPE ~ ViewerClasses.Viewer; Argument: TYPE ~ MathExpr.Argument; CompoundClass: TYPE ~ MathExpr.CompoundClass; EXPR: TYPE ~ MathExpr.EXPR; DisplayExpr: TYPE ~ MathDisplayExpr.DisplayExpr; <> fontToViewerSizeRatio: REAL = 75.0; -- magnify from TeX font units to Viewer pixels <> <> <<>> State: TYPE ~ REF StateRec; StateRec: TYPE ~ RECORD [ exprViewer: Viewer, -- viewer containing expression offsetX, offsetY: REAL, -- offsets into imager context of expr lower left-hand corner scale: REAL, -- magnification factor (1.0 = default) displayExpr: DisplayExpr, -- expression displayBox: BOX, -- bounding box for expression physicalBox: BOX, -- physical mapping between expression and viewer lastDisplayExpr: DisplayExpr _ NIL -- last display expression (for undo, etc.) ]; <> <<>> kbBuffer: RECORD[type: {integer, real, variable}, data: ROPE]; SetDisplayExpr: PROC[viewer: Viewer, replacement: DisplayExpr] ~ { <> <> <> << go thru this procedure to keep "UNDO" information valid.>> <<>> state: State _ NARROW[viewer.data]; -- get current state state.lastDisplayExpr _ state.displayExpr; -- old gets current state.displayExpr _ replacement; -- current gets new state.displayBox _ state.displayExpr.Format[normal]; -- current gets new }; UndoViewer: PROC[viewer: Viewer] ~ { <> <> <<>> state: State _ NARROW[viewer.data]; -- get current state <> ViewExprOps.UnSelectViewer[viewer]; <> SetDisplayExpr[viewer, state.lastDisplayExpr]; <> ViewerOps.PaintViewer[viewer, client]; }; ReplaceInViewer: PROC[viewer: Viewer, old, new: DisplayExpr] ~ { <> <> << Reformats the display expression for viewer.>> state: State _ NARROW[viewer.data]; replacement: DisplayExpr _ MathDisplayExpr.Replace[state.displayExpr, old, new]; SetDisplayExpr[viewer, replacement]; }; DeleteSelection: PROC[flavor: ATOM, reselect: BOOL _ TRUE] ~ { <> << Replaces selection with an expression "placeholder".>> << This placeholder remians selected iff reselect is TRUE.>> <> replacement, selectedExpr: DisplayExpr _ NIL; selectedViewer: Viewer _ NIL; IF ~ViewExprOps.Active[flavor] THEN RETURN; -- return w/o action if seln not active [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[flavor]; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakePlaceHolder[]]; ReplaceInViewer[selectedViewer, selectedExpr, replacement]; <> <> ViewExprOps.Select[r.newSelect, selectedViewer, replacement]; }; -- end of "if ok" }; -- end of "replace Parse action choice" w: REF WrapAction => { <> selectedViewer: Viewer _ NIL; selectedExpr: DisplayExpr _ NIL; ok: BOOL _ TRUE; [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[w.selection ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ok THEN WrapTemplateAround[w.class, selectedViewer, selectedExpr]; }; -- end of "wrap Parse action choice" n: REF NoAction => NULL; -- do nothing; no action ENDCASE => ERROR; }; Scale: Menus.MenuProc ~ { <> << If selected with left button, scale _ scale * 1.5,>> << right button, scale _ scale / 1.5,>> << middle button, scale _ 1.0>> s: State _ NARROW[clientData]; SELECT mouseButton FROM red => {s.scale _ s.scale * 1.5}; yellow => {s.scale _ 1.0}; blue => {s.scale _ s.scale / 1.5}; ENDCASE => ERROR; ViewerOps.PaintViewer[s.exprViewer, client]; }; ConvertToASRope: Menus.MenuProc ~ { <> v: ViewerTools.Viewer _ NIL; s: State _ NARROW[clientData]; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection }; <> [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; <> v _ ViewerTools.MakeNewTextViewer[info: [name: Rope.Cat["ASRope for ", s.exprViewer.name]]]; ViewerTools.SetContents[v, MathDisplayExpr.ASRopeFromDisplayExpr[primaryExpr]]; }; ConvertToRope: Menus.MenuProc ~ { <> <<>> v: ViewerTools.Viewer _ NIL; s: State _ NARROW[clientData]; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; ropeVal: ROPE _ NIL; <> IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection, so complain }; <> [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; <> ropeVal _ MathExpr.RopeFromExpr[MathDisplayExpr.ExprFromDisplayExpr[primaryExpr]]; <> v _ ViewerTools.MakeNewTextViewer[info: [name: Rope.Cat["Rope for ", s.exprViewer.name]]]; ViewerTools.SetContents[v, ropeVal]; }; ConvertFromRope: Menus.MenuProc ~ { <> <<>> <> ropeVal: ROPE _ ViewerTools.GetSelectionContents[]; -- get rope from text selection exprVal, primaryExpr: DisplayExpr _ NIL; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; <> IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection, so complain }; <> [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; exprVal _ MathDisplayExpr.DisplayExprFromExpr[MathExpr.ExprFromRope[ropeVal ! MathExpr.parseError => {ok _ FALSE; CONTINUE}]]; IF ok THEN { <> ReplaceInViewer[primaryViewer, primaryExpr, exprVal]; ViewExprOps.UnSelect[$primary]; ViewExprOps.FlushPaintQueue[]; -- update viewer contents } ELSE { <> MessageWindow.Append["Expression Format Error.", TRUE]; MessageWindow.Blink[]; }; }; Break: SIGNAL = CODE; EnterDebugger: Menus.MenuProc ~ { <> <<>> s: State _ NARROW[clientData]; expr: DisplayExpr _ s.displayExpr; SIGNAL Break; }; CopySecondaryToPrimary: PROC[] ~ { <> << replaces primary selection by secondary selection.>> <> replacement, primaryExpr, copyExpr: DisplayExpr _ NIL; primaryViewer, copyViewer: Viewer _ NIL; IF ~(ViewExprOps.Active[$primary] AND ViewExprOps.Active[$copy]) THEN { MessageWindow.Append["Insufficient Selections for Copy.", TRUE]; MessageWindow.Blink[]; RETURN; }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; [copyViewer, copyExpr] _ ViewExprOps.GetSelection[$copy]; replacement _ MathDisplayExpr.Copy[copyExpr]; ReplaceInViewer[primaryViewer, primaryExpr, replacement]; <> ViewExprOps.UnSelect[$primary]; DeleteSelection[$move, FALSE]; }; }; EnterMatrix: Menus.MenuProc ~ { <> << whose size is chosen from a series op pop-up menus.>> << Returns without action if any errors occur.>> <<>> <> primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; oneToTen: LIST OF ROPE _ LIST["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]; nRows, nCols: INT _ 0; rows: LIST OF LIST OF EXPR _ NIL; replacement: DisplayExpr _ NIL; ok: BOOL _ TRUE; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; <> nRows _ PopUpMenu.RequestSelection[label: "# Rows", choice: oneToTen]; IF (nRows < 1) THEN RETURN; -- no selection or timeout nCols _ PopUpMenu.RequestSelection[label: "# Cols", choice: oneToTen]; IF (nCols < 1) THEN RETURN; -- no selection or timeout <> rows _ NIL; -- cons up list of rows FOR r:INT IN [1..nRows] DO currentRow: LIST OF EXPR _ NIL; -- cons up list of elements FOR c:INT IN [1..nCols] DO currentRow _ CONS[MathConstructors.MakeInt["0"], currentRow]; ENDLOOP; rows _ CONS[currentRow, rows]; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeMatrix[nRows, nCols, rows]]; <> ReplaceInViewer[primaryViewer, primaryExpr, replacement]; <> ViewExprOps.PaintEnqueue[s.exprViewer]; <> ViewExprOps.FlushPaintQueue[]; }; EnterVector: Menus.MenuProc ~ { <> << whose size and type is chosen from a series op pop-up menus.>> << Returns without action if any errors occur.>> <<>> <> primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; oneToTen: LIST OF ROPE _ LIST["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]; dimension: INT _ 0; elements: LIST OF EXPR _ NIL; replacement: DisplayExpr _ NIL; row: INT _ 0; -- =1 => row vector, =2 => col vector, ELSE => invalid rowVec: BOOL _ FALSE; -- TRUE iff vector is a row vector ok: BOOL _ TRUE; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; <> <<>> row _ PopUpMenu.RequestSelection[label: "Vector Type", choice: LIST["row", "column"]]; IF (row < 1) OR (row > 2) THEN RETURN; -- no selection or timeout IF row = 1 THEN rowVec _ TRUE; dimension _ PopUpMenu.RequestSelection[label: "Dimension", choice: oneToTen]; IF (dimension < 1) THEN RETURN; -- no selection or timeout <> FOR i: INT IN [1..dimension] DO elements _ CONS[MathConstructors.MakeInt["0"], elements]; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVector[dimension, elements, rowVec]]; <> ReplaceInViewer[primaryViewer, primaryExpr, replacement]; <> ViewExprOps.PaintEnqueue[s.exprViewer]; <> ViewExprOps.FlushPaintQueue[]; }; EnterExpression: Menus.MenuProc ~ { <> << which is chosen from a pop-up menu.>> << Returns without action if any errors occur.>> <> exprClassName: ATOM; choices: LIST OF ROPE _ NIL; choiceClassNames: LIST OF ATOM _ NIL; selection: INT _ 0; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; <> <<>> <> FOR l: LIST OF ATOM _ MathDB.CompoundClassNames, l.rest UNTIL l = NIL DO description: ROPE _ MathDB.LookupCompoundClass[l.first].description; choices _ CONS[description, choices]; choiceClassNames _ CONS[l.first, choiceClassNames]; ENDLOOP; <> selection _ PopUpMenu.RequestSelection[label: "Choose Expr", choice: choices]; IF (selection < 1) THEN RETURN; -- no selection or timeout <> FOR l: LIST OF ATOM _ choiceClassNames, l.rest UNTIL l = NIL DO <> selection _ selection - 1; IF selection = 0 THEN {exprClassName _ l.first; EXIT}; ENDLOOP; IF (selection # 0) THEN RETURN; -- should never occur if pop-up works ok ReplaceWithTemplate[exprClassName, primaryViewer, primaryExpr]; }; WrapExpression: Menus.MenuProc ~ { <> << which is chosen from a pop-up menu.>> << Returns without action if any errors occur.>> <> choices: LIST OF ROPE _ NIL; choiceClassNames: LIST OF ATOM _ NIL; selection: INT _ 0; exprClassName: ATOM; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; <> <<>> <> FOR l: LIST OF ATOM _ MathDB.CompoundClassNames, l.rest UNTIL l = NIL DO description: ROPE _ MathDB.LookupCompoundClass[l.first].description; choices _ CONS[description, choices]; choiceClassNames _ CONS[l.first, choiceClassNames]; ENDLOOP; <> selection _ PopUpMenu.RequestSelection[label: "Choose Expr", choice: choices]; IF (selection < 1) THEN RETURN; -- no selection or timeout <> FOR l: LIST OF ATOM _ choiceClassNames, l.rest UNTIL l = NIL DO < ATOM)>> selection _ selection - 1; IF selection = 0 THEN {exprClassName _ l.first; EXIT}; ENDLOOP; IF (selection # 0) THEN RETURN; -- should never occur if pop-up works ok <> WrapTemplateAround[exprClassName, primaryViewer, primaryExpr]; }; WrapTemplateAround: PROC [class: ATOM, viewer: Viewer, expr: DisplayExpr] ~ { <> <> <> << The primary selection is changed to be a sibling of the "hot" argument.>> <> exprClass: CompoundClass; -- class data associated with class argExprs: LIST OF MathExpr.TaggedMathExpr _ NIL; replacement, selectableSibling: DisplayExpr _ NIL; hotTag: ATOM; -- tag of "hot" argument <> exprClass _ MathDB.LookupCompoundClass[class]; <> FOR l: LIST OF Argument _ exprClass.arguments, l.rest UNTIL l = NIL DO hot: BOOL _ FALSE; FOR aliases: LIST OF ATOM _ l.first.aliases, aliases.rest UNTIL aliases = NIL DO IF aliases.first = $aliasHot THEN {hotTag _ l.first.name; hot _ TRUE; EXIT}; ENDLOOP; IF hot AND (expr.Class[] # $placeholder) THEN { argExprs _ CONS[[l.first.name, MathDisplayExpr.ExprFromDisplayExpr[expr]], argExprs]; } ELSE { argExprs _ CONS[[l.first.name, MathConstructors.MakePlaceHolder[]], argExprs]; }; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathExpr.MakeCompoundExpr[class, argExprs]]; <> ReplaceInViewer[viewer, expr, replacement]; <> FOR l: LIST OF DisplayExpr _ replacement.GetSubExprs[], l.rest UNTIL l = NIL DO IF l.first.Tag[] = hotTag THEN { selectableSibling _ l.first.SelectableSibling[! MathDisplayExpr.noSelection => CONTINUE]; EXIT; -- break out of FOR loop }; ENDLOOP; <> ViewExprOps.PaintEnqueue[viewer]; <> IF selectableSibling # NIL THEN ViewExprOps.Select[$primary, viewer, selectableSibling] ELSE ViewExprOps.UnSelect[$primary]; <> ViewExprOps.FlushPaintQueue[]; }; ReplaceWithTemplate: PROC [class: ATOM, viewer: Viewer, expr: DisplayExpr] ~ { <> <> <> <> exprClass: CompoundClass; argExprs: LIST OF MathExpr.TaggedMathExpr _ NIL; replacement: DisplayExpr _ NIL; <> exprClass _ MathDB.LookupCompoundClass[class]; <> FOR l: LIST OF Argument _ exprClass.arguments, l.rest UNTIL l = NIL DO argExprs _ CONS[[l.first.name, MathConstructors.MakePlaceHolder[]], argExprs]; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathExpr.MakeCompoundExpr[class, argExprs]]; <> ReplaceInViewer[viewer, expr, replacement]; <> ViewExprOps.PaintEnqueue[viewer]; <> ViewExprOps.FlushPaintQueue[]; }; EnterAtom: Menus.MenuProc ~ { <> << Returns without action if any errors (e.g. parsing) occur.>> <> displayExpr: DisplayExpr _ NIL; choices: LIST OF ROPE _ LIST["greek variable", "infinity", "integer", "real", "variable"]; selection: INT _ 0; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; textSelection: ROPE _ NIL; <> integer: INT = 3; real: INT = 4; variable: INT = 5; greekVar: INT = 1; infinity: INT = 2; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; <> selection _ PopUpMenu.RequestSelection[label: "Choose Atom", choice: choices]; { ENABLE MathConstructors.badFormat, Convert.Error => { MessageWindow.Append["Input format error.", TRUE]; MessageWindow.Blink[]; GOTO abort; }; <> textSelection _ ViewerTools.GetSelectionContents[]; SELECT selection FROM integer => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInt[textSelection]]; }; real => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeReal[Convert.RealFromRope[textSelection]]]; }; variable => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVariable[textSelection]]; }; infinity => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInfinity[]]; }; greekVar => { <> greekName: ROPE _ NIL; greekChoices: LIST OF ROPE _ LIST["alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", "mu", "nu", "xi", "pi", "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega"]; <> selection _ PopUpMenu.RequestSelection[label: "Choose Variable", choice: greekChoices]; IF selection < 1 THEN RETURN; -- timeout or no selection FOR l: LIST OF ROPE _ greekChoices, l.rest UNTIL l = NIL DO < ATOM)>> selection _ selection - 1; IF selection = 0 THEN {greekName _ l.first; EXIT}; ENDLOOP; displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeGreekVar[greekName]]; }; ENDCASE => RETURN; <> ViewExprOps.UnSelect[$primary]; -- unselect currently active selection ReplaceInViewer[primaryViewer, primaryExpr, displayExpr]; <> ViewerOps.PaintViewer[s.exprViewer, client]; }; EXITS abort => NULL; }; Undo: Menus.MenuProc ~ { <> <<>> s: State _ NARROW[clientData]; UndoViewer[s.exprViewer]; }; Home: Menus.MenuProc ~ { <> s: State _ NARROW[clientData]; s.offsetX _ 0.0; s.offsetY _ 0.0; ViewerOps.PaintViewer[s.exprViewer, client]; }; VerticalScroll: ViewerClasses.ScrollProc ~ { <> s: State _ NARROW[self.data]; -- get viewer state info <> topPercentage: INTEGER _ 100 + Real.RoundI[((-self.ch + (s.physicalBox.Offset[].y - s.physicalBox.Extents[].descent)) / s.physicalBox.Height[]) * 100]; bottomPercentage: INTEGER _ 100 - Real.RoundI[((s.physicalBox.Extents[].descent - s.physicalBox.Offset[].y) / s.physicalBox.Height[]) * 100]; SELECT op FROM query => { RETURN[top: MIN[100, MAX[0, topPercentage]], bottom: MAX[0, MIN[100, bottomPercentage]]]; }; up => { s.offsetY _ s.offsetY + amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; down => { s.offsetY _ s.offsetY - amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; thumb => RETURN[]; ENDCASE => ERROR; }; HorizontalScroll: ViewerClasses.HScrollProc ~ { <> s: State _ NARROW[self.data]; -- get viewer state info <> leftPercentage: INTEGER _ Real.RoundI[((s.physicalBox.Extents[].leftExtent - s.physicalBox.Offset[].x) / s.physicalBox.Width[]) * 100]; rightPercentage: INTEGER _ Real.RoundI[((self.cw - (s.physicalBox.Offset[].x - s.physicalBox.Extents[].leftExtent)) / s.physicalBox.Width[]) * 100]; SELECT op FROM query => { RETURN[left: MIN[100, MAX[0, leftPercentage]], right: MAX[0, MIN[100, rightPercentage]]]; }; left => { s.offsetX _ s.offsetX - amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; right => { s.offsetX _ s.offsetX + amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; thumb => RETURN[]; ENDCASE => ERROR; }; Create: PUBLIC PROC[expr: EXPR _ NIL, name: ROPE] RETURNS[Viewer] ~ { <> << Viewer will contain math expression expr. If expr = NIL, then>> << Viewer will contain an empty "placeholder".>> << >> v: Viewer _ NIL; -- expression viewer instance s: State; -- expression viewer state expression: EXPR _ expr; -- intial expression to display in viewer displayExpr: DisplayExpr _ NIL; -- display form of expr displayBox: BOX _ NIL; -- box for displayExpr <> IF expression = NIL THEN expression _ MathConstructors.MakePlaceHolder[]; displayExpr _ MathDisplayExpr.DisplayExprFromExpr[expression]; displayBox _ displayExpr.Format[normal]; <> v _ ViewerOps.CreateViewer[flavor: $expr, info: [label: name, name: name, scrollable: TRUE, hscrollable: TRUE]]; <> s _ NARROW[v.data]; s.displayExpr _ displayExpr; s.lastDisplayExpr _ displayExpr; -- for UNDO s.displayBox _ displayBox; <> ViewerOps.PaintViewer[v, all]; <> RETURN[v]; }; SetContents: PUBLIC PROC[viewer: Viewer, expr: EXPR] ~ { <> <> << to be expr.>> <<>> s: State _ NARROW[viewer.data]; expression: EXPR _ expr; <> IF expression = NIL THEN expression _ MathConstructors.MakePlaceHolder[]; <> ReplaceInViewer[viewer, s.displayExpr, MathDisplayExpr.DisplayExprFromExpr[expression]]; ViewExprOps.UnSelectViewer[viewer]; -- destroy any references to old contents ViewerOps.PaintViewer[viewer, client]; -- repaint updated viewer }; GetContents: PUBLIC PROC[viewer: Viewer] RETURNS[ROPE] ~ { <> << a format understood by the AlgebraStructures parser.>> <<>> s: State _ NARROW[viewer.data]; RETURN[MathDisplayExpr.ASRopeFromDisplayExpr[s.displayExpr]]; }; RegisterExprViewerClass: PROC ~ { <> ViewerOps.RegisterViewerClass[ $expr, NEW[ViewerClasses.ViewerClassRec _ [ init: InitializeExprViewer, destroy: DestroyExprViewer, notify: NotifyExprViewer, tipTable: TIPUser.InstantiateNewTIPTable["ViewExpr.tip"], icon: Icons.NewIconFromFile["Meddle.icons", 0], paint: PaintExprViewer, scroll: VerticalScroll, hscroll: HorizontalScroll ]] ]; }; RegisterExprViewerClass[]; END.