ViewExprOpsImpl:
CEDAR
PROGRAM
IMPORTS ViewerOps, MathDisplayExpr, List
EXPORTS ViewExprOps ~ BEGIN
Type Abbreviations From Imported Interfacs
Viewer: TYPE ~ ViewerClasses.Viewer;
DisplayExpr: TYPE ~ MathDisplayExpr.DisplayExpr;
Types
Selection: TYPE ~ REF SelectionRep;
SelectionRep:
TYPE ~
RECORD [
active: BOOL ← FALSE, -- TRUE iff selection is currently active
flavor: ATOM, -- type of selection
viewer: Viewer ← NIL, -- viewer containing selection
expr: DisplayExpr ← NIL -- selected expression within viewer
];
Global Data (global within this module only)
paintQueue: LIST OF Viewer ← NIL; -- list of viewers waiting to be painted
GlobalSelections: List.AList ← NIL; -- list of active selections
SelectionFlavors: LIST OF ATOM ← NIL; -- list of each flavor in GlobalSelections
Paint "Queue" Operations
PaintEnqueue:
PUBLIC PROC[v: Viewer] ~ {
modifies: paintQueue
effects: If v is not already enqueued, adds v to paintQueue
FOR l:
LIST
OF Viewer ← paintQueue, l.rest
UNTIL l =
NIL
DO
IF v = l.first THEN RETURN; -- don't need to enqueue if already waiting
ENDLOOP;
if above loop doesn't return, then we need to add v to queue
paintQueue ← CONS[v, paintQueue];
};
PaintDequeue:
PUBLIC PROC[v: Viewer] ~ {
modifies: paintQueue
effects: Removes v from paintQueue.
cons up new queue skipping over v
newQueue: LIST OF Viewer ← NIL;
FOR l:
LIST
OF Viewer ← paintQueue, l.rest
UNTIL l =
NIL
DO
IF v # l.first THEN newQueue ← CONS[l.first, newQueue];
ENDLOOP;
make newQueue active
paintQueue ← newQueue;
};
FlushPaintQueue:
PUBLIC PROC[] ~ {
modifies: paintQueue
effects: Paints each viewer in paintQueue and resets paintQueue (NIL).
FOR l:
LIST
OF Viewer ← paintQueue, l.rest
UNTIL l =
NIL
DO
ViewerOps.PaintViewer[l.first, client];
ENDLOOP;
paintQueue ← NIL;
};
Selection Operations
noSelection: PUBLIC ERROR = CODE;
GetSelection:
PUBLIC PROC[flavor:
ATOM]
RETURNS[Viewer, DisplayExpr] ~ {
effects: Returns the viewer and display expression associated with
currently active selection of type flavor.
SIGNALS noSelection if no such selection exists.
selection: Selection ← NARROW[List.Assoc[key: flavor, aList: GlobalSelections]];
IF (selection # NIL) AND (selection.active) THEN RETURN[selection.viewer, selection.expr];
ERROR noSelection; -- no active selection of type flavor exists
};
Active:
PUBLIC
PROC[flavor:
ATOM]
RETURNS[
BOOL] ~ {
effects: Returns TRUE if selection of type flavor is currently active.
Otherwise returns FALSE.
selection: Selection ← NARROW[List.Assoc[key: flavor, aList: GlobalSelections]];
IF (selection # NIL) AND (selection.active) THEN RETURN[TRUE] ELSE RETURN[FALSE];
};
Select:
PUBLIC PROC[flavor:
ATOM, viewer: Viewer, expr: DisplayExpr] ~ {
modifies: GlobalSelections, SelectionFlavors
effects: Sets selection flavor to expr in viewer and repaints viewer.
If a previous selection existed, its associated viewer is repainted.
If expr is unselectable, primary selection is cleared (and viewer repainted).
save old selection
oldSelection: Selection ← NARROW[List.Assoc[key: flavor, aList: GlobalSelections]];
newSelection: Selection ← NEW[SelectionRep ← [active: TRUE, flavor: flavor, viewer: viewer, expr: expr]];
*** this is a hack ***
IF flavor = $primary THEN UnSelect[$keyboard];
set new selection if expr is selectable
IF ~expr.Selectable[] THEN newSelection ← NIL;
GlobalSelections ← List.PutAssoc[key: flavor, val: newSelection, aList: GlobalSelections];
AddNewFlavor[flavor]; -- update "all flavors" list
IF (oldSelection # NIL) AND (oldSelection.active) THEN PaintEnqueue[oldSelection.viewer];
IF newSelection.active THEN PaintEnqueue[newSelection.viewer];
};
UnSelect:
PUBLIC PROC[flavor:
ATOM] ~ {
modifies: GlobalSelections
effects: UnSelects current selection of type flavor.
Repaints associated viewer if selection was active.
save old selection
oldSelection: Selection ← NARROW[List.Assoc[key: flavor, aList: GlobalSelections]];
reset old selection
GlobalSelections ← List.PutAssoc[key: flavor, val: NIL, aList: GlobalSelections];
repaint viewer if necessary
IF (oldSelection # NIL) AND (oldSelection.active) THEN PaintEnqueue[oldSelection.viewer];
};
UnSelectViewer:
PUBLIC
PROC[v: Viewer] ~ {
modifies: GlobalSelections
effects: UnSelects all selections associated with viewer v.
local declarations
viewer: Viewer ← NIL;
displayExpr: DisplayExpr ← NIL;
FOR flavors:
LIST
OF
ATOM ← SelectionFlavors, flavors.rest
UNTIL flavors =
NIL
DO
[viewer, displayExpr] ← GetSelection[flavors.first ! noSelection => {viewer ← NIL; CONTINUE}];
IF v = viewer THEN UnSelect[flavors.first];
ENDLOOP;
};
AddNewFlavor:
PROC[flavor:
ATOM] ~ {
modifies: SelectionFlavors
effects: SelectionFlavors ← UNION(SelectionFlavors, flavor)
FOR flavors:
LIST
OF
ATOM ← SelectionFlavors, flavors.rest
UNTIL flavors =
NIL
DO
IF flavors.first = flavor THEN RETURN; -- already exists in list
ENDLOOP;
SelectionFlavors ← CONS[flavor, SelectionFlavors]; -- not already in list, so add it
};
END.