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
Type Abbreviations From Imported Interfaces
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;
Constants
fontToViewerSizeRatio: REAL = 75.0; -- magnify from TeX font units to Viewer pixels
Type Definitions
ViewerClass $expr
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.)
];
Global Data (global only to this module)
kbBuffer: RECORD[type: {integer, real, variable}, data: ROPE];
SetDisplayExpr:
PROC[viewer: Viewer, replacement: DisplayExpr] ~ {
modifies: viewer
effects: Sets and formats display expression for viewer to replacement.
intent: ALL assignments to viewer display expressions and boxes should
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] ~ {
modifies: viewer
effects: Restores viewer state (expression & box info) to previous state.
state: State ← NARROW[viewer.data]; -- get current state
cancel any current selections in viewer since UNDO may invalidate them
ViewExprOps.UnSelectViewer[viewer];
replace current expr by last saved expr
SetDisplayExpr[viewer, state.lastDisplayExpr];
now paint the viewer so UNDO is seen
ViewerOps.PaintViewer[viewer, client];
};
ReplaceInViewer:
PROC[viewer: Viewer, old, new: DisplayExpr] ~ {
modifies: viewer
effects: Replaces old in the display expression for viewer with new.
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] ~ {
effects: Deletes selection of type flavor, if active.
Replaces selection with an expression "placeholder".
This placeholder remians selected iff reselect is TRUE.
local declarations
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];
following select will repaint viewer to reflect change
select placeholder replacement
IF reselect THEN ViewExprOps.Select[flavor, selectedViewer, replacement]
ELSE ViewExprOps.UnSelect[flavor];
};
InitializeExprViewer: ViewerClasses.InitProc ~ {
effects: Initializes an expression viewer instance.
self.data ←
NEW[StateRec ← [
exprViewer: self,
offsetX: 0.0,
offsetY: 0.0,
scale: 1.0,
displayExpr: NIL,
displayBox: NIL,
physicalBox: NIL]];
self.menu ← Menus.CreateMenu[lines: 3];
self.menu.AppendMenuEntry[Menus.CreateEntry[name: "Debug", proc: EnterDebugger, clientData: self.data, guarded: TRUE], 0];
self.menu.AppendMenuEntry[Menus.CreateEntry["Scale", Scale, self.data], 0];
self.menu.AppendMenuEntry[Menus.CreateEntry["Home", Home, self.data], 0];
self.menu.AppendMenuEntry[Menus.CreateEntry["Undo", Undo, self.data], 0];
self.menu.AppendMenuEntry[Menus.CreateEntry["FromRope", ConvertFromRope, self.data], 1];
self.menu.AppendMenuEntry[Menus.CreateEntry["ToRope", ConvertToRope, self.data], 1];
self.menu.AppendMenuEntry[Menus.CreateEntry["ToASRope", ConvertToASRope, self.data], 1];
self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithMatrix", EnterMatrix, self.data], 2];
self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithVector", EnterVector, self.data], 2];
self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithExpr", EnterExpression, self.data], 2];
self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithAtom", EnterAtom, self.data], 2];
self.menu.AppendMenuEntry[Menus.CreateEntry["WrapWithExpr", WrapExpression, self.data], 2];
};
DestroyExprViewer: ViewerClasses.DestroyProc ~ {
effects: Peforms orderly termination of an expression viewer.
s: State ← NARROW[self.data];
if self contains any selections, unselect them
ViewExprOps.UnSelectViewer[self];
remove self from paint queue
ViewExprOps.PaintDequeue[self];
s.exprViewer ← NIL; -- break up circular ref for GC
};
NotifyExprViewer: ViewerClasses.NotifyProc ~ {
effects: Performs actions based on parsed TIP events.
s: State ← NARROW[self.data];
replacement: DisplayExpr ← NIL;
c: CHAR;
grab the input focus so keyboard events get parsed
[] ← InputFocus.SetInputFocus[self];
WITH input.first SELECT FROM
refC:
REF
CHAR => {
copy char data & parse it
c ← refC^;
ParseKBChar[c];
};
xy: TIPUser.TIPScreenCoords => {
SELECT input.rest.first FROM
$PrimarySelect => {
ViewExprOps.Select[$primary, s.exprViewer, MathDisplayExpr.DisplayExprFromCoords[s.displayExpr, xy.mouseX, xy.mouseY ! MathDisplayExpr.noSelection => {ViewExprOps.UnSelect[$primary]; CONTINUE}]];
};
$MoveSelect => {
can't make a move selection w/o an existing primary selection
IF ~ViewExprOps.Active[$primary]
THEN {
MessageWindow.Append["Make a primary selection first.", TRUE];
MessageWindow.Blink[];
RETURN;
};
ViewExprOps.Select[$move, s.exprViewer, MathDisplayExpr.DisplayExprFromCoords[s.displayExpr, xy.mouseX, xy.mouseY ! MathDisplayExpr.noSelection => {ViewExprOps.UnSelect[$move]; CONTINUE}]];
};
$CopySelect => {
can't make a copy selection w/o an existing primary selection
IF ~ViewExprOps.Active[$primary]
THEN {
MessageWindow.Append["Make a primary selection first.", TRUE];
MessageWindow.Blink[];
RETURN;
};
ViewExprOps.Select[$copy, s.exprViewer, MathDisplayExpr.DisplayExprFromCoords[s.displayExpr, xy.mouseX, xy.mouseY ! MathDisplayExpr.noSelection => {ViewExprOps.UnSelect[$copy]; CONTINUE}]];
};
$PrimaryParentSelect, $MoveParentSelect, $CopyParentSelect => {
local declarations
flavor: ATOM;
selectedExpr: DisplayExpr ← NIL;
selectedViewer: Viewer ← NIL;
SELECT input.rest.first
FROM
$PrimaryParentSelect => flavor ← $primary;
$MoveParentSelect => flavor ← $move;
$CopyParentSelect => flavor ← $copy;
ENDCASE => ERROR;
make sure selection of flavor already exists (if not, return)
IF ~ViewExprOps.Active[flavor] THEN RETURN;
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[flavor];
ViewExprOps.Select[flavor, selectedViewer, MathDisplayExpr.SelectableParent[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]];
};
$PrimaryChildSelect, $MoveChildSelect, $CopyChildSelect => {
local declarations
flavor: ATOM;
selectedExpr: DisplayExpr ← NIL;
selectedViewer: Viewer ← NIL;
SELECT input.rest.first
FROM
$PrimaryChildSelect => flavor ← $primary;
$MoveChildSelect => flavor ← $move;
$CopyChildSelect => flavor ← $copy;
ENDCASE => ERROR;
make sure selection of flavor already exists (if not, return)
IF ~ViewExprOps.Active[flavor] THEN RETURN;
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[flavor];
ViewExprOps.Select[flavor, selectedViewer, MathDisplayExpr.SelectableChild[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]];
};
$PrimarySiblingSelect, $MoveSiblingSelect, $CopySiblingSelect => {
local declarations
flavor: ATOM;
selectedExpr: DisplayExpr ← NIL;
selectedViewer: Viewer ← NIL;
SELECT input.rest.first
FROM
$PrimarySiblingSelect => flavor ← $primary;
$MoveSiblingSelect => flavor ← $move;
$CopySiblingSelect => flavor ← $copy;
ENDCASE => ERROR;
make sure selection of flavor already exists (if not, return)
IF ~ViewExprOps.Active[flavor] THEN RETURN;
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[flavor];
ViewExprOps.Select[flavor, selectedViewer, MathDisplayExpr.SelectableSibling[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]];
};
$KBPrimaryDelete => {
IF ViewExprOps.Active[$primary]
THEN
DeleteSelection[$primary]
ELSE
DeleteSelection[$keyboard];
kbBuffer.data ← ""; -- reset keyboard buffer contents
};
$DoPendingCopy => {
IF ViewExprOps.Active[$copy] THEN CopySecondaryToPrimary[];
};
$DoPendingMove => {
IF ViewExprOps.Active[$move] THEN MoveSecondaryToPrimary[];
};
$KBPrimaryParentSelect => {
extend active primary or keyboard selection to its parent as a primary selection
local declarations
selectedViewer: Viewer ← NIL;
selectedExpr: DisplayExpr ← NIL;
IF ViewExprOps.Active[$primary]
THEN {
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[$primary];
ViewExprOps.Select[$primary, selectedViewer, MathDisplayExpr.SelectableParent[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]];
}
ELSE
IF ViewExprOps.Active[$keyboard]
THEN {
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[$keyboard];
ViewExprOps.UnSelect[$keyboard];
ViewExprOps.Select[$primary, selectedViewer, MathDisplayExpr.SelectableParent[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]];
};
};
ENDCASE => RETURN; -- take no action given unrecognized TIP actions
};
ENDCASE => ERROR;
flush any pending paint requests
ViewExprOps.FlushPaintQueue[];
};
SelectionColor:
PROC[flavor:
ATOM]
RETURNS[Imager.Color] ~ {
effects: Returns the color associated with flavor.
If no such association exists, uses "$Invert" color.
RETURN[
SELECT flavor
FROM
$primary => ImagerColor.ColorFromAtom[$Invert],
$copy => ImagerBackdoor.MakeStipple[8421H, TRUE],
$move => ImagerBackdoor.MakeStipple[8020H, TRUE],
$keyboard => ImagerBackdoor.MakeStipple[00F0H, TRUE],
ENDCASE => ImagerColor.ColorFromAtom[$Invert]
];
};
PaintExprViewer: ViewerClasses.PaintProc ~ {
effects: Repaints contents of an expression viewer.
local declarations
scaleFactor: REAL ← fontToViewerSizeRatio; -- scaling factor
s: State ← NARROW[self.data];
highlight: LIST OF MathDisplayExpr.Selection ← NIL;
IF (s.displayExpr = NIL) OR (s.displayBox = NIL) THEN RETURN; -- nothing to paint
modify scale factor by user-specified growth factor
scaleFactor ← scaleFactor * s.scale;
s.physicalBox ← MathBox.ChangeOffset[MathBox.Scale[s.displayBox, [scaleFactor, scaleFactor]],
[s.offsetX + (scaleFactor * s.displayBox.Extents.leftExtent), s.offsetY + (scaleFactor * s.displayBox.Extents.descent)]];
cons up list of selections to highlight
FOR l:
LIST
OF
ATOM ←
LIST[$primary, $copy, $move, $keyboard], l.rest
UNTIL l =
NIL
DO
IF ViewExprOps.Active[l.first]
THEN {
selectedViewer: Viewer ← NIL;
selectedExpr: DisplayExpr ← NIL;
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[l.first];
IF selectedViewer = s.exprViewer THEN highlight ← CONS[[selectedExpr, SelectionColor[l.first]], highlight];
};
ENDLOOP;
spray the bits onto screen via imager
MathDisplayExpr.Paint[s.displayExpr, context, s.physicalBox, highlight];
};
LexType: TYPE ~ {ucAlpha, lcAlpha, digit, decimal, op, other};
LexChar:
PROC[c:
CHAR]
RETURNS[LexType] ~ {
effects: Returns the lexical type of c
RETURN[
SELECT c FROM
IN ['0..'9] => digit,
IN ['a..'z] => lcAlpha,
IN ['A..'Z] => ucAlpha,
'. => decimal,
'+, '- , '*, '^, '=, '>, '<, '&, '|, '/, '(, '~, '#, ', => op,
ENDCASE => other
];
};
this pointless extra level of indirection proves that variants LOSE BIG TIME in Cedar
oh, what I'd give for CLU oneof's...
ParseAction: TYPE ~ REF ParseActionRep;
ReplaceAction: TYPE ~ replace ParseActionRep;
WrapAction: TYPE ~ wrap ParseActionRep;
NoAction: TYPE ~ none ParseActionRep;
ParseActionRep:
TYPE ~
RECORD [
SELECT type:*
FROM
replace => [
oldSelect: ATOM, -- old selection flavor which contains expr to replace
construct: {integer, real, variable}, -- new expression to be constructed
fromRope: ROPE, -- data for construction
newSelect: ATOM -- selection flavor to select as after replacement
],
wrap => [
selection: ATOM, -- old selection flavor
class: ATOM -- class for template wrapper
],
none => NULL -- nothing
ENDCASE
];
ParseKBChar:
PROC[c:
CHAR] ~ {
modifies: kbBuffer, message window, $keyboard selection
effects: Parses c w.r.t contents of kbBuffer.
Updates kbBuffer, message window, and $keyboard selection (viewer & expr).
local declarations
lexC: LexType ← LexChar[c]; -- lexical type of character c
ropeC: ROPE ← Convert.RopeFromChar[c, FALSE]; -- rope equivalent of char c
action: ParseAction ← NIL;
first check to see if c is an operator type
IF lexC = op
THEN {
wrapAround: ATOM ← $keyboard; -- wrap operation around keyboard selection
IF ViewExprOps.Active[$primary] THEN wrapAround ← $primary; -- or primary if active
SELECT c
FROM
'+ => action ← NEW[WrapAction ← [wrap[wrapAround, $sum]]];
', => action ← NEW[WrapAction ← [wrap[wrapAround, $list]]];
'- => action ← NEW[WrapAction ← [wrap[wrapAround, $difference]]];
'* => action ← NEW[WrapAction ← [wrap[wrapAround, $product]]];
'^ => action ← NEW[WrapAction ← [wrap[wrapAround, $pow]]];
'= => action ← NEW[WrapAction ← [wrap[wrapAround, $eqFormula]]];
'> => action ← NEW[WrapAction ← [wrap[wrapAround, $gtFormula]]];
'< => action ← NEW[WrapAction ← [wrap[wrapAround, $ltFormula]]];
'& => action ← NEW[WrapAction ← [wrap[wrapAround, $and]]];
'| => action ← NEW[WrapAction ← [wrap[wrapAround, $or]]];
'~ => action ← NEW[WrapAction ← [wrap[wrapAround, $not]]];
'/ => action ← NEW[WrapAction ← [wrap[wrapAround, $fraction]]];
'( => action ← NEW[WrapAction ← [wrap[wrapAround, $paren]]];
'# => action ← NEW[WrapAction ← [wrap[wrapAround, $notEqFormula]]];
ENDCASE => ERROR;
}
ELSE {
SELECT ViewExprOps.Active[$keyboard]
FROM
FALSE => {
newly active keyboard; replace $primary with keystroke
SELECT lexC
FROM
digit => {
kbBuffer.type ← integer;
kbBuffer.data ← ropeC;
action ← NEW[ReplaceAction ← [replace[$primary, integer, kbBuffer.data, $keyboard]]];
};
lcAlpha, ucAlpha => {
kbBuffer.type ← variable;
kbBuffer.data ← ropeC;
action ← NEW[ReplaceAction ← [replace[$primary, variable, kbBuffer.data, $keyboard]]];
};
ENDCASE => action ← NEW[NoAction];
};
TRUE => {
already active keyboard; extend current selection
SELECT kbBuffer.type
FROM
integer => {
SELECT lexC
FROM
digit => {
kbBuffer.data ← kbBuffer.data.Cat[ropeC];
action ← NEW[ReplaceAction ←[replace[$keyboard, integer, kbBuffer.data, $keyboard]]];
};
decimal => {
kbBuffer.data ← kbBuffer.data.Cat[ropeC];
kbBuffer.type ← real;
action ← NEW[ReplaceAction ← [replace[$keyboard, real, kbBuffer.data, $keyboard]]];
};
ENDCASE => action ← NEW[NoAction];
};
real => {
SELECT lexC
FROM
digit => {
kbBuffer.data ← kbBuffer.data.Cat[ropeC];
action ← NEW[ReplaceAction ← [replace[$keyboard, real, kbBuffer.data, $keyboard]]];
};
ENDCASE => action ← NEW[NoAction];
};
variable => {
SELECT lexC
FROM
ucAlpha, lcAlpha => {
kbBuffer.data ← kbBuffer.data.Cat[ropeC];
action ← NEW[ReplaceAction ← [replace[$keyboard, variable, kbBuffer.data, $keyboard]]];
};
ENDCASE => action ← NEW[NoAction];
};
};
ENDCASE => ERROR;
}; -- end of ELSE
perform parse action
WITH action
SELECT
FROM
r:
REF ReplaceAction => {
local declarations
selectedViewer: Viewer ← NIL;
selectedExpr, replacement: DisplayExpr ← NIL;
ok: BOOL ← TRUE;
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[r.oldSelect ! ViewExprOps.noSelection => {ok ← FALSE; CONTINUE}];
IF ok
THEN {
SELECT r.construct
FROM
integer => {
replacement ← MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInt[r.fromRope]];
};
real => {
replacement ← MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeReal[Convert.RealFromRope[r.fromRope]]];
};
variable => {
replacement ← MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVariable[r.fromRope]];
};
ENDCASE => ERROR;
actually do the replace
ReplaceInViewer[selectedViewer, selectedExpr, replacement];
unselect the old primary selection
ViewExprOps.UnSelect[r.oldSelect];
select will repaint viewer to reflect change (replacement)
ViewExprOps.Select[r.newSelect, selectedViewer, replacement];
}; -- end of "if ok"
}; -- end of "replace Parse action choice"
w:
REF WrapAction => {
local declarations
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 ~ {
effects: Changes viewer scale factor.
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 ~ {
effects: Creates a new text viewer with contents = AS value of active selection
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
};
get viewer & expression associated with primary selection
[primaryViewer, primaryExpr] ← ViewExprOps.GetSelection[$primary];
pop up a new text viewer and convert to rope
v ← ViewerTools.MakeNewTextViewer[info: [name: Rope.Cat["ASRope for ", s.exprViewer.name]]];
ViewerTools.SetContents[v, MathDisplayExpr.ASRopeFromDisplayExpr[primaryExpr]];
};
ConvertToRope: Menus.MenuProc ~ {
effects: Creates a new text viewer with contents = canonical output form of active seln.
v: ViewerTools.Viewer ← NIL;
s: State ← NARROW[clientData];
primaryViewer: Viewer ← NIL;
primaryExpr: DisplayExpr ← NIL;
ropeVal: ROPE ← NIL;
make sure there is an active primary selection
IF ~ViewExprOps.Active[$primary]
THEN {
MessageWindow.Append["Make a primary selection first.", TRUE];
MessageWindow.Blink[];
RETURN; -- no selection, so complain
};
get viewer & expression associated with primary selection
[primaryViewer, primaryExpr] ← ViewExprOps.GetSelection[$primary];
convert expression to rope
ropeVal ← MathExpr.RopeFromExpr[MathDisplayExpr.ExprFromDisplayExpr[primaryExpr]];
pop up a new text viewer containing rope
v ← ViewerTools.MakeNewTextViewer[info: [name: Rope.Cat["Rope for ", s.exprViewer.name]]];
ViewerTools.SetContents[v, ropeVal];
};
ConvertFromRope: Menus.MenuProc ~ {
effects: Replaces primary selection with expression parsed from linear GetSelection ROPE.
local declarations
ropeVal: ROPE ← ViewerTools.GetSelectionContents[]; -- get rope from text selection
exprVal, primaryExpr: DisplayExpr ← NIL;
ok: BOOL ← TRUE;
primaryViewer: Viewer ← NIL;
make sure there is an active primary selection
IF ~ViewExprOps.Active[$primary]
THEN {
MessageWindow.Append["Make a primary selection first.", TRUE];
MessageWindow.Blink[];
RETURN; -- no selection, so complain
};
get viewer & expression associated with primary selection
[primaryViewer, primaryExpr] ← ViewExprOps.GetSelection[$primary];
exprVal ← MathDisplayExpr.DisplayExprFromExpr[MathExpr.ExprFromRope[ropeVal ! MathExpr.parseError => {ok ← FALSE; CONTINUE}]];
IF ok
THEN {
replace primary selection with exprVal
ReplaceInViewer[primaryViewer, primaryExpr, exprVal];
ViewExprOps.UnSelect[$primary];
ViewExprOps.FlushPaintQueue[]; -- update viewer contents
}
ELSE {
can't do it (parse error), so complain
MessageWindow.Append["Expression Format Error.", TRUE];
MessageWindow.Blink[];
};
};
Break: SIGNAL = CODE;
EnterDebugger: Menus.MenuProc ~ {
effects: SIGNALS Break to enter the symbolic debugger.
s: State ← NARROW[clientData];
expr: DisplayExpr ← s.displayExpr;
SIGNAL Break;
};
CopySecondaryToPrimary:
PROC[] ~ {
effects: If both primary and secondary selections are active,
replaces primary selection by secondary selection.
local declarations
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];
select ops below will repaint viewers to reflect replacement
ViewExprOps.UnSelect[$primary];
ViewExprOps.UnSelect[$copy];
};
MoveSecondaryToPrimary:
PROC[] ~ {
effects: If both primary and move selections are active,
replaces primary selection by move selection and
deletes secondary selection.
local declarations
moveReplacement, primaryExpr, moveExpr: DisplayExpr ← NIL;
moveViewer, primaryViewer: Viewer ← NIL;
IF ~(ViewExprOps.Active[$primary]
AND ViewExprOps.Active[$move])
THEN {
MessageWindow.Append["Insufficient Selections for Move.", TRUE];
MessageWindow.Blink[];
RETURN;
};
[primaryViewer, primaryExpr] ← ViewExprOps.GetSelection[$primary];
[moveViewer, moveExpr] ← ViewExprOps.GetSelection[$move];
moveReplacement ← MathDisplayExpr.Copy[moveExpr];
IF moveViewer = primaryViewer
THEN {
same viewer, so use ReplaceN to make two simultaneous replacements
state: State ← NARROW[primaryViewer.data];
placeholder: DisplayExpr ← MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakePlaceHolder[]];
doubleReplacement: DisplayExpr ← MathDisplayExpr.ReplaceN[state.displayExpr, LIST[[old: primaryExpr, new: moveReplacement], [old: moveExpr, new: placeholder]]];
SetDisplayExpr[primaryViewer, doubleReplacement]; -- mutate viewer expression
ViewExprOps.PaintEnqueue[primaryViewer];
}
ELSE {
different viewers, so do one replacement at a time
ReplaceInViewer[primaryViewer, primaryExpr, moveReplacement];
select ops below will repaint viewers to reflect replacement
ViewExprOps.UnSelect[$primary];
DeleteSelection[$move, FALSE];
};
};
EnterMatrix: Menus.MenuProc ~ {
effects: Replaces active primary selection with a template for a matrix
whose size is chosen from a series op pop-up menus.
Returns without action if any errors occur.
local declarations
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
};
display pop-up menu for # rows then #cols
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
contruct a zero matrix of size nRows x nCols
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]];
replace expr in viewer with replacement
ReplaceInViewer[primaryViewer, primaryExpr, replacement];
repaint viewer to reflect replacement
ViewExprOps.PaintEnqueue[s.exprViewer];
make sure that changes are seen
ViewExprOps.FlushPaintQueue[];
};
EnterVector: Menus.MenuProc ~ {
effects: Replaces active primary selection with a template for a vector
whose size and type is chosen from a series op pop-up menus.
Returns without action if any errors occur.
local declarations
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
};
display pop-up menus for vector type and dimension
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
contruct a zero matrix of size dimension
FOR i:
INT
IN [1..dimension]
DO
elements ← CONS[MathConstructors.MakeInt["0"], elements];
ENDLOOP;
replacement ← MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVector[dimension, elements, rowVec]];
replace expr in viewer with replacement
ReplaceInViewer[primaryViewer, primaryExpr, replacement];
repaint viewer to reflect replacement
ViewExprOps.PaintEnqueue[s.exprViewer];
make sure that changes are seen
ViewExprOps.FlushPaintQueue[];
};
EnterExpression: Menus.MenuProc ~ {
effects: Replaces active primary selection with a template for an expression type
which is chosen from a pop-up menu.
Returns without action if any errors occur.
local declarations
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
};
display pop-up menu of all possible compound expression classes
construct list of choices
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;
do pop-up menu
selection ← PopUpMenu.RequestSelection[label: "Choose Expr", choice: choices];
IF (selection < 1) THEN RETURN; -- no selection or timeout
find selection class name
FOR l:
LIST
OF
ATOM ← choiceClassNames, l.rest
UNTIL l =
NIL
DO
match selection (INT) with class name
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 ~ {
effects: Wraps around primarySelection a template for an expression type
which is chosen from a pop-up menu.
Returns without action if any errors occur.
local declarations
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
};
display pop-up menu of all possible compound expression classes
construct list of choices
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;
do pop-up menu
selection ← PopUpMenu.RequestSelection[label: "Choose Expr", choice: choices];
IF (selection < 1) THEN RETURN; -- no selection or timeout
find selection class name
FOR l:
LIST
OF
ATOM ← choiceClassNames, l.rest
UNTIL l =
NIL
DO
match selection (INT) with class name (ROPE -> 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
perform the wrap
WrapTemplateAround[exprClassName, primaryViewer, primaryExpr];
};
WrapTemplateAround:
PROC [class:
ATOM, viewer: Viewer, expr: DisplayExpr] ~ {
requires: viewer contains expr
modifies: viewer
effects: Wraps a template for an expression of compound class "class" around expr.
The primary selection is changed to be a sibling of the "hot" argument.
local declarations
exprClass: CompoundClass; -- class data associated with class
argExprs: LIST OF MathExpr.TaggedMathExpr ← NIL;
replacement, selectableSibling: DisplayExpr ← NIL;
hotTag: ATOM; -- tag of "hot" argument
lookup classname in database
exprClass ← MathDB.LookupCompoundClass[class];
create expressions for each argument to make LIST OF TaggedMathExpr
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]];
replace expr in viewer with replacement
ReplaceInViewer[viewer, expr, replacement];
find selectable sibling
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;
repaint viewer to reflect replacement
ViewExprOps.PaintEnqueue[viewer];
if possible, select sibling as primary selection
IF selectableSibling # NIL THEN ViewExprOps.Select[$primary, viewer, selectableSibling] ELSE ViewExprOps.UnSelect[$primary];
make sure that changes are seen
ViewExprOps.FlushPaintQueue[];
};
ReplaceWithTemplate:
PROC [class:
ATOM, viewer: Viewer, expr: DisplayExpr] ~ {
requires: viewer contains expr
modifies: viewer.data
effects: Replaces expr in viewer with a template for an class expression.
local declarations
exprClass: CompoundClass;
argExprs: LIST OF MathExpr.TaggedMathExpr ← NIL;
replacement: DisplayExpr ← NIL;
lookup classname in database
exprClass ← MathDB.LookupCompoundClass[class];
create expressions for each argument to make LIST OF TaggedMathExpr
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]];
replace expr in viewer with replacement
ReplaceInViewer[viewer, expr, replacement];
repaint viewer to reflect replacement
ViewExprOps.PaintEnqueue[viewer];
make sure that changes are seen
ViewExprOps.FlushPaintQueue[];
};
EnterAtom: Menus.MenuProc ~ {
effects: Replaces active selection with an atomic expression.
Returns without action if any errors (e.g. parsing) occur.
local declarations
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;
constants
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
};
do pop-up menu
selection ← PopUpMenu.RequestSelection[label: "Choose Atom", choice: choices];
{
ENABLE MathConstructors.badFormat, Convert.Error => {
MessageWindow.Append["Input format error.", TRUE];
MessageWindow.Blink[];
GOTO abort;
};
get text viewer selection, if any (not all choices use this)
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 => {
local declarations
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"];
display a second pop-up menu to choose greek letter from
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
match selection (INT) with class name (ROPE -> ATOM)
selection ← selection - 1;
IF selection = 0 THEN {greekName ← l.first; EXIT};
ENDLOOP;
displayExpr ← MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeGreekVar[greekName]];
};
ENDCASE => RETURN;
replace active selection
ViewExprOps.UnSelect[$primary]; -- unselect currently active selection
ReplaceInViewer[primaryViewer, primaryExpr, displayExpr];
repaint viewer to reflect replacement
ViewerOps.PaintViewer[s.exprViewer, client];
};
};
Undo: Menus.MenuProc ~ {
effects: "Undoes" last action on viewer by restoring previously saved state.
s: State ← NARROW[clientData];
UndoViewer[s.exprViewer];
};
Home: Menus.MenuProc ~ {
effects: Repaints viewer after zeroing all offsets from scrolling.
s: State ← NARROW[clientData];
s.offsetX ← 0.0;
s.offsetY ← 0.0;
ViewerOps.PaintViewer[s.exprViewer, client];
};
VerticalScroll: ViewerClasses.ScrollProc ~ {
effects: Performs verical scrolling of an expression viewer.
s: State ← NARROW[self.data]; -- get viewer state info
compute current top & bottom percentage visible metrics
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 ~ {
effects: Performs horizontal scrolling of an expression viewer.
s: State ← NARROW[self.data]; -- get viewer state info
compute current left & right percentage visible metrics
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] ~ {
effects: Creates a new instance of an expression viewer with banner name.
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 expr to display is defaulted, use empty placeholder
IF expression = NIL THEN expression ← MathConstructors.MakePlaceHolder[];
displayExpr ← MathDisplayExpr.DisplayExprFromExpr[expression];
displayBox ← displayExpr.Format[normal];
instantiate new viewer
v ← ViewerOps.CreateViewer[flavor: $expr, info: [label: name, name: name, scrollable: TRUE, hscrollable: TRUE]];
set its state
s ← NARROW[v.data];
s.displayExpr ← displayExpr;
s.lastDisplayExpr ← displayExpr; -- for UNDO
s.displayBox ← displayBox;
paint new viewer
ViewerOps.PaintViewer[v, all];
return the newly spawned viewer
RETURN[v];
};
SetContents:
PUBLIC
PROC[viewer: Viewer, expr:
EXPR] ~ {
modifies: viewer
effects: Changes the contents of math expression viewer viewer
to be expr.
s: State ← NARROW[viewer.data];
expression: EXPR ← expr;
If expr to display is defaulted, use empty placeholder
IF expression = NIL THEN expression ← MathConstructors.MakePlaceHolder[];
replace entire display expr for viewer
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] ~ {
effects: Returns the contents of viewer as a ROPE in
a format understood by the AlgebraStructures parser.
s: State ← NARROW[viewer.data];
RETURN[MathDisplayExpr.ASRopeFromDisplayExpr[s.displayExpr]];
};
RegisterExprViewerClass:
PROC ~ {
effects: Registers a new viewer class for expression viewers.
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.