PEViewerImpl.mesa
Copyright (C) 1984 by Xerox Corporation. All rights reserved.
Last Edited by Plebon, August 23, 1983 4:17 pm
Routines to manage the Path Editor viewer. These routines are essentially Frank Crow's Quick Viewer with a few improvements.
Last Edited by: Tso, July 12, 1984 1:25:00 pm PDT
DIRECTORY
Atom USING [GetPName],
Buttons USING [Create],
Containers USING [Container, Create, ChildXBound, ChildYBound],
Convert USING [Error, RealFromRope], -- to be used in the future to catch real no. format bugs
Convert USING [RealFromRope],
Graphics USING [Context, Mark, Save, white, GetBounds, SetColor, DrawBox, Restore, Translate],
InputFocus USING [ReleaseButtons],
Menus USING [AppendMenuEntry, ClickProc, CreateEntry, CreateMenu, Menu, MenuProc],
PathEditor USING [SetGlobal, SetLocal, SetSimplify, StoreBetas],
PEViewer,
Process USING [Detach],
Real USING [FixI],
Rope USING [ROPE],
TIPUser USING [InstantiateNewTIPTable,TIPScreenCoords],
ViewerClasses USING [Viewer, ViewerClass, ViewerClassRec, NotifyProc, PaintProc, DestroyProc, ScrollProc],
ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass],
ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection],
WindowManager USING [RestoreCursor];
PEViewerImpl: CEDAR MONITOR
IMPORTS Atom, Buttons, Containers, Convert, Graphics, InputFocus, Menus, PathEditor, Process, Real, TIPUser, ViewerOps, ViewerTools, WindowManager
EXPORTS PEViewer =
BEGIN OPEN PEViewer;
entryHeight: CARDINAL = 12;   -- height of a line of items in a menu
entryVSpace: CARDINAL = 3;   -- vertical leading between lines
entryHSpace: CARDINAL = 8;    -- horizontal space between items on a line
PathViewerData: TYPE = REF PathViewerDataRec;
PathViewerDataRec: TYPE = RECORD [
outer: Containers.Container ← NIL,
viewer: ViewerClasses.Viewer,
biasviewer: ViewerClasses.Viewer,
tensionviewer: ViewerClasses.Viewer,
deltaviewer: ViewerClasses.Viewer,
showbiasviewer: ViewerClasses.Viewer,
showtensionviewer: ViewerClasses.Viewer,
doitviewer: ViewerClasses.Viewer,
showdeltaviewer: ViewerClasses.Viewer,
xTranslation, yTranslation: REAL ← 0.,
xLeft, xRight, yBottom, yTop: REAL ← 0.,
buttonPasserState: ButtonPasserState ← alive,
inputEvent: ATOM,
newEventArrival: CONDITION,
gotANewHit: BOOLEANFALSE,
controlPointX, controlPointY: REAL ← 0.,
redrawProc: RedrawProc,  -- name of procedure for redrawing on window resizing, etc.
buttonProc: ButtonProc,   -- name of procedure for acting on button pushes
quitProc: QuitProc,    -- name of procedure for cleaning up on exit
clientData: REF ANY
];
ButtonPasserState: TYPE = {alive, dying, dead};
MenuData: TYPE = REF MenuDataRec;
MenuDataRec: TYPE = RECORD [
menuButton: ATOM,
pathViewerData: PathViewerData
];
BuildViewer: PUBLIC PROCEDURE [name: Rope.ROPE, menuLabels: LIST OF MenuLabelRec, clientData: REF ANY, redrawProc: RedrawProc, quitProc: QuitProc, buttonProc: ButtonProc] RETURNS [pathViewer: ViewerClasses.Viewer, biasViewer: ViewerClasses.Viewer, tensionViewer: ViewerClasses.Viewer] = {
This routine makes a viewer with the supplied name and menu labels. The supplied redrawProc is called when the screen must be redrawn (because the viewer changes size, etc.). The supplied quitProc is called to clean up on program termination (which occurs when the viewer is destroyed.
menu: Menus.Menu ← Menus.CreateMenu[lines: 2];
pathViewerData: PathViewerData ← NEW[PathViewerDataRec ← [redrawProc: redrawProc, buttonProc: buttonProc, quitProc: quitProc, clientData: clientData]];
Menus.AppendMenuEntry[     -- enter "erase" button
menu: menu,
entry: Menus.CreateEntry[
name: "Erase",
proc: Erase,
clientData: pathViewerData,
documentation: "Erase the viewer"
]
];
Menus.AppendMenuEntry[     -- enter "<" button
menu: menu,
entry: Menus.CreateEntry[
name: "<",
proc: RollLeft,
clientData: pathViewerData,
documentation: "Roll image to left"
]
];
Menus.AppendMenuEntry[     -- enter ">" button
menu: menu,
entry: Menus.CreateEntry[
name: ">",
proc: RollRight,
clientData: pathViewerData,
documentation: "Roll image to right"
]
];
Menus.AppendMenuEntry[     -- enter "simplify" button
menu: menu,
entry: Menus.CreateEntry[
name: "Simplify",
proc: CallSimplify,
clientData: pathViewerData,
documentation: "Reduces number of control points by one"
],
line: 1
];
Menus.AppendMenuEntry[     -- enter "local" button
menu: menu,
entry: Menus.CreateEntry[
name: "Local",
proc: MakeLocal,
clientData: pathViewerData,
documentation: "Set bias and tension locally"
],
line: 1
];
Menus.AppendMenuEntry[     -- enter "global" button
menu: menu,
entry: Menus.CreateEntry[
name: "Global",
proc: MakeGlobal,
clientData: pathViewerData,
documentation: "Set bias and tension globally"
],
line: 1
];
FOR menuLabels ← menuLabels, menuLabels.rest UNTIL menuLabels = NIL DO
Menus.AppendMenuEntry[     -- enter menu buttons 
menu: menu,
entry: Menus.CreateEntry[
name: Atom.GetPName[menuLabels.first.label],
proc: MenuHit,
clientData: NEW[MenuDataRec ← [
menuButton: menuLabels.first.label,
pathViewerData: pathViewerData
] ],
documentation: "",
guarded: menuLabels.first.guarded
]
];
ENDLOOP;
pathViewerData.outer ← Containers.Create[ [
name: name,
menu: menu,
iconic: TRUE,
column: left,
scrollable: FALSE
] ];                     
pathViewerData.viewer ← ViewerOps.CreateViewer[
flavor: $PEViewer,
info: [
parent: pathViewerData.outer,
wx: 0, wy: entryHeight + entryVSpace,  -- position WRT parent
ww: pathViewerData.outer.ww,  -- CHildXBound below
wh: pathViewerData.outer.wh,   -- CHildYBound below
data: pathViewerData,     -- describes the current scene
scrollable: TRUE
]
];
pathViewerData.biasviewer ← Buttons.Create[
info: [
parent: pathViewerData.outer,
wx: 10, wy: 0,
ww: pathViewerData.outer.ww/3, 
ww: 35,  
wh: entryHeight + entryVSpace, 
data: pathViewerData,     -- describes the current scene
scrollable: FALSE,
border: FALSE,
name: "Bias: "
],
proc: ClickBias,
clientData: pathViewerData
];
pathViewerData.showbiasviewer ← ViewerTools.MakeNewTextViewer[
info: [
parent: pathViewerData.outer,
wx: 45, wy: 0,
ww: 55,  
wh: entryHeight + entryVSpace, 
scrollable: FALSE,
border: FALSE
]
];
pathViewerData.tensionviewer ← Buttons.Create[
info: [
parent: pathViewerData.outer,
wx: pathViewerData.outer.ww/3, wy: 0,
ww: pathViewerData.outer.ww/3,  
wx: 100, wy: 0,
ww: 60,  
wh: entryHeight + entryVSpace, 
data: pathViewerData,     -- describes the current scene
scrollable: FALSE,
border: FALSE,
name: "Tension: "
],
proc: ClickTension,
clientData: pathViewerData
];
pathViewerData.showtensionviewer ← ViewerTools.MakeNewTextViewer[
info: [
parent: pathViewerData.outer,
wx: pathViewerData.outer.ww/3, wy: 0,
ww: pathViewerData.outer.ww/3,  
wx: 160, wy: 0,
ww: 55,  
wh: entryHeight + entryVSpace, 
scrollable: FALSE,
border: FALSE
]
];
pathViewerData.doitviewer ← Buttons.Create[
info: [
parent: pathViewerData.outer,
wx: (2*pathViewerData.outer.ww)/3, wy: 0,
ww: pathViewerData.outer.ww/3,  -- CHildXBound below
wx: 215, wy: 0,
ww: 100,  -- CHildXBound below
wh: entryHeight + entryVSpace, 
data: pathViewerData,     -- describes the current scene
scrollable: FALSE,
border: FALSE,
name: "Doit"
],
proc: ClickDoit,
clientData: pathViewerData
];
pathViewerData.deltaviewer ← Buttons.Create[
info: [
parent: pathViewerData.outer,
wx: 315, wy: 0,
ww: 60,  
wh: entryHeight + entryVSpace, 
data: pathViewerData,     -- describes the current scene
scrollable: FALSE,
border: FALSE,
name: "|| Delta: "
],
proc: ClickDelta,
clientData: pathViewerData
];
pathViewerData.showdeltaviewer ← ViewerTools.MakeNewTextViewer[
info: [
parent: pathViewerData.outer,
wx: 375, wy: 0,
ww: 55,  
wh: entryHeight + entryVSpace, 
scrollable: FALSE,
border: FALSE
]
];
Constrain the graphics area to lie in viewer space left over after menu, etc. are drawn.    
Containers.ChildXBound[pathViewerData.outer, pathViewerData.viewer];
Containers.ChildYBound[pathViewerData.outer, pathViewerData.viewer];
Containers.ChildXBound[pathViewerData.outer, pathViewerData.showdeltaviewer];
ViewerTools.SetContents[pathViewerData.showdeltaviewer, "1.0"];
ViewerOps.PaintViewer[pathViewerData.outer, all];
pathViewerData.xTranslation ← pathViewerData.yTranslation ← 0.;
pathViewerData.xLeft ← pathViewerData.yBottom ← 0.;
pathViewerData.xRight ← pathViewerData.viewer.ww;
pathViewerData.yTop ← pathViewerData.viewer.wh;
TRUSTED {Process.Detach[FORK ButtonPasser[pathViewerData]];};
RETURN [pathViewerData.viewer, pathViewerData.showbiasviewer, pathViewerData.showtensionviewer];
};
DrawInViewer: PUBLIC PROCEDURE [pathViewer: ViewerClasses.Viewer, drawProc: DrawProc] = {
This routine calls the PaintProc to draw in the viewer.
doDrawProc: REF DrawProc ← NIL;
IF pathViewer # NIL THEN {
TRUSTED {doDrawProc ← NEW[DrawProc ← drawProc];};
ViewerOps.PaintViewer[
viewer: pathViewer,
hint: client,
whatChanged: doDrawProc,
clearClient: FALSE
];
};
};
ClickBias: Menus.ClickProc = {
This routine processes a hit on the bias button
pathViewerData: PathViewerData ← NARROW[clientData];
IF mouseButton = blue THEN ViewerTools.SetContents[pathViewerData.showbiasviewer, ""];
ViewerTools.SetSelection[pathViewerData.showbiasviewer];
};
ClickTension: Menus.ClickProc = {
This routine processes a hit on the tension button
pathViewerData: PathViewerData ← NARROW[clientData];
IF mouseButton = blue THEN ViewerTools.SetContents[pathViewerData.showtensionviewer, ""];
ViewerTools.SetSelection[pathViewerData.showtensionviewer];
};
ClickDoit: Menus.ClickProc = {
This routine updates the active vertex to the current values of bias and tension.
bias, tension: REAL;
pathViewerData: PathViewerData ← NARROW[clientData];
bias ← Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showbiasviewer]];
tension ← Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showtensionviewer]];
bias ← Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showbiasviewer] ! Convert.Error => {RETURN;}];
tension ← Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showtensionviewer] ! Convert.Error => {RETURN;}];
PathEditor.StoreBetas[NARROW[clientData, PathViewerData].clientData, bias, tension];
};
ClickDelta: Menus.ClickProc = {
This routine processes a hit on the tension button
pathViewerData: PathViewerData ← NARROW[clientData];
IF mouseButton = blue THEN ViewerTools.SetContents[pathViewerData.showdeltaviewer, ""];
ViewerTools.SetSelection[pathViewerData.showdeltaviewer];
};
PaintProc: ViewerClasses.PaintProc = {
This routine repaints the screen with updates.
pathViewerData: PathViewerData ← NARROW[self.data];
x,y: REAL;
x ← pathViewerData.xTranslation;
y ← pathViewerData.yTranslation;
IF whatChanged = NIL THEN {
Graphics.Translate[context, x, y];
pathViewerData.redrawProc[pathViewerData.clientData];
}
ELSE {
Graphics.Translate[context, x, y];
NARROW[whatChanged, REF DrawProc]^[context];
};
}; 
MakeGlobal: Menus.MenuProc = {
This routine changes the bias and tension on a global basis for the active trajectory.
PathEditor.SetGlobal[NARROW[clientData, PathViewerData].clientData];
};
MakeLocal: Menus.MenuProc = {
This routine changes the bias and tension on a local basis for the active trajectory for continuously shaped Beta-splines.
PathEditor.SetLocal[NARROW[clientData, PathViewerData].clientData];
};
CallSimplify: Menus.MenuProc = {
This routine reduces the number of control points of the active trajectory by one and the resulting retains the basic shape of the original spline.
delta: REAL;
pathViewerData: PathViewerData ← NARROW[clientData];
delta ← Convert.RealFromRope[ViewerTools.GetContents[pathViewerData.showdeltaviewer]];
PathEditor.SetSimplify[NARROW[clientData, PathViewerData].clientData, delta];
};
Erase: Menus.MenuProc = {
This routine erases the contents the the graphics viewer.
DoErase: PROC [context: Graphics.Context] = {
mark: Graphics.Mark ← Graphics.Save[context];
Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, Graphics.GetBounds[context]];
Graphics.Restore[context,mark];
};
pathViewerData: PathViewerData ← NARROW[clientData];
DrawInViewer[pathViewerData.viewer, DoErase];
};
RollLeft: Menus.MenuProc = {
This routine moves the image to the left in the graphics viewer. 
pathViewerData: PathViewerData ← NARROW[clientData];
pathViewerData.xTranslation ← pathViewerData.xTranslation - 64;
pathViewerData.redrawProc[clientData: pathViewerData.clientData];
};
RollRight: Menus.MenuProc = {
This routine moves the image to the right in the graphics viewer. 
pathViewerData: PathViewerData ← NARROW[clientData];
pathViewerData.xTranslation ← pathViewerData.xTranslation + 64;
pathViewerData.redrawProc[clientData: pathViewerData.clientData];
};
MenuHit: Menus.MenuProc = {
This routine handles client menu item hits.
menuData: MenuData ← NARROW[clientData];
ButtonMonitor[menuData.pathViewerData, menuData.menuButton];
};
ButtonMonitor: ENTRY PROCEDURE [pathViewerData: PathViewerData, event: ATOM] = {
This routine stores a button event and notifies the button passing process.
pathViewerData.inputEvent ← event;
pathViewerData.gotANewHit ← TRUE;
NOTIFY pathViewerData.newEventArrival;
};
ButtonPasser: PROCEDURE [pathViewerData: PathViewerData] = {
This process monitors button events.
WHILE pathViewerData.buttonPasserState = alive DO
IF GotAButtonHit[pathViewerData] THEN pathViewerData.buttonProc[pathViewerData.clientData, pathViewerData.inputEvent, pathViewerData.controlPointX, pathViewerData.controlPointY];
ENDLOOP;
pathViewerData.buttonPasserState ← dead;
};
GotAButtonHit: ENTRY PROCEDURE [pathViewerData: PathViewerData] RETURNS [gotAHit: BOOLEAN] = {
This routine waits for notification of an event, and determines if the event is a button hit.
IF ~pathViewerData.gotANewHit THEN WAIT pathViewerData.newEventArrival;
gotAHit ← pathViewerData.gotANewHit;
pathViewerData.gotANewHit ← FALSE;
};
NotifyProc: ViewerClasses.NotifyProc = {
This routine responds to input events (from the TIP table).
ENABLE UNWIND => { InputFocus.ReleaseButtons[]; WindowManager.RestoreCursor[]; };
pathViewerData: PathViewerData ← NARROW[self.data];
FOR list: LIST OF REF ANY ← input, list.rest UNTIL list = NIL DO
WITH list.first SELECT FROM
x: ATOM => ButtonMonitor[pathViewerData, x];
z: TIPUser.TIPScreenCoords => {
OPEN pathViewerData;
controlPointX ← z.mouseX - xTranslation;
controlPointY ← z.mouseY - yTranslation;
Expand work area if clicked outside existing bounds.
IF controlPointX > xRight THEN xRight ← controlPointX
ELSE IF controlPointX < xLeft THEN xLeft ← controlPointX;
IF controlPointY > yTop THEN yTop ← controlPointY
ELSE IF controlPointY < yBottom THEN yBottom ← controlPointY;
};
ENDCASE => ERROR;
ENDLOOP;
};
DestroyProc: ViewerClasses.DestroyProc = {
This routine cleans up when the viewer is destroyed.
pathViewerData: PathViewerData ← NARROW[self.data];
DestroyButtonPasser[pathViewerData];
IF pathViewerData.quitProc # NIL THEN pathViewerData.quitProc[pathViewerData.clientData];
pathViewerData.viewer ← NIL;
pathViewerData.outer ← NIL;
};
DestroyButtonPasser: ENTRY PROCEDURE [pathViewerData: PathViewerData] = {
This routine causes the button passer process to kill itself.
pathViewerData.buttonPasserState ← dying;
NOTIFY pathViewerData.newEventArrival;
};
ScrollProc: ViewerClasses.ScrollProc = {
This routine handles scrollbar mouse hits.
pathViewerData: PathViewerData ← NARROW[self.data];
SELECT op FROM
up => {
pathViewerData.yTranslation ← pathViewerData.yTranslation + amount;
pathViewerData.redrawProc[clientData: pathViewerData.clientData];
};
down => {
pathViewerData.yTranslation ← pathViewerData.yTranslation - amount;
pathViewerData.redrawProc[clientData: pathViewerData.clientData];
};
thumb => {
pathViewerData.yTranslation ← - (pathViewerData.yBottom + (1. - amount/100.) * (pathViewerData.yTop - pathViewerData.yBottom));
pathViewerData.redrawProc[clientData: pathViewerData.clientData];
};
query => {
OPEN pathViewerData;
RETURN [Real.FixI[100. - (-yTranslation + self.ch - yBottom) * 100. / (yTop - yBottom)], Real.FixI[100. - (-yTranslation - yBottom) * 100. / (yTop - yBottom)]];
};
ENDCASE;
};
Init: PROCEDURE [] = {
This routine starts up the viewer.
pathViewerClass: ViewerClasses.ViewerClass;
pathViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
paint: PaintProc,
notify: NotifyProc,
destroy: DestroyProc,
scroll: ScrollProc,
tipTable: TIPUser.InstantiateNewTIPTable["PEViewer.TIP"],
icon: document]
];
ViewerOps.RegisterViewerClass[$PEViewer, pathViewerClass];
};
Init[];
END.