File: GGMenusImpl.mesa
Author: Eric Bier on June 5, 1985 11:34:50 pm PDT
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on November 7, 1985 8:42:44 pm PST
Contents: Code to create a Gargoyle window. Calls GGContainer to make the container and adds menus at the top. The graphics part of the Gargoyle window is a new viewer of class $ActionArea. The paint proc for a $ActionArea is defined herein.
Stone, July 15, 1985 11:39:29 am PDT
DIRECTORY
FS, GGButtons, GGDrawProcess, GGError, GGGraphicsButton, GGInterfaceTypes, GGMenus, GGModelTypes, GGMouseEvent, GGSessionLog, GGUserInput, IO, Menus, PopUpMenu, Rope, TiogaButtons, ViewerClasses, ViewerTools;
GGMenusImpl: CEDAR PROGRAM
IMPORTS FS, GGButtons, GGDrawProcess, GGError, GGGraphicsButton, GGMouseEvent, GGSessionLog, GGUserInput, Rope, PopUpMenu, ViewerTools
EXPORTS GGMenus =
BEGIN
Camera: TYPE = REF CameraObj;
CameraObj: TYPE = GGModelTypes.CameraObj;
Caret: TYPE = REF CaretObj;
CaretObj: TYPE = GGInterfaceTypes.CaretObj;
Cluster: TYPE = GGModelTypes.Cluster;
ImagerProc: TYPE = GGInterfaceTypes.ImagerProc;
MouseButton: TYPE = Menus.MouseButton;
Outline: TYPE = GGModelTypes.Outline;
PaintQueue: TYPE = REF PaintQueueObj;
PaintQueueObj: TYPE = GGInterfaceTypes.PaintQueueObj;
Point: TYPE = GGModelTypes.Point;
Scene: TYPE = REF SceneObj;
SceneObj: TYPE = GGModelTypes.SceneObj;
Segment: TYPE = GGModelTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Traj: TYPE = GGModelTypes.Traj;
Viewer: TYPE = ViewerClasses.Viewer;
GargoyleData: TYPE = REF GargoyleDataObj;
GargoyleDataObj: TYPE = GGInterfaceTypes.GargoyleDataObj;
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;
nameSize: CARDINAL = 140;
smallNumberSize: CARDINAL = 45;
numberSize: CARDINAL = 80;
bigNumberSize: CARDINAL = 160;
pointSize: CARDINAL = 160;
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, gargoyleData: GargoyleData] = {
choices: LIST OF Rope.ROPE;
index: NAT;
choices ← ExtractChoiceList[menu];
index ← PopUpMenu.RequestSelection[
label: label,
choice: choices,
default: 0,
mouse: NIL];
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 {
GGUserInput.GeneralDispatch[entries.first.input, gargoyleData];
RETURN;
};
index ← index - 1;
REPEAT
FINISHED => ERROR;
ENDLOOP;
};
BuildControlPanel: PUBLIC PROC [gargoyleData: GargoyleData, windowMenu: Menus.Menu] = {
BuildFileMenuLine[gargoyleData, windowMenu];
BuildMasterMenuLine[gargoyleData, windowMenu];
BuildStyleMenuLine[gargoyleData, windowMenu];
BuildGravityLine[gargoyleData];
BuildAddLine[gargoyleData];
BuildSlopeLine[gargoyleData];
BuildRadiusLine[gargoyleData];
BuildDistanceLine[gargoyleData];
BuildCoordinateLine[gargoyleData];
};
BuildFileMenuLine: PROC [gargoyleData: GargoyleData, windowMenu: Menus.Menu] = {
Files: Clear, Reset, Get, Store, Save, Split, Interpress
GGButtons.BuildQueuedMenuLine [windowMenu, 0, gargoyleData, LIST[
["Clear", LIST[$Clear], confirmClear],
["Reset", LIST[$Reset], confirmReset],
["Get", LIST[$Get], confirmGet],
["Store", LIST[$Store], confirmStore],
["Save", LIST[$Save]],
["Split", LIST[$Split]],
["Interpress", LIST[$Interpress]]
]];
GGButtons.BuildMenuLine[windowMenu, 0, gargoyleData, LIST[
["OpenLog", OpenSessionLog],
["CloseLog", CloseSessionLog],
["Playback", PlaybackLog],
["FastPlay", FastPlaybackLog]
]];
};
BuildMasterMenuLine: PROC [gargoyleData: GargoyleData, windowMenu: Menus.Menu] = {
Master: Hierarchy Curve Transform Overlap EditCurve View Debug Abort Revive
GGButtons.BuildMenuLine [windowMenu, 1, gargoyleData, LIST[
["Hierarchy", HierarchyMenu],
["Curve", CurveTypeMenu],
["Transform", Dummy],
["Overlap", OverlapMenu],
["EditCurve", EditCurveMenu],
["View", ViewMenu],
["Debug", DebugMenu],
["Abort!", AbortButton],
["Revive!", ReviveButton],
["Help", TIPTableMenu]
]];
};
BuildStyleMenuLine: PROC [gargoyleData: GargoyleData, windowMenu: Menus.Menu] = {
Style: LineEnds LineWidth LineColor AreaColor TextAlign Font
GGButtons.BuildMenuLine[windowMenu, 2, gargoyleData, LIST[
["LineWidth", LineWidthMenu],
["AreaColor", AreaColorMenu]
]];
};
BuildGravityLine: PROC [gargoyleData: GargoyleData] = {
nextX: NAT;
gargoyleData.hitTest.gravityTypeMenu ←
GGButtons.BuildEnumTypeSelection[viewer: gargoyleData.outer, y: gargoyleData.height,
title: "GravityChoice:",
buttonNames: LIST["StrictDistance", "PointsPreferred", "None"],
default: "PointsPreferred",
borderOnButtons: TRUE,
atom: $GravityChoiceChange,
clientdata: gargoyleData,
style: flipThru,
allInOneRow: TRUE];
gargoyleData.hitTest.gravityType ← innerCircle;
nextX ← gargoyleData.hitTest.gravityTypeMenu.nextx;
nextX ← GGButtons.BuildButtonLine[gargoyleData.outer, nextX + entryHSpace, gargoyleData.height, gargoyleData, LIST[
["GravityExtent:", label]
]];
nextX ← GGGraphicsButton.BuildGraphicsButton[
viewer: gargoyleData.outer,
x: nextX + entryHSpace,
y: gargoyleData.height,
w: 72,
h: entryHeight,
name: "GravityExtent",
atom: $GravityExtentChange,
initValue: 25.0,
clientData: gargoyleData];
nextX ← GGButtons.BuildLineOfPopUps[gargoyleData.outer, nextX + entryHSpace, gargoyleData.height, gargoyleData, LIST[
["HotSpots", button, NIL, HotSpotsMenu]
]];
[gargoyleData.refresh.doubleBuffer, nextX] ←
GGButtons.BuildTwoStateButton[viewer: gargoyleData.outer,
x: nextX + entryHSpace,
y: gargoyleData.height,
name: "DoubleBuffer",
action: LIST[$ToggleDoubleBuffer],
clientData: gargoyleData,
init: on];
[gargoyleData.hitTest.linesAlwaysOn, nextX] ←
GGButtons.BuildTwoStateButton[viewer: gargoyleData.outer,
x: nextX + entryHSpace,
y: gargoyleData.height,
name: "AlwaysOn",
action: LIST[$ToggleAlwaysOn],
clientData: gargoyleData,
init: on];
gargoyleData.height ← gargoyleData.height + entryHeight;
};
BuildAddLine: PROC [gargoyleData: GargoyleData] = {
nextX: NAT;
nextX ← GGButtons.BuildButtonLine[gargoyleData.outer, 0, gargoyleData.height, gargoyleData, LIST[
[" Slope:", button, LIST[$SlopePrompt]],
["0.0", text, NIL, SlopeInGargoyleData, smallNumberSize],
["Add!", button, LIST[$AddSlope]],
["GetSlope!", button, LIST[$GetSlope]],
["Radius:", button, LIST[$RadiusPrompt]],
["0.0", text, NIL, RadiusInGargoyleData, smallNumberSize],
["Add!", button, LIST[$AddRadius]],
["GetRadius!", button, LIST[$GetRadius]],
["Distance:", button, LIST[$DistancePrompt]],
["0.0", text, NIL, DistanceInGargoyleData, smallNumberSize],
["Add!", button, LIST[$AddDistance]],
["GetDistance!", button, LIST[$GetDistance]],
["Delete!", button, LIST[$DeleteRadius]]
]];
gargoyleData.height ← gargoyleData.height + entryHeight;
};
BuildSlopeLine: PROC [gargoyleData: GargoyleData] = {
gargoyleData.hitTest.slopeButtons ← GGButtons.BuildScalarButtons[gargoyleData, "Slopes: ", LIST[
["0", 0.0, LIST[$ToggleSlope, NEW[REAL ← 0.0]], off],
["30", 30.0, LIST[$ToggleSlope, NEW[REAL ← 30.0]], off],
["45", 45.0, LIST[$ToggleSlope, NEW[REAL ← 45.0]], off],
["60", 60.0, LIST[$ToggleSlope, NEW[REAL ← 60.0]], off],
["90", 90.0, LIST[$ToggleSlope, NEW[REAL ← 90.0]], off],
["120", 120.0, LIST[$ToggleSlope, NEW[REAL ← 120.0]], off],
["135", 135.0, LIST[$ToggleSlope, NEW[REAL ← 135.0]], off],
["150", 150.0, LIST[$ToggleSlope, NEW[REAL ← 150.0]], off]
]];
};
BuildCoordinateLine: PROC [gargoyleData: GargoyleData] = {
nextX: NAT;
nextX ← GGButtons.BuildButtonLine[gargoyleData.outer, 0, gargoyleData.height, gargoyleData, LIST[
[" Coordinates:", label],
["lastCaret:", label],
["[-999.99, -999.99]", label, NIL, CoordLabelInGargoyleData],
["currentCaret:", label],
["[-999.98, -999.98]", label, NIL, CoordLabelInGargoyleData],
["distance:", label],
["0.0", text, NIL, CoordLabelInGargoyleData, smallNumberSize]
]];
gargoyleData.height ← gargoyleData.height + entryHeight;
};
BuildRadiusLine: PROC [gargoyleData: GargoyleData] = {
gargoyleData.hitTest.radiusButtons ← GGButtons.BuildScalarButtons[gargoyleData, "Radius: ", LIST[
["72", 72.0, LIST[$ToggleRadius, NEW[REAL ← 72.0]], off],
["144", 144.0, LIST[$ToggleRadius, NEW[REAL ← 144.0]], off]
]];
};
BuildDistanceLine: PROC [gargoyleData: GargoyleData] = {
gargoyleData.hitTest.distanceButtons ← GGButtons.BuildScalarButtons[gargoyleData, "Distance: ", LIST[
["0", 0.0, LIST[$ToggleDistance, NEW[REAL ← 0.0]], off]
]];
};
Dummy: Menus.ClickProc = {
GGError.Append["Menu Level: This feature not yet implemented", oneLiner];
};
File Line:
confirmReset: REF Menus.ClickProc ← NEW[Menus.ClickProc ← ConfirmReset];
confirmSave: REF Menus.ClickProc ← NEW[Menus.ClickProc ← ConfirmSave];
confirmGet: REF Menus.ClickProc ← NEW[Menus.ClickProc ← ConfirmGet];
confirmStore: REF Menus.ClickProc ← NEW[Menus.ClickProc ← ConfirmStore];
confirmClear: REF Menus.ClickProc ← NEW[Menus.ClickProc ← ConfirmClear];
ConfirmClear: Menus.ClickProc = {};
ConfirmReset: Menus.ClickProc = {};
ConfirmGet: Menus.ClickProc = {};
ConfirmStore: Menus.ClickProc = {};
ConfirmSave: Menus.ClickProc = {};
Master Line:
HierarchyMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["Delete (DEL)", LIST [$Delete]],
["Extend Select (^V)", LIST [$ExtendSelect]],
["Undo (SHIFT-ESC)", LIST [$Undo]],
["Redo (ESC)", LIST [$Redo]],
["AddHoles", LIST [$AddHoles]],
["FreeHoles", LIST [$FreeHoles]],
["Cluster", LIST [$Cluster]],
["Un Cluster", LIST [$UnCluster]]
];
QueuePopUpMenuAction["Hierarchy", menu, gargoyleData];
};
CurveTypeMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["Straight", LIST [$SetStraight]],
["Arc", LIST [$SetArc]],
["Conic", LIST [$SetConic]],
["Bezier", LIST [$SetBezier]],
["Spline", LIST [$SetSpline]]
];
QueuePopUpMenuAction["Curve", menu, gargoyleData];
};
OverlapMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["Top", LIST [$Top]],
["Bottom", LIST [$Bottom]],
["Up One", LIST [$UpOne]],
["Down One", LIST [$DownOne]]
];
QueuePopUpMenuAction["Overlap", menu, gargoyleData];
};
EditCurveMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["Show Points", LIST [$ShowPoints]],
["Hide Points", LIST [$HidePoints]],
["Smooth", LIST [$Smooth]],
["Corner", LIST [$Corner]],
["Close (^C)", LIST [$Close]],
["Open", LIST [$Open]]
];
QueuePopUpMenuAction["EditCurve", menu, gargoyleData];
};
ViewMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["Refresh", LIST [$Refresh]],
["Rubberband On", LIST [$RubberbandOn]],
["Rubberband Off", LIST [$RubberbandOff]],
["Show Hot", LIST [$RubberbandOff]],
["Hide Hot", LIST [$RubberbandOff]]
];
QueuePopUpMenuAction["View", menu, gargoyleData];
};
DebugMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["Test Gravity", LIST [$TestGravity]],
["Draw Touch Points", LIST [$DrawTouchPoints]],
["Describe Touch Points", LIST [$DescribeTouchPoints]],
["Draw Bounding Boxes", LIST [$DrawBoundBoxes]],
["Draw Selection Box", LIST [$DrawSelectionBox]],
["Typescript", LIST [$Typescript]],
["SlackLog", LIST [$SlackLog]],
["Describe Caret Object", LIST [$DescribeCaretObject]]
];
QueuePopUpMenuAction["Debug", menu, gargoyleData];
};
TIPTableMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
[" Left Middle Right", LIST [$NoOp]],
["Plain: SelectPoint SelectTrajectory ExtendSelect", LIST [$NoOp]],
["SHIFT: CopySelectPoint CopySelectTrajectory ExtendCopySelect", LIST [$NoOp]],
["CTRL: AddLine Drag Rotate", LIST [$NoOp]],
["CTRL-SHIFT: Scale", LIST [$NoOp]]
];
QueuePopUpMenuAction["TIP Table", menu, gargoyleData];
};
ReviveButton: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
GGMouseEvent.ResetMouseMachinery[gargoyleData];
gargoyleData.refresh.suppressRefresh ← FALSE;
GGDrawProcess.Restart[];
};
AbortButton: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
gargoyleData.aborted ← TRUE;
};
Style Line:
LineWidthMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["1 pt (LOOK 1)", LIST[$LineWidth, NEW[INT ← 1]]],
["2 pt (LOOK 2)" , LIST[$LineWidth, NEW[INT ← 2]]],
["3 pt (LOOK 3)", LIST[$LineWidth, NEW[INT ← 3]]],
["4 pt (LOOK 4)", LIST[$LineWidth, NEW[INT ← 4]]],
["other", LIST[$LineWidth, NEW[INT ← 5]]]
];
QueuePopUpMenuAction["Line Width", menu, gargoyleData];
};
AreaColorMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["AreaColorFromColorTool", LIST[$AreaColorFromColorTool]],
["black" , LIST[$AreaColorBlack]],
["white", LIST[$AreaColorWhite]],
["gray", LIST[$AreaColorGray]]
];
QueuePopUpMenuAction["Area Color", menu, gargoyleData];
};
OpenSessionLog: Menus.ClickProc = {
name, wdir, fullName: Rope.ROPE;
success: BOOL;
gargoyleData: GargoyleData ← NARROW[clientData];
name ← ViewerTools.GetSelectionContents[];
IF Rope.Length[name] = 0 THEN RETURN;
wdir ← gargoyleData.originalWorkingDirectory;
success ← TRUE;
[fullName,,] ← FS.ExpandName[name, wdir
! FS.Error => IF error.group = user THEN {
success ← FALSE;
CONTINUE;
}
];
IF NOT success THEN RETURN;
GGDrawProcess.OpenSessionLog[fullName];
};
CloseSessionLog: Menus.ClickProc = {
GGDrawProcess.CloseSessionLog[];
};
PlaybackLog: Menus.ClickProc = {
name: Rope.ROPE;
gargoyleData: GargoyleData ← NARROW[clientData];
name ← ViewerTools.GetSelectionContents[];
GGSessionLog.PlaybackFromFile[name, gargoyleData];
};
FastPlaybackLog: Menus.ClickProc = {
name: Rope.ROPE;
gargoyleData: GargoyleData ← NARROW[clientData];
name ← ViewerTools.GetSelectionContents[];
GGUserInput.GeneralDispatch[LIST[$DisableRefresh], gargoyleData];
GGSessionLog.PlaybackFromFile[name, gargoyleData];
GGUserInput.GeneralDispatch[LIST[$EnableRefresh], gargoyleData];
GGUserInput.GeneralDispatch[LIST[$Refresh], gargoyleData];
};
Alignment Line:
HotSpotsMenu: Menus.ClickProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
menu: LIST OF PopUpMenuEntry;
menu ← LIST [
["Make Hot (^s)", LIST[$MakeHot]],
["Make Cold (^S)" , LIST[$MakeCold]],
["Drop Anchor (^a)" , LIST[$DropAnchor]],
["Lift Anchor (^A)" , LIST[$LiftAnchor]]
];
QueuePopUpMenuAction["Hot Spots", menu, gargoyleData];
};
Distance Line:
CoordLabelInGargoyleData: GGButtons.UpdateProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
SELECT TRUE FROM
Rope.Equal[name, "[-999.99, -999.99]"] => gargoyleData.distanceLine.oldCaret ← button;
Rope.Equal[name, "[-999.98, -999.98]"] => gargoyleData.distanceLine.newCaret ← button;
Rope.Equal[name, "0.0"] => gargoyleData.distanceLine.distance ← button;
ENDCASE => ERROR;
};
RadiusInGargoyleData: GGButtons.UpdateProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
gargoyleData.hitTest.radiusText ← button;
};
SlopeInGargoyleData: GGButtons.UpdateProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
gargoyleData.hitTest.slopeText ← button;
};
DistanceInGargoyleData: GGButtons.UpdateProc = {
gargoyleData: GargoyleData ← NARROW[clientData];
gargoyleData.hitTest.distanceText ← button;
};
DistanceOfInterest: Menus.ClickProc = {
};
END.