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: BOOLEAN ← FALSE,
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.