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: BOOLTRUE] = {
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: BOOLTRUE;
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: BOOLTRUE;
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.