<> <> 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, 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 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 ]; <> <<>> kbBuffer: RECORD[type: {integer, real, variable}, data: ROPE]; DeleteSelection: PROC[flavor: ATOM, reselect: BOOL _ TRUE] ~ { <> << Replaces selection with an expression "placeholder".>> << This placeholder remians selected iff reselect is TRUE.>> <> state: State; 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[]]; state _ NARROW[selectedViewer.data]; MathDisplayExpr.Replace[selectedExpr, replacement ! MathDisplayExpr.replaceAll => {state.displayExpr _ replacement; CONTINUE}]; <> state.displayBox _ state.displayExpr.Format[normal]; <> <> 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]; }; ConvertToRope: 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]; RETURN; -- no selection }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; v _ ViewerTools.MakeNewTextViewer[info: [name: "Rope"]]; ViewerTools.SetContents[v, MathDisplayExpr.ASRopeFromDisplayExpr[primaryExpr]]; }; Break: SIGNAL = CODE; EnterDebugger: Menus.MenuProc ~ { <> <<>> s: State _ NARROW[clientData]; expr: DisplayExpr _ s.displayExpr; SIGNAL Break; }; CopySecondaryToPrimary: PROC[] ~ { <> << replaces primary selection by secondary selection.>> <> primaryState, secondaryState: State; 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]; RETURN; }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; [copyViewer, copyExpr] _ ViewExprOps.GetSelection[$copy]; primaryState _ NARROW[primaryViewer.data]; secondaryState _ NARROW[copyViewer.data]; replacement _ MathDisplayExpr.Copy[copyExpr]; MathDisplayExpr.Replace[primaryExpr, replacement ! MathDisplayExpr.replaceAll => {primaryState.displayExpr _ replacement; CONTINUE}]; <> primaryState.displayBox _ primaryState.displayExpr.Format[normal]; <> 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]; RETURN; -- no selection to replace }; <> nRows _ PopUpMenu.RequestSelection[label: "# Rows", choice: oneToTen, timeOut: 10]; IF (nRows < 1) THEN RETURN; -- no selection or timeout nCols _ PopUpMenu.RequestSelection[label: "# Cols", choice: oneToTen, timeOut: 10]; 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]]; <> MathDisplayExpr.Replace[primaryExpr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr _ replacement; CONTINUE}]; <> s.displayBox _ s.displayExpr.Format[normal]; <> 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]; 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, timeOut: 10]; 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]; 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, timeOut: 10]; 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 s: State _ NARROW[viewer.data]; <> 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]]; <> MathDisplayExpr.Replace[expr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr _ replacement; CONTINUE}]; <> s.displayBox _ s.displayExpr.Format[normal]; <> 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[s.exprViewer]; <> IF selectableSibling # NIL THEN ViewExprOps.Select[$primary, s.exprViewer, 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; s: State _ NARROW[viewer.data]; <> 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]]; <> MathDisplayExpr.Replace[expr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr _ replacement; CONTINUE}]; <> s.displayBox _ s.displayExpr.Format[normal]; <> ViewExprOps.PaintEnqueue[s.exprViewer]; <> ViewExprOps.FlushPaintQueue[]; }; EnterAtom: Menus.MenuProc ~ { <> << Returns without action if any errors (e.g. parsing) occur.>> <> displayExpr: DisplayExpr _ NIL; choices: LIST OF ROPE _ LIST["integer", "real", "variable"]; 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]; RETURN; -- no selection to replace }; <> selection _ PopUpMenu.RequestSelection[label: "Choose Atom", choice: choices, timeOut: 10]; { ENABLE MathConstructors.badFormat, Convert.Error => { MessageWindow.Append["Input format error.", TRUE]; MessageWindow.Blink[]; GOTO abort; }; SELECT selection FROM 1 => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInt[ViewerTools.GetSelectionContents[]]]; }; 2 => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeReal[Convert.RealFromRope[ViewerTools.GetSelectionContents[]]]]; }; 3 => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVariable[ViewerTools.GetSelectionContents[]]]; }; ENDCASE => RETURN; <> ViewExprOps.UnSelect[$primary]; -- unselect currently active selection MathDisplayExpr.Replace[primaryExpr, displayExpr ! MathDisplayExpr.replaceAll => { s.displayExpr _ displayExpr; CONTINUE}]; <> s.displayBox _ s.displayExpr.Format[normal]; <> ViewerOps.PaintViewer[s.exprViewer, client]; }; EXITS abort => NULL; }; 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 _ MathDisplayExpr.Format[displayExpr, normal]; <> v _ ViewerOps.CreateViewer[flavor: $expr]; <> s _ NARROW[v.data]; v.name _ name; -- set caption for viewer s.displayExpr _ displayExpr; 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[]; s.displayExpr _ MathDisplayExpr.DisplayExprFromExpr[expression]; s.displayBox _ MathDisplayExpr.Format[s.displayExpr, normal]; 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"], paint: PaintExprViewer, scroll: VerticalScroll, hscroll: HorizontalScroll ]] ]; }; RegisterExprViewerClass[]; END.