SVMenusImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on January 28, 1987 11:16:05 pm PST
Contents: Routines for building the control panel on each solid viewer.
DIRECTORY
AtomButtons, AtomButtonsTypes, Buttons, CodeTimer, Feedback, FS, GraphicsButton, Imager, IO, Menus, PopUpSelection, Rope, SlackProcess, SV2d, SV3d, SVDraw, SVEditUser, SVEvent, SVFiles, SVInterfaceTypes, SVMenus, SVModelTypes, SVMouseEvent, SVSceneTypes, SVUserInput, SVWindow, VFonts, ViewerClasses, ViewerTools;
SVMenusImpl:
CEDAR
PROGRAM
IMPORTS AtomButtons, Buttons, CodeTimer, Feedback, FS, GraphicsButton, Imager, IO, PopUpSelection, Rope, SlackProcess, SVDraw, SVEditUser, SVEvent, SVFiles, SVMouseEvent, SVUserInput, SVWindow, VFonts, ViewerTools
EXPORTS SVMenus =
BEGIN
Camera: TYPE = SVModelTypes.Camera;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
EditToolData: TYPE = SVInterfaceTypes.EditToolData;
FeedbackData: TYPE = AtomButtonsTypes.FeedbackData;
FileCamera: TYPE = SVSceneTypes.FileCamera;
MouseButton: TYPE = Menus.MouseButton;
Plane: TYPE = SV3d.Plane;
Point2d: TYPE = SV2d.Point2d;
Scene: TYPE = SVSceneTypes.Scene;
Viewer: TYPE = ViewerClasses.Viewer;
SVData: TYPE = SVInterfaceTypes.SVData;
entryHeight: CARDINAL = 15; -- height of a line of items
entryVSpace: CARDINAL = 2; -- vertical leading between lines
entryHSpace: CARDINAL = 2; -- horizontal space between items on a line
column1: CARDINAL = 200; -- horizontal space between margin and column 1;
column2: CARDINAL = 250; -- horizontal space between margin and column 2.
column3: CARDINAL = 500; -- horizontal space between margin and column 3;
fullColumn: CARDINAL = 600; -- the width of the standard large left column.
boldFont: VFonts.Font; -- initialized in Init below;
PopUpMenuEntry:
TYPE =
RECORD [
name: Rope.ROPE,
input: LIST OF REF ANY
];
ExtractChoiceList:
PROC [menu:
LIST
OF PopUpMenuEntry]
RETURNS [choices:
LIST
OF Rope.
ROPE] = {
pos: LIST OF Rope.ROPE;
newCell: LIST OF Rope.ROPE;
Non-destructive (copies the menu names).
IF menu = NIL THEN ERROR;
choices ← CONS[menu.first.name, NIL];
pos ← choices;
FOR l:
LIST
OF PopUpMenuEntry ← menu.rest, l.rest
UNTIL l =
NIL
DO
newCell ← CONS[l.first.name, NIL];
pos.rest ← newCell;
pos ← newCell;
ENDLOOP;
};
QueuePopUpMenuAction:
PROC [label: Rope.
ROPE, menu:
LIST
OF PopUpMenuEntry, svData: SVData, onceOnly:
BOOL ←
TRUE] = {
index: NAT;
choices: LIST OF Rope.ROPE ← ExtractChoiceList[menu];
DO
index ← PopUpSelection.Request[header: label, choice: choices];
IF index < 0 THEN ERROR;
IF index = 0 THEN RETURN;
FOR entries:
LIST
OF PopUpMenuEntry ← menu, entries.rest
UNTIL entries =
NIL
DO
IF index = 1
THEN {
SVUserInput.EventNotify[svData, entries.first.input];
EXIT;
};
index ← index - 1;
REPEAT
FINISHED => ERROR;
ENDLOOP;
IF onceOnly THEN RETURN;
ENDLOOP;
};
BuildControlPanel:
PUBLIC
PROC [svData: SVData, windowMenu: Menus.Menu, workingDirectory: Rope.
ROPE] = {
BuildFileMenuLine[svData];
BuildMasterMenuLine[svData];
BuildGravityLine[svData];
BuildSpecial3DLine[svData];
BuildFeedbackLine[svData];
};
Build All of the Control Panel Menus
BuildFileMenuLine:
PROC [svData: SVData] = {
Clear, Restore, Get, Store, Save, Split, Erase, Interpress, Script, CastRays, Revive!
nextX: INTEGER;
interpressMenu: AtomButtons.ButtonLineEntry ← [popUpButton[
"Interpress",
LIST[
[LIST[$ToIP], "ToIP", "Writes an interpress master to selected filename", boldFont],
[LIST[$StorePoly], "StorePoly", "Stores a polygonal scene representation to selected filename"]
],
-1, FALSE, boldFont
]];
svData.height ← svData.height + entryVSpace;
Space down from the top of the viewer.
nextX ← AtomButtons.BuildButtonLine [svData.outer, 0, svData.height, svData, SVUserInput.EventNotify,
LIST[
[button["Clear", LIST[LIST[$Clear]], -1, FALSE, NIL, ConfirmClear]],
[button["Restore", LIST[LIST[$Restore]], -1, FALSE, NIL, ConfirmReset]],
[button["Get", LIST[LIST[$Get]], -1, FALSE, NIL, ConfirmGet]],
[button["Store", LIST[LIST[$Store]], -1, FALSE, NIL, ConfirmStore]],
[button["Save", LIST[LIST[$Save]]]],
[button["Split", LIST[LIST[$Split]]]],
[button["Erase", LIST[LIST[$Erase]]]],
interpressMenu
]];
nextX ← AtomButtons.BuildUnQueuedButtonLine [svData.outer, nextX, svData.height, svData,
LIST[
["Script", button, ScriptMenu, NIL, 0, -1, FALSE, NIL, boldFont],
["CastRays", button, CastRaysMenu, NIL, 0, -1, FALSE, NIL, boldFont],
["Revive!", button, ReviveButton]
]];
svData.height ← svData.height + entryVSpace + entryHeight;
};
ReviveButton: Menus.ClickProc = {
svData: SVData ← NARROW[clientData];
SVMouseEvent.ResetMouseMachinery[svData];
CodeTimer.ResetTable[CodeTimer.GetTable[$Solidviews]];
SlackProcess.Restart[svData.slackHandle];
svData.refresh.suppressRefresh ← FALSE;
svData.aborted ← ALL[FALSE];
IF mouseButton#blue THEN SVWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, svData: svData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE]
};
BuildMasterMenuLine:
PROC [svData: SVData] = {
This section implements a set of buttons, text windows, and options listers in this format:
Draw: BBox BSphere Scene Coords Point Cross Color B&W Dither CoordsMode
nextX: INTEGER;
nextX ← AtomButtons.BuildUnQueuedButtonLine [svData.outer, 0, svData.height, svData,
LIST[
["Debug", button, DebugMenu, NIL, 0, -1, FALSE, NIL, boldFont]
]];
nextX ← AtomButtons.BuildButtonLine [svData.outer, nextX, svData.height, svData, SVUserInput.EventNotify,
LIST[
[button["Refresh!", LIST[LIST[$DrawSceneButton]]]],
[button["DrawColor", LIST[LIST[$DrawColor]]]],
[button["DrawB&W", LIST[LIST[$DrawBlackAndWhite]]]],
[button["KillSelections", LIST[LIST[$KillSelectionsButton]]]],
[button["DeleteJacks", LIST[LIST[$DeleteJacksButton]]]]
]];
svData.height ← svData.height + entryVSpace + entryHeight;
}; -- end of BuildMasterMenuLine
GravityExtentData: TYPE = REF GravityExtentDataObj;
GravityExtentDataObj: TYPE = SVInterfaceTypes.GravityExtentDataObj;
GravityExtentRepaint:
PROC [dc: Imager.Context, clientData:
REF
ANY, buttonData:
REF
ANY, button: Viewer] = {
caretPoint: Point2d;
ged: GravityExtentData ← NARROW[buttonData];
extent: REAL ← ged.extent;
Imager.TranslateT[dc, [button.ww, button.wh/2.0]];
caretPoint ← [-extent, 0.0];
SVDraw.DrawCaret[dc, caretPoint];
};
BuildGravityLine:
PROC [svData: SVData] = {
nextX: NAT ← 0;
svData.hitTest.gravityTypeMenu ←
AtomButtons.BuildEnumTypeSelection[viewer: svData.outer, x: 0, y: svData.height, maxWidth: 144,
clientData: svData,
handleProc: SVUserInput.EventNotify,
title: "GravType:", default: "PreferPoints",
borderOnButtons: TRUE, style: flipThru, allInOneRow: TRUE,
buttonNames: LIST["StrictDistance", "PreferLines", "PreferPoints"],
atom: $GravityChoiceChange];
svData.hitTest.gravityType ← pointsPreferred;
nextX ← svData.hitTest.gravityTypeMenu.nextx;
nextX ← AtomButtons.BuildButtonLine[
svData.outer, nextX + entryHSpace, svData.height, svData, SVUserInput.EventNotify,
LIST[
[label["GravExtent:"]]
]];
nextX ← GraphicsButton.BuildGraphicsButton[
container: svData.outer,
x: nextX + entryHSpace,
y: svData.height,
w: 60, -- changed from 72 to make more room in line
h: entryHeight,
clientData: svData,
choices:
LIST[
[LIST[$GravityExtentChange, $ValueUp]],
[LIST[$GravityExtentChange, $InitialValue]],
[LIST[$GravityExtentChange, $ValueDown]]],
handleProc: SVUserInput.EventNotify,
repaintProc: GravityExtentRepaint,
buttonData: NEW[GravityExtentDataObj ← [extent: SVEditUser.GetDefaultGravityExtent[]]],
updateProc: GravityExtentInSVData
];
[svData.hitTest.gravButton, nextX] ←
AtomButtons.BuildTwoStateButton[viewer: svData.outer, x: nextX + 2*entryHSpace, y: svData.height,
clientData: svData,
handleProc: SVUserInput.EventNotify,
name: "Gravity",
action: LIST[$ToggleGravity],
init: on];
[svData.hitTest.midpointButton, nextX] ←
AtomButtons.BuildTwoStateButton[viewer: svData.outer, x: nextX + entryHSpace, y: svData.height,
clientData: svData,
handleProc: SVUserInput.EventNotify,
name: "Midpoints",
action: LIST[$ToggleMidpoints],
init: off];
[svData.hitTest.heuristicsButton, nextX] ←
AtomButtons.BuildTwoStateButton[viewer: svData.outer, x: nextX + entryHSpace, y: svData.height,
clientData: svData,
handleProc: SVUserInput.EventNotify,
name: "Heuristics",
action: LIST[$ToggleHeuristics],
init: IF SVEditTool.GetDefaultHeuristics[] THEN on ELSE off];
svData.height ← svData.height + entryHeight;
};
BuildSpecial3DLine:
PROC [svData: SVData] = {
This section implements a set of buttons, text windows, and options listers in this format:
Text: <somefile> Style: <drawStyle> x,y,z: <x, y, z> Selected 2Buffer
AIS: <filename> Kill Selections DeleteJacks
nextX: INTEGER;
bigStringSize: NAT ← VFonts.StringWidth["///Users/Name.pa/Solidviews/PictureName.pic"];
smallStringSize: NAT ← VFonts.StringWidth["WireframeStyle"];
threeNumberSize: NAT ← VFonts.StringWidth["18, 18, 18"];
nextX ← AtomButtons.BuildButtonLine [svData.outer, 0, svData.height, svData, SVUserInput.EventNotify,
LIST[
[button["Style:", LIST[LIST[$StylePrompt]] ]],
[label["Shaded", SceneStyleInSVData, smallStringSize]],
[button["CoordsMode", LIST[LIST[$ShowCoordsMode]], -1, FALSE, NIL, NIL, ShowCoordsInSVData]],
[button["x,y,z:", LIST[LIST[$XYZPrompt]]]],
[text["0, 0, 0", XYZViewInSVData, threeNumberSize]],
[button["Selected", LIST[LIST[$Selected]], -1, FALSE, NIL, NIL, SelectedInSVData]],
[button["2Buffer", LIST[LIST[$DoubleBuffer]], -1, FALSE, NIL, NIL, DoubleBufferInSVData]]
]];
svData.height ← svData.height + entryVSpace + entryHeight;
}; -- end of BuildSpecial3DLine
BuildFeedbackLine:
PROC [svData: SVData] = {
nextX:
NAT ← AtomButtons.BuildButtonLine[svData.outer, 0, svData.height, svData, SVUserInput.EventNotify,
LIST[
[label["Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Line", FeedbackLineInSVData, fullColumn]]
]];
svData.height ← svData.height + entryHeight;
};
File Line Menus:
ConfirmClear:
PUBLIC
PROC [clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
scene: Scene ← svData.scene;
rope: Rope.ROPE;
dirty: BOOL;
dirty ← svData.outer.newVersion;
IF dirty THEN rope ← "Confirm discard of edits"
ELSE rope ← "Confirm emptying of viewer";
Feedback.AppendHerald[svData.feedback, rope, oneLiner];
};
ConfirmReset:
PUBLIC
PROC [clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
scene: Scene ← svData.scene;
dirty: BOOL;
dirty ← svData.outer.newVersion;
IF dirty THEN Feedback.PutFHerald[svData.feedback, oneLiner, "Confirm discard of edits"]
ELSE Feedback.PutFHerald[svData.feedback, oneLiner, "Confirm reset of %g", [rope[scene.name]]];
}; -- end of ConfirmReset
ConfirmSave:
PUBLIC
PROC [clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
scene: Scene ← svData.scene;
exists: BOOL;
rope: Rope.ROPE;
exists ← SVFiles.FileExists[scene.name];
IF exists THEN rope ← IO.PutFR["Confirm Save to %g [Old File]", [rope[scene.name]]]
ELSE {
rope ← IO.PutFR["%g doesn't exist. Use Store", [rope[scene.name]]];
Feedback.Blink[svData.feedback]
};
Feedback.Append[svData.feedback, rope, oneLiner];
};
ConfirmStore:
PUBLIC
PROC [clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
editToolData: EditToolData ← NARROW[svData.editToolData];
scene: Scene ← svData.scene;
picName: Rope.ROPE ← ViewerTools.GetSelectionContents[];
fullName, wdir: Rope.ROPE;
exists: BOOL;
success: BOOL ← TRUE;
rope: Rope.ROPE;
IF Rope.Length[picName] = 0
THEN {
Feedback.Append[svData.feedback, "Please select a file name.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
wdir ← editToolData.originalWorkingDirectory;
[fullName,,] ←
FS.ExpandName[picName, wdir
!
FS.Error =>
IF error.group = user
THEN {
Feedback.Append[svData.feedback, Rope.Concat["Store: ", error.explanation], oneLiner];
success ← FALSE;
CONTINUE;
}
];
exists ← SVFiles.FileExists[fullName];
IF exists THEN rope ← IO.PutFR["Confirm Store of %g [Old File]", [rope[fullName]]]
ELSE rope ← IO.PutFR["Confirm Store to %g [New File]", [rope[fullName]]];
Feedback.AppendHerald[svData.feedback, rope, oneLiner];
};
ConfirmGet:
PUBLIC
PROC [clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
editToolData: EditToolData ← NARROW[svData.editToolData];
scene: Scene ← svData.scene;
picName: Rope.ROPE ← ViewerTools.GetSelectionContents[];
fullName, wdir: Rope.ROPE;
success: BOOL ← TRUE;
dirty: BOOL;
IF Rope.Length[picName] = 0
THEN {
Feedback.Append[svData.feedback, "Please select a file name.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
wdir ← editToolData.originalWorkingDirectory;
[fullName,,] ←
FS.ExpandName[picName, wdir
!
FS.Error =>
IF error.group = user
THEN {
Feedback.Append[svData.feedback, Rope.Concat["Get: ", error.explanation], oneLiner];
success ← FALSE;
CONTINUE;
}
];
IF NOT success THEN RETURN;
dirty ← svData.outer.newVersion;
IF dirty THEN Feedback.PutF[svData.feedback, oneLiner, "Confirm discard of edits and load of %g", [rope[fullName]]]
ELSE Feedback.PutF[svData.feedback, oneLiner, "Confirm load of %g", [rope[fullName]]];
}; -- end of ConfirmGet
Master Line Menus:
DebugMenu: Menus.ClickProc = {
svData: SVData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ←
LIST [
["DrawBoundBoxes", LIST [$DrawBoundBoxes]],
["DrawBoundSpheres", LIST [$DrawBoundSpheres]],
["DrawCoordSystems", LIST [$DrawCoordSystems]],
["DrawPoint", LIST [$DrawPoint]],
["Draw Selection Box", LIST [$DrawSelectionBox]],
["CrossHairs", LIST [$CrossHairs]],
["TestGravity", LIST [$TestGravity]],
["Reset Statistics", LIST [$ResetStatistics]],
["Print Statistics", LIST [$PrintStatistics]]
];
QueuePopUpMenuAction["Debug", menu, svData, mouseButton#blue];
};
CastRaysMenu: Menus.ClickProc = {
svData: SVData ← NARROW[clientData];
menu:
LIST
OF PopUpMenuEntry ←
LIST [
["RayCast", LIST [$RayCast]],
["StopRays", LIST [$StopRays]],
["ARay", LIST [$ARay]]
];
choices: LIST OF Rope.ROPE ← ExtractChoiceList[menu];
index: NAT ← PopUpSelection.Request[header: "SVCastRays", choice: choices];
SELECT index
FROM
=0 => RETURN;
=1 => SVEvent.RayCast[LIST[$RayCast], svData];
=2 => SVEvent.StopRays[LIST[$StopRays], svData];
=3 => SVEvent.ARay[LIST[$ARay], svData];
ENDCASE => ERROR;
QueuePopUpMenuAction["SVCastRays", menu, svData, mouseButton#blue];
};
ScriptMenu: Menus.ClickProc = {
svData: SVData ← NARROW[clientData];
menu:
LIST
OF PopUpMenuEntry ←
LIST [
["OpenScript", LIST [$ScriptAction, $Open]],
["AppendToScript", LIST [$ScriptAction, $Append]],
["CloseScript", LIST [$ScriptAction, $Close]],
["PlaybackScript", LIST [$ScriptAction, $Playback]],
["FastPlayScript", LIST [$ScriptAction, $FastPlay]]
];
choices: LIST OF Rope.ROPE ← ExtractChoiceList[menu];
index: NAT ← PopUpSelection.Request[header: "Script", choice: choices];
SELECT index
FROM
=0 => RETURN;
=1 => SVEvent.OpenScript[parent, clientData, mouseButton, shift, control];
=2 => SVEvent.AppendToScript[parent, clientData, mouseButton, shift, control];
=3 => SVEvent.CloseScript[parent, clientData, mouseButton, shift, control];
=4 => SVEvent.PlaybackScript[parent, clientData, mouseButton, shift, control];
=5 => SVEvent.FastPlayScript[parent, clientData, mouseButton, shift, control];
ENDCASE => ERROR;
};
InterpressMenu: Menus.ClickProc = {
svData: SVData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["ToIP", LIST [$ToIP]],
["StorePoly", LIST [$StorePoly]]
];
QueuePopUpMenuAction["Interpress", menu, svData, mouseButton#blue];
};
ConfirmRayCast:
PUBLIC
PROC [clientData:
REF
ANY] = {
svData: SVData ← NARROW[clientData];
exists: BOOL;
aisFileRope: Rope.ROPE;
redRope: Rope.ROPE ← Rope.Concat[SVFiles.FilenameMinusExtension[aisFileRope], "-red.ais"];
greenRope: Rope.ROPE ← Rope.Concat[SVFiles.FilenameMinusExtension[aisFileRope], "-green.ais"];
blueRope: Rope.ROPE ← Rope.Concat[SVFiles.FilenameMinusExtension[aisFileRope], "-blue.ais"];
rope: Rope.ROPE;
IF mouseButton = blue THEN { -- black and white only
exists ← SVFiles.FileExists[aisFileRope];
rope ← IO.PutFR["Confirm raycast to %g.", [rope[aisFileRope]]];
IF exists THEN rope ← Rope.Concat[rope, " [Old File]"]
ELSE rope ← Rope.Concat[rope, " [New File]"];
}
ELSE {
exists ← SVFiles.FileExists[aisFileRope] OR SVFiles.FileExists[redRope] OR SVFiles.FileExists[greenRope] OR SVFiles.FileExists[blueRope];
rope ← IO.PutFR["Confirm raycast to %g [-red, -green, -blue, plain].ais", [rope[SVFiles.FilenameMinusExtension[aisFileRope]]]];
IF exists THEN rope ← Rope.Concat[rope, " [Old Files]"]
ELSE rope ← Rope.Concat[rope, " [New Files]"];
};
Feedback.AppendHerald[svData.feedback, rope, oneLiner];
};
Gravity Line Menus:
GravityExtentInSVData: AtomButtonsTypes.UpdateGraphicsButtonProc = {
svData: SVData ← NARROW[clientData];
svData.hitTest.gravityExtentButton ← stateInfo;
};
Feedback Line
FeedbackLineInSVData: AtomButtonsTypes.InitButtonProc = {
svData: SVData ← NARROW[clientData];
feedback: FeedbackData ← Feedback.RegisterFeedback[button, $Solidviews];
svData.feedback ← feedback;
};
Wiring Up Buttons to the Container:
ShowCoordsInSVData: AtomButtons.InitButtonProc = {
svData: SVData ← NARROW[clientData];
svData.textSection.showCoords ← button;
};
SceneStyleInSVData: AtomButtons.InitButtonProc = {
svData: SVData ← NARROW[clientData];
svData.textSection.viewStyle ← button;
};
SelectedInSVData: AtomButtons.InitButtonProc = {
svData: SVData ← NARROW[clientData];
svData.textSection.selected ← button;
};
XYZViewInSVData: AtomButtons.InitButtonProc = {
svData: SVData ← NARROW[clientData];
svData.textSection.xyz ← button;
};
DoubleBufferInSVData: AtomButtons.InitButtonProc = {
svData: SVData ← NARROW[clientData];
svData.textSection.doubleBuffer ← button;
Buttons.SetDisplayStyle[svData.textSection.doubleBuffer, $WhiteOnBlack];
};
Init:
PROC = {
boldFont ← VFonts.EstablishFont[family: "Helvetica", size: 10, bold: TRUE,
italic: FALSE, defaultOnFailure: TRUE];
};
Init[];
END.