ViewExprOpsImpl.mesa
Carl Waldspurger ,August 21, 1986 3:28:21 pm PDT
DIRECTORY
ViewerClasses USING [Viewer],
ViewerOps,
List USING [AList, PutAssoc, Assoc],
MathDisplayExpr,
ViewExprOps;
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: BOOLFALSE, -- 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 ATOMNIL; -- 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]];
*** this is a hack ***
IF flavor = $primary THEN UnSelect[$keyboard];
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.