Carl Waldspurger, August 18, 1986 10:38:13 pm PDT
ViewExprImpl.mesa
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
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
];
Global Data (global only to this module)
kbBuffer: RECORD[type: {integer, real, variable}, data: ROPE];
DeleteSelection: PROC[flavor: ATOM, reselect: BOOLTRUE] ~ {
effects: Deletes selection of type flavor, if active.
Replaces selection with an expression "placeholder".
This placeholder remians selected iff reselect is TRUE.
local declarations
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}];
reformat entire display expr
state.displayBox ← state.displayExpr.Format[normal];
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.scrollable ← TRUE;
self.hscrollable ← TRUE;
self.menu ← Menus.CreateMenu[lines: 2];
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["ToRope", ConvertToRope, self.data], 0];
self.menu.AppendMenuEntry[Menus.CreateEntry["Debug", EnterDebugger, self.data], 0];
self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithMatrix", EnterMatrix, self.data], 1];
self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithExpr", EnterExpression, self.data], 1];
self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithAtom", EnterAtom, self.data], 1];
self.menu.AppendMenuEntry[Menus.CreateEntry["WrapWithExpr", WrapExpression, self.data], 1];
};
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];
xy: TIPUser.TIPScreenCoords;
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];
};
coords: TIPUser.TIPScreenCoords => {
xy ← coords;
terminate any pending keyboard input
ViewExprOps.UnSelect[$keyboard];
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}]];
};
$PrimaryDelete => {
DeleteSelection[$primary];
};
$DoPendingCopy => {
IF ViewExprOps.Active[$copy] THEN CopySecondaryToPrimary[];
};
$DoPendingMove => {
IF ViewExprOps.Active[$move] THEN MoveSecondaryToPrimary[];
};
ENDCASE => RETURN; -- take no action given unrecognized TIP actions
};
ENDCASE => ERROR;
flush any pending paint requests
ViewExprOps.FlushPaintQueue[];
};
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;
selectedViewer: Viewer ← NIL;
selectedExpr: DisplayExpr ← 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
IF ViewExprOps.Active[$primary] THEN {
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[$primary];
highlight ← CONS[[expr: selectedExpr, color: ImagerColor.ColorFromAtom[$Invert]], highlight];
};
IF ViewExprOps.Active[$copy] THEN {
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[$copy];
highlight ← CONS[[expr: selectedExpr, color: ImagerBackdoor.MakeStipple[8421H, TRUE]], highlight];
};
IF ViewExprOps.Active[$move] THEN {
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[$move];
highlight ← CONS[[expr: selectedExpr, color: ImagerBackdoor.MakeStipple[8020H, TRUE]], highlight];
};
IF ViewExprOps.Active[$keyboard] THEN {
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[$keyboard];
highlight ← CONS[[expr: selectedExpr, color: ImagerBackdoor.MakeStipple[00F0H, TRUE]], highlight];
};
spray the bits onto screen via imager
MathDisplayExpr.Paint[s.displayExpr, context, s.physicalBox, highlight];
};
some control char abbreviations:
ctrlI: CHAR = '\011; -- control-I
LexType: TYPE ~ {ucAlpha, lcAlpha, digit, decimal, op, other};
LexChar: PROC[c: CHAR] RETURNS[LexType] ~ {
effects: Returns the lexical type of c
SELECT c FROM
IN ['0..'9] => RETURN[digit];
IN ['a..'z] => RETURN[lcAlpha];
IN ['A..'Z] => RETURN[ucAlpha];
'. => RETURN[decimal];
'+, '- , '*, '^, '=, '>, '<, '&, '|, '/, '(, '~, ctrlI => RETURN[op];
ENDCASE => RETURN[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, $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]]];
ctrlI => action ← NEW[WrapAction ← [wrap[wrapAround, $integration]]];
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
digit, ucAlpha, lcAlpha => {
kbBuffer.data ← kbBuffer.data.Cat[ropeC];
action ← NEW[ReplaceAction ← [replace[$keyboard, variable, kbBuffer.data, $keyboard]]];
};
ENDCASE => ERROR;
};
ENDCASE => ERROR;
};
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: BOOLTRUE;
[selectedViewer, selectedExpr] ← ViewExprOps.GetSelection[r.oldSelect ! ViewExprOps.noSelection => {ok ← FALSE; CONTINUE}];
IF ok THEN {
state: State ← NARROW[selectedViewer.data];
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
MathDisplayExpr.Replace[selectedExpr, replacement ! MathDisplayExpr.replaceAll => {state.displayExpr ← replacement; CONTINUE}];
reformat display expr
state.displayBox ← state.displayExpr.Format[normal];
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: BOOLTRUE;
[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];
};
ConvertToRope: 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];
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 ~ {
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
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}];
reformat primary expression
primaryState.displayBox ← primaryState.displayExpr.Format[normal];
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
primaryState, secondaryState: State;
replacement, primaryExpr, moveExpr: DisplayExpr ← NIL;
moveViewer, primaryViewer: Viewer ← NIL;
IF ~(ViewExprOps.Active[$primary] AND ViewExprOps.Active[$move]) THEN {
MessageWindow.Append["Insufficient Selections for Move.", TRUE];
RETURN;
};
[primaryViewer, primaryExpr] ← ViewExprOps.GetSelection[$primary];
[moveViewer, moveExpr] ← ViewExprOps.GetSelection[$move];
primaryState ← NARROW[primaryViewer.data];
secondaryState ← NARROW[moveViewer.data];
replacement ← MathDisplayExpr.Copy[moveExpr];
MathDisplayExpr.Replace[primaryExpr, replacement ! MathDisplayExpr.replaceAll => {primaryState.displayExpr ← replacement; CONTINUE}];
reformat primary expression
primaryState.displayBox ← primaryState.displayExpr.Format[normal];
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 ROPELIST["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
nRows, nCols: INT ← 0;
rows: LIST OF LIST OF EXPRNIL;
replacement: DisplayExpr ← NIL;
ok: BOOLTRUE;
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
};
display pop-up menu for # rows then #cols
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
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 EXPRNIL; -- 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
MathDisplayExpr.Replace[primaryExpr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr ← replacement; CONTINUE}];
reformat entire display expr
s.displayBox ← s.displayExpr.Format[normal];
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 ROPENIL;
choiceClassNames: LIST OF ATOMNIL;
selection: INT ← 0;
ok: BOOLTRUE;
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
};
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, timeOut: 10];
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 ROPENIL;
choiceClassNames: LIST OF ATOMNIL;
selection: INT ← 0;
exprClassName: ATOM;
ok: BOOLTRUE;
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
};
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, timeOut: 10];
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.data
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
s: State ← NARROW[viewer.data];
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: BOOLFALSE;
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
MathDisplayExpr.Replace[expr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr ← replacement; CONTINUE}];
reformat entire display expr
s.displayBox ← s.displayExpr.Format[normal];
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[s.exprViewer];
if possible, select sibling as primary selection
IF selectableSibling # NIL THEN ViewExprOps.Select[$primary, s.exprViewer, 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;
s: State ← NARROW[viewer.data];
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
MathDisplayExpr.Replace[expr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr ← replacement; CONTINUE}];
reformat entire display expr
s.displayBox ← s.displayExpr.Format[normal];
repaint viewer to reflect replacement
ViewExprOps.PaintEnqueue[s.exprViewer];
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 ROPELIST["integer", "real", "variable"];
selection: INT ← 0;
ok: BOOLTRUE;
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
};
do pop-up menu
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;
replace active selection
ViewExprOps.UnSelect[$primary]; -- unselect currently active selection
MathDisplayExpr.Replace[primaryExpr, displayExpr ! MathDisplayExpr.replaceAll => { s.displayExpr ← displayExpr; CONTINUE}];
reformat entire display expr
s.displayBox ← s.displayExpr.Format[normal];
repaint viewer to reflect replacement
ViewerOps.PaintViewer[s.exprViewer, client];
};
EXITS
abort => NULL;
};
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: EXPRNIL, 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: BOXNIL; -- box for displayExpr
If expr to display is defaulted, use empty placeholder
IF expression = NIL THEN expression ← MathConstructors.MakePlaceHolder[];
displayExpr ← MathDisplayExpr.DisplayExprFromExpr[expression];
displayBox ← MathDisplayExpr.Format[displayExpr, normal];
instantiate new viewer
v ← ViewerOps.CreateViewer[flavor: $expr];
set its state
s ← NARROW[v.data];
v.name ← name; -- set caption for viewer
s.displayExpr ← displayExpr;
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[];
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] ~ {
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"],
paint: PaintExprViewer,
scroll: VerticalScroll,
hscroll: HorizontalScroll
]]
];
};
RegisterExprViewerClass[];
END.