DIRECTORY AtomButtons, EBTypes, EmbeddedButtons, Feedback, FeedbackOps, FeedbackTypes, GGActive, GGBasicTypes, GGCircleCache, GGContainer, GGControlPanelTypes, GGEvent, GGInterfaceTypes, GGMenu, GGModelTypes, GGScene, GGSceneType, GGSegmentTypes, GGShapes, GGState, GGUserInput, GGUserProfile, GGWindow, Imager, ImagerFont, List, Menus, PFS, PFSNames, Rope, Rules, SimpleFeedback, TextNode, TiogaActive, TiogaIO, TiogaOps, UserProfile, ViewerClasses, ViewerTools; GGMenuImpl: CEDAR PROGRAM IMPORTS AtomButtons, EmbeddedButtons, Feedback, FeedbackOps, GGActive, GGCircleCache, GGContainer, GGEvent, GGScene, GGShapes, GGState, GGUserInput, GGUserProfile, GGWindow, Imager, ImagerFont, List, PFS, PFSNames, Rope, Rules, SimpleFeedback, TiogaActive, TiogaIO, TiogaOps, UserProfile, ViewerTools EXPORTS GGInterfaceTypes, GGModelTypes, GGMenu = BEGIN ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; MsgRouter: TYPE = FeedbackTypes.MsgRouter; GGData: TYPE = GGInterfaceTypes.GGData; GravityExtentData: TYPE = REF GravityExtentDataObj; GravityExtentDataObj: TYPE = GGInterfaceTypes.GravityExtentDataObj; Point: TYPE = GGBasicTypes.Point; SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type SceneRef: TYPE = REF SceneObj; Viewer: TYPE = ViewerClasses.Viewer; ROPE: TYPE = Rope.ROPE; popUpFont: Imager.Font; -- initialized in Init below; entryHeight: CARDINAL = 15; -- height of a line of items docEntryHeight: CARDINAL = 19; -- height of a line of items entryHSpace: CARDINAL = 2; -- horizontal space between items on a line entryVSpace: CARDINAL = 2; -- vertical leading between lines fullColumn: CARDINAL = 600; -- the width of the standard large left column. wideBody: CARDINAL = 1000; -- the feedback window is too narrow smallNumberSize: CARDINAL = 60; AddARule: PROC [ggData: GGData] = { rule: Rules.Rule ¬ Rules.Create[[ parent: ggData.controls.panel, wy: ggData.height, ww: ggData.controls.panel.cw, wh: 2 ]]; GGContainer.ChildXBound[ggData.controls.panel, rule]; ggData.height ¬ ggData.height + rule.wh + entryVSpace; }; BuildControlPanel: PUBLIC PROC [ggData: GGData, fancyPanel: BOOL ¬ FALSE] = { BuildActiveDocument[ggData, fancyPanel]; InitializeGravityLine[ggData]; BuildSlopeLine[ggData]; BuildAngleLine[ggData]; BuildRadiusLine[ggData]; BuildDistanceLine[ggData]; BuildMeasureLine[ggData]; BuildFeedbackLine[ggData]; AddARule[ggData]; }; BuildFeedbackLine: PROC [ggData: GGData] = { nextX: NAT ¬ AtomButtons.BuildButtonLine[ggData.controls.panel, 0, ggData.height, ggData, GGUserInput.EventNotify, LIST[ [label["Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Line", FeedbackLineInGGData, wideBody]] ]]; ggData.height ¬ ggData.height + entryHeight; }; FeedbackLineInGGData: AtomButtons.UpdateProc = { ggData: GGData ¬ NARROW[clientData]; router: MsgRouter ¬ Feedback.CreateRouter[]; FeedbackOps.SetMultiLabel[router, button, TRUE, LIST[$Error, $Complaint]]; -- blink FeedbackOps.SetMultiLabel[router, button, FALSE, LIST[$DuringMouse, $Feedback, $Warning, $Confirm, $Show, $Statistics]]; FeedbackOps.SetMultiTypescript[router, $SystemScript, LIST[$Error, $Warning, $Show, $Typescript, $Complaint, $Statistics]]; ggData.router ¬ router; ggData.controls.feedbackLine ¬ button; }; IsGargoyle: PROC [path: PFS.PATH] RETURNS [BOOL] = { short: ROPE ~ PFSNames.ShortNameRope[path]; RETURN[Rope.Match["*.gargoyle", short, FALSE] OR Rope.Match["*.gg", short, FALSE]]; }; defaultPanelName: ARRAY BOOL--fancy-- OF ROPE ~ [ FALSE: "[Cedar]GargoyleControlPanel.tioga", TRUE: "[Cedar]GargoyleControlPanel.gargoyle"]; FindControlPanel: PROC [fancy: BOOL ¬ FALSE] RETURNS [path: PFS.PATH ¬ NIL, isGargoyle: BOOL ¬ FALSE] ~ { value: LIST OF ROPE ~ UserProfile.ListOfTokens["Gargoyle.ControlPanelFile", NIL]; FOR list: LIST OF ROPE ¬ value, list.rest UNTIL list=NIL DO { ENABLE PFS.Error => CONTINUE; rope: ROPE ~ list.first; fullFName: PFS.PATH ~ PFS.FileInfo[PFS.PathFromRope[rope]].fullFName; gargoyle: BOOL ~ IsGargoyle[fullFName]; IF ((NOT fancy) OR gargoyle) THEN RETURN[fullFName, gargoyle]; }; ENDLOOP; { ENABLE PFS.Error => CONTINUE; rope: ROPE ~ defaultPanelName[fancy]; fullFName: PFS.PATH ~ PFS.FileInfo[PFS.PathFromRope[rope]].fullFName; RETURN[fullFName, IsGargoyle[fullFName]]; }; RETURN[NIL, FALSE]; }; LinkToActionArea: GGUserInput.UserInputProc = { theirData: GGData ¬ GGState.GetGGInputFocus[]; IF theirData = NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "LinkToActionArea failed: place input focus in target Gargoyle viewer"] ELSE { ConnectToGGControlPanel[ggData: theirData, panelData: ggData]; }; }; ConnectToGGControlPanel: PUBLIC PROC [ggData: GGData, panelData: GGData] = { panel: EBTypes.ActiveDoc; panel ¬ ggData.controls.controlPanel ¬ GGActive.LookupDoc[panelData]; ggData.controls.controlPanelViewer ¬ NIL; EmbeddedButtons.LinkDocToApplication[doc: panel, target: $Gargoyle, targetViewer: ggData.controls.actionArea, applicationData: ggData, notifyProc: ControlPanelNotify]; EmbeddedButtons.LinkDocToApplication[doc: panel, target: $UnQueuedGargoyle, targetViewer: ggData.controls.actionArea, applicationData: ggData, notifyProc: UnQueuedControlPanelNotify]; }; BuildActiveDocument: PROC [ggData: GGData, fancyPanel: BOOL] = { nextX: NAT ¬ 0; v: Viewer; lines: NAT ~ 6; height: NAT ¬ lines*docEntryHeight; filePath: PFS.PATH ¬ NIL; fileName: ROPE ¬ NIL; isGargoyle: BOOL ¬ FALSE; [filePath, isGargoyle] ¬ FindControlPanel[fancyPanel]; IF filePath = NIL THEN SimpleFeedback.Append[$System, oneLiner, $Error, "No file found for Gargoyle control panel in [Cedar] or in Gargoyle.ControlPanelFile user profile entry."] ELSE { fileName ¬ PFS.RopeFromPath[filePath]; SimpleFeedback.PutF[$System, oneLiner, $Feedback, "Loading %g as Gargoyle's control panel", [rope[fileName]]]; }; IF isGargoyle THEN { thisData: GGData; scene: GGModelTypes.Scene ¬ GGScene.CreateScene[]; height ¬ 140; [v, thisData] ¬ GGWindow.CreateChildViewer[scene: scene, wx: 0, wy: ggData.height, wh: height, parent: ggData.controls.panel, workingDirectory: ggData.currentWDir, paint: TRUE ]; ggData.controls.controlPanel ¬ GGActive.LookupDoc[thisData]; GGUserInput.EventNotify[thisData, LIST[$MergeAll, fileName]]; GGUserInput.EventNotify[thisData, LIST[$DeselectAll]]; GGUserInput.EventNotify[thisData, LIST[$SetActive, Rope.Literal["TRUE"]]]; GGUserInput.EventNotify[thisData, LIST[$SetScaleUnit, NEW[REAL ¬ 1.0], $Quiet]]; GGUserInput.EventNotify[thisData, LIST[$CleanVersion]]; GGState.SetReadOnly[thisData, TRUE]; } ELSE { v ¬ ViewerTools.MakeNewTextViewer[[ wx: 0, wy: ggData.height, wh: height, parent: ggData.controls.panel, data: TiogaIO.FromFile[filePath].root, scrollable: TRUE, border: TRUE]]; ggData.controls.controlPanel ¬ TiogaActive.LookupDoc[v]; TiogaOps.Interpret[v, LIST[$ActivityOn]]; }; GGContainer.ChildXBound[ggData.controls.panel, v]; ggData.controls.controlPanelViewer ¬ v; EmbeddedButtons.LinkDocToApplication[doc: ggData.controls.controlPanel, target: $Gargoyle, targetViewer: ggData.controls.actionArea, applicationData: ggData, notifyProc: ControlPanelNotify]; EmbeddedButtons.LinkDocToApplication[doc: ggData.controls.controlPanel, target: $UnQueuedGargoyle, targetViewer: ggData.controls.actionArea, applicationData: ggData, notifyProc: UnQueuedControlPanelNotify]; ggData.height ¬ ggData.height + height; }; ControlPanelNotify: EmbeddedButtons.NotifyProc = { newEvent: LIST OF REF ¬ List.Append[events]; -- make a copy of the eventList, because EmbeddedButtons resuses the original ggData: GGData ¬ NARROW[applicationData]; [] ¬ GGActive.ControlPanelButtonHandler[ggData, newEvent, buttonInfo]; }; UnQueuedControlPanelNotify: EmbeddedButtons.NotifyProc = { newEvent: LIST OF REF ANY ¬ List.Append[events]; -- make a copy of the eventList, because EmbeddedButtons resuses the original GGUserInput.UnQueuedEventNotify[applicationData, newEvent]; }; InitializeGravityLine: PROC [ggData: GGData] = { ggData.hitTest.gravityType ¬ pointsPreferred; GGState.SetGravityExtent[ggData, GGUserProfile.GetDefaultGravityExtent[]/72.0]; GGState.SetHeuristics[ggData, GGUserProfile.GetDefaultHeuristics[]]; }; BuildSlopeLine: PROC [ggData: GGData] = { buttonHandle: AtomButtons.SortedButtonHandle; nextX: NAT ¬ AtomButtons.BuildButtonLine[ggData.controls.panel, 0, ggData.height, ggData, GGUserInput.EventNotify,LIST[ [button["Slope:", LIST[LIST[$SlopePrompt]] ]], [button["Get!", LIST[LIST[$GetSlope]] ]], [button["Add!", LIST[LIST[$AddSlope]] ]], [button["Delete!", LIST[LIST[$DeleteSlope]] ]] ]]; buttonHandle ¬ AtomButtons.CreateSortedButtonViewer[ggData.controls.panel, nextX, ggData.height]; ggData.controls.slopeHandle ¬ buttonHandle; GGEvent.StandardSlopes[ggData, LIST[$InitStandardSlopes]]; ggData.height ¬ ggData.height + entryHeight; }; BuildAngleLine: PROC [ggData: GGData] = { buttonHandle: AtomButtons.SortedButtonHandle; nextX: NAT ¬ AtomButtons.BuildButtonLine[ggData.controls.panel, 0, ggData.height, ggData, GGUserInput.EventNotify,LIST[ [button["Angle:", LIST[LIST[$AnglePrompt]] ]], [button["Get!", LIST[LIST[$GetAngle]] ]], [button["Add!", LIST[LIST[$AddAngle]] ]], [button["Delete!", LIST[LIST[$DeleteAngle]] ]] ]]; buttonHandle ¬ AtomButtons.CreateSortedButtonViewer[ggData.controls.panel, nextX, ggData.height]; ggData.controls.angleHandle ¬ buttonHandle; GGEvent.StandardAngles[ggData, LIST[$InitStandardAngles]]; ggData.height ¬ ggData.height + entryHeight; }; BuildRadiusLine: PROC [ggData: GGData] = { buttonHandle: AtomButtons.SortedButtonHandle; nextX: NAT ¬ AtomButtons.BuildButtonLine[ggData.controls.panel, 0, ggData.height, ggData, GGUserInput.EventNotify, LIST[ [button["Radius:", LIST[LIST[$RadiusPrompt]] ]], [button["Get!", LIST[LIST[$GetRadius]] ]], [button["Add!", LIST[LIST[$AddRadius]] ]], [button["Delete!", LIST[LIST[$DeleteRadius]] ]] ]]; buttonHandle ¬ AtomButtons.CreateSortedButtonViewer[ggData.controls.panel, nextX, ggData.height]; ggData.controls.radiusHandle ¬ buttonHandle; GGEvent.StandardRadii[ggData, LIST[$InitStandardRadii]]; ggData.controls.radiusCircleCache ¬ GGCircleCache.Create[]; ggData.height ¬ ggData.height + entryHeight; }; BuildDistanceLine: PROC [ggData: GGData] = { buttonHandle: AtomButtons.SortedButtonHandle; nextX: NAT ¬ AtomButtons.BuildButtonLine[ggData.controls.panel, 0, ggData.height, ggData, GGUserInput.EventNotify, LIST[ [button["LineDist:", LIST[LIST[$DistancePrompt]] ]], [button["Get!", LIST[LIST[$GetDistance]] ]], [button["Add!", LIST[LIST[$AddDistance]] ]], [button["Delete!", LIST[LIST[$DeleteDistance]] ]] ]]; buttonHandle ¬ AtomButtons.CreateSortedButtonViewer[ggData.controls.panel, nextX, ggData.height]; ggData.controls.distanceHandle ¬ buttonHandle; GGEvent.StandardDistances[ggData, LIST[$InitStandardDistances]]; ggData.height ¬ ggData.height + entryHeight; }; BuildMeasureLine: PROC [ggData: GGData] = { nextX: NAT ¬ AtomButtons.BuildButtonLine[ggData.controls.panel, 0, ggData.height, ggData, GGUserInput.EventNotify, LIST[ [button["_", LIST[LIST[$MeasureSlopeFromSelection]], -1, TRUE ]], [button["Slope:", LIST[LIST[$MeasureSlopeHit]], -1, TRUE ]], [text["0.0", SlopeViewInGGData, smallNumberSize]], [button["_", LIST[LIST[$MeasureAngleFromSelection]], -1, TRUE ]], [button["Angle:", LIST[LIST[$MeasureAngleHit]], -1, TRUE ]], [text["0.0", AngleViewInGGData, smallNumberSize]], [button["_", LIST[LIST[$MeasureRadiusFromSelection]], -1, TRUE ]], [button["Radius:", LIST[LIST[$MeasureRadiusHit]], -1, TRUE ]], [text["0.0", DistanceViewInGGData, smallNumberSize]], [button["_", LIST[LIST[$MeasureLineDistFromSelection]], -1, TRUE ]], [button["LineDist:", LIST[LIST[$MeasureLineDistHit]], -1, TRUE ]], [text["0.0", LineDistViewInGGData, smallNumberSize]] ]]; ggData.height ¬ ggData.height + entryHeight; }; GravityInGGData: AtomButtons.InitTwoStateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.gravButton ¬ twoState; }; AlignmentsInGGData: AtomButtons.InitTwoStateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.alignments ¬ twoState; }; MidpointsInGGData: AtomButtons.InitTwoStateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.midpointButton ¬ twoState; }; HeuristicsInGGData: AtomButtons.InitTwoStateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.heuristicsButton ¬ twoState; }; GravityExtentRepaint: PROC [dc: Imager.Context, clientData: REF ANY, buttonData: REF ANY, button: Viewer] = { caretPoint: Point; ged: GravityExtentData ¬ NARROW[buttonData]; extent: REAL ¬ ged.extent; Imager.TranslateT[dc, [0, button.wh/2.0]]; caretPoint ¬ [extent, 0.0]; GGShapes.DrawCaret[dc, caretPoint, [0,-1], 1.0]; }; SlopeViewInGGData: AtomButtons.UpdateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.slopeView ¬ button; }; AngleViewInGGData: AtomButtons.UpdateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.angleView ¬ button; }; DistanceViewInGGData: AtomButtons.UpdateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.radiusView ¬ button; }; LineDistViewInGGData: AtomButtons.UpdateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.lineDistView ¬ button; }; Choice: PROC [action: LIST OF REF ANY, actionImage: Rope.ROPE, doc: Rope.ROPE, font: Imager.Font ¬ NIL] RETURNS [AtomButtons.PopUpChoice]= { RETURN[[action, actionImage, doc, font]]; }; gRouter: MsgRouter; -- the router used by routines that are too deep in Gargoyle to know what viewer they are working on behalf of. Sends output to the Gargoyle typescript and the Cedar Message Window. Init: PROC = { popUpFont ¬ ImagerFont.Find["xerox/tiogafonts/helvetica10I", substituteQuietly]; gRouter ¬ Feedback.CreateRouter[]; [] ¬ Feedback.RegisterRouter[gRouter, $Gargoyle]; FeedbackOps.SetMultiMessageWindow[gRouter, TRUE, LIST[$Error, $Complaint]]; -- blink FeedbackOps.SetMultiMessageWindow[gRouter, FALSE, LIST[$DuringMouse, $Feedback, $Warning, $Confirm, $Show, $Statistics]]; FeedbackOps.SetMultiTypescript[gRouter, $SystemScript, LIST[$Error, $Warning, $Show, $Typescript, $Complaint, $Statistics]]; GGUserInput.RegisterAction[$LinkToActionArea, LinkToActionArea, none]; }; Init[]; END. Μ GGMenuImpl.mesa Contents: Routines to build the Gargoyle control panel. Copyright Σ 1985, 1987, 1988, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved. Created by Pier on June 23, 1987 when GGMenuImpl overflowed Last edited by Pier on October 22, 1992 11:30 am PDT Bier, March 18, 1993 2:18 pm PST Eisenman, July 14, 1987 6:34:23 pm PDT Doug Wyatt, April 16, 1992 5:15 pm PDT If you update this routine, also update Init below. Currently, we know of these message classes used in Gargoyle: (BT) Complaint: Gargoyle informs the user of a user-initiated problem (BT) Error: internal error. One of Gargoyle's invariants has been broken (L) Confirm: Gargoyle asks the user to confirm a guarded operation (L) DuringMouse: feedback generated each time the mouse is moved. (L) Feedback: an acknowledgement that Gargoyle has done what you asked (LT) Show: info that was explicitly requested (e.g. what color is this?) (LT) Statistics: Gargoyle prints user requested statistics (LT) Warning: Problems during a batch operation, like MergeInterpress (T) Typescript: Output that only seems appropriate for the typescript, period. (L) = send to label. (B) = send to label and blink. (T) = send to typescript There are a couple places to look in for the Gargoyle control panel: (1) The file specified in Gargoyle.ControlPanelFile: in the user profile. (2) The file "[Cedar]GargoyleControlPanel.tioga" (useful during development) Use whichever is found first. Return NIL if none are found. PROC [ggData: GGData, event: LIST OF REF]; Connect GGData to the given control panel. Update invariants to correspond to the new control panel settings. PROC [buttonInfo: ButtonInfo, events: LIST OF REF ANY, targetViewer: Viewer, applicationData: REF] RETURNS [result: REF _ NIL]; PROC [buttonInfo: ButtonInfo, events: LIST OF REF ANY, targetViewer: Viewer, applicationData: REF] RETURNS [result: REF _ NIL]; <> [label["Measure- "]], Style Line: Gravity Line: GravityExtentInGGData: GraphicsButton.UpdateGraphicsButtonProc = { ggData: GGData _ NARROW[clientData]; ggData.controls.gravityExtentButton _ stateInfo; }; Distance Line: If you update the router, remember to also correct it in FeedbackLineInGGData above. ΚΑ•NewlineDelimiter –(cedarcode) style™codešœ™K™7Kšœ ΟeœU™`Kšœ;™;K™4K™ Kšœ#Οk™&K™&K˜—šž ˜ JšœΘžœ{˜ΖK˜—šΟn œžœž˜JšžœΑžœa˜¬Kšžœ*ž˜6—˜Kšœ žœžœ#˜;K˜Kšœ žœ˜*Kšœžœ˜'Kšœžœžœ˜3Kšœžœ)˜CKšœžœ˜!Kšœ žœžœΟc˜FKšœ žœžœ ˜Kšœžœ˜$K˜Kšžœžœžœ˜Kšœ ˜5Kšœ žœ ˜8Kšœžœ ˜;Kšœ žœ +˜FKšœ žœ !˜K˜—Kšžœ˜—šœžœžœ žœ˜Kšœžœ˜%Kš œ žœžœžœ žœ˜EKšžœ#˜)K˜—Kšžœžœžœ˜K˜K˜—šŸœ˜/Kšžœžœžœžœ™*K˜.Kšžœ žœžœ}˜”šžœ˜K˜>K˜—Kšœ˜K˜—šŸœžœžœ(˜LK™nK˜K˜EKšœ%žœ˜)Kšœ“‘œ˜§Kšœ›‘œ˜·K˜K˜—šŸœžœžœ˜@Kšœžœ˜K˜ Kšœžœ˜Kšœžœ˜#Kšœ žœžœžœ˜Kšœ žœžœ˜Kšœ žœžœ˜K˜6Kšžœ žœžœ¦˜Όšžœ˜Kšœ žœ˜&K˜nK˜—šžœ žœ˜Kšœ˜K˜2K˜ ˜8Kšœ%˜%Kšœ˜K˜%Kšœž˜ K˜—K˜Kšœ5˜5Kšœ žœžœ&žœ˜DKšœžœžœžœ˜BKšœ4˜4K˜—K˜,K˜—K˜K™ K™™ K™—šŸœ"˜1Kšœžœ ˜$K˜&K˜—šŸœ"˜4Kšœžœ ˜$K˜&K˜—šŸœ"˜3Kšœžœ ˜$K˜*K˜—šΠbn Ÿœ"˜4Kšœžœ ˜$K˜,K˜K˜—šŸœ-™BKšœžœ ™$Kšœ0™0K™K™—š Ÿœžœ"žœžœžœžœ˜mK˜Kšœžœ ˜,Kšœžœ˜Kšœ*˜*K˜Kšœ0˜0K˜—K™™K™—šŸœ˜-Kšœžœ ˜$K˜#K˜K˜—šŸœ˜-Kšœžœ ˜$K˜#K˜K˜—šŸœ˜0Kšœžœ ˜$K˜$K˜K˜—šŸœ˜0Kšœžœ ˜$K˜&K˜—K˜šŸœžœ žœžœžœžœžœ žœžœžœ˜ŒKšžœ#˜)K˜—K˜Kšœ Ά˜ΚšŸœžœ˜K•StartOfExpansionO[name: ROPE, substitution: ImagerFont.Substitution _ substituteWithWarning]˜PK™TK˜"K˜1Kšœ+žœžœ ˜TKšœ+žœžœC˜yKšœ7žœA˜|K˜FK˜—K˜Kšœ˜K˜Kšžœ˜K˜—…—7FUΣ