<<>> <> <> <> <> <> <> DIRECTORY BiScrollers, CodeTimer, Commander, EBEditors, EBEditorsExtras, EBNullDoc, EBTypes, EmbeddedButtons, Feedback, FeedbackTypes, GGActive, GGAlign, GGBasicTypes, GGControlPanelTypes, GGCoreTypes, GGEmbedTypes, GGEvent, GGEventExtras, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGProps, GGScene, GGSliceOps, GGState, GGUIUtility, GGUserInput, GGWindow, Imager, ImagerTransformation, InputFocus, IO, List, MultiCursors, Rope, ScreenCoordsTypes, TIPUser, ViewerClasses, ViewerOps; GGActiveImpl: CEDAR PROGRAM IMPORTS BiScrollers, CodeTimer, Commander, EBEditors, EBEditorsExtras, EBNullDoc, EmbeddedButtons, Feedback, GGAlign, GGEventExtras, GGMouseEvent, GGMultiGravity, GGProps, GGScene, GGSliceOps, GGState, GGUIUtility, GGUserInput, ImagerTransformation, InputFocus, IO, List, MultiCursors, TIPUser, ViewerOps EXPORTS GGActive, GGInterfaceTypes = BEGIN ActiveDoc: TYPE = EBTypes.ActiveDoc; ActiveButton: TYPE = EBTypes.ActiveButton; BoundBox: TYPE = GGModelTypes.BoundBox; Color: TYPE = Imager.Color; ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes FeatureData: TYPE = GGModelTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; MsgRouter: TYPE = FeedbackTypes.MsgRouter; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; StrokeEnd: TYPE = Imager.StrokeEnd; TIPScreenCoords: TYPE = ScreenCoordsTypes.TIPScreenCoords; TIPScreenCoordsRec: TYPE = ScreenCoordsTypes.TIPScreenCoordsRec; Transformation: TYPE = ImagerTransformation.Transformation; UserInputProc: TYPE = GGEvent.UserInputProc; Viewer: TYPE = ViewerClasses.Viewer; <<>> <> InitPaletteApplication: PROC = { EmbeddedButtons.RegisterApplication[$Palette, PaletteNotify]; }; PaletteNotify: EmbeddedButtons.RegisteredNotifyProc = { <> ggData: GGData ¬ NARROW[buttonInfo.doc.theDoc]; sliceD: SliceDescriptor ¬ NARROW[buttonInfo.button]; FOR list: LIST OF REF ¬ events, list.rest UNTIL list = NIL DO SELECT list.first FROM $TransferFillColor => [] ¬ GGMouseEvent.SetFillColorRemote[ggData, sliceD]; $TransferStrokeColor => [] ¬ GGMouseEvent.SetStrokeColorRemote[ggData, sliceD]; $TransferBothColors => { [] ¬ GGMouseEvent.SetFillColorRemote[ggData, sliceD]; [] ¬ GGMouseEvent.SetStrokeColorRemote[ggData, sliceD]; }; ENDCASE; ENDLOOP; }; <<>> <> ggActiveDocClass: EBEditors.ActiveDocClass ¬ NEW[EBEditors.ActiveDocClassObj ¬ [ name: $Gargoyle, getRef: GGGetRef, setRef: GGSetRef, mapRef: GGMapRef, getDocName: GGGetDocName, feedback: GGButtonFeedback, -- Feedback is the name of an interface inButton: GGInButton ]]; LookupDoc: PUBLIC PROC [ggData: GGData] RETURNS [doc: ActiveDoc] ~ { <> IF ggData.behavior.activeDoc = NIL THEN { doc ¬ EBEditors.CreateActiveDoc[ggData, ggActiveDocClass]; ggData.behavior.activeDoc ¬ doc; } ELSE doc ¬ NARROW[ggData.behavior.activeDoc] }; NullDoc: PUBLIC PROC [ggData: GGData] RETURNS [doc: ActiveDoc] ~ { doc ¬ EBNullDoc.Create["GGNullDoc"]; <<>> <> [] _ EBNullDoc.CreateBOOLButton[doc, "Midpoints", FALSE]; [] _ EBNullDoc.CreateBOOLButton[doc, "ShowAlignments", FALSE]; [] _ EBNullDoc.CreateBOOLButton[doc, "DoubleBuffer", TRUE]; [] _ EBNullDoc.CreateBOOLButton[doc, "Active", FALSE]; -- scene itself is not active [] _ EBNullDoc.CreateBOOLButton[doc, "Editable", TRUE]; -- scene is editable [] _ EBNullDoc.CreateBOOLButton[doc, "Palette", FALSE]; [] _ EBNullDoc.CreateEnumeratedButton[doc, "ScreenStyle", $SpecifiedFonts, LIST[$SpecifiedFonts, $AlternateFonts, $WYSIWYG]]; [] _ EBNullDoc.CreateBOOLButton[doc, "Gravity", TRUE]; [] _ EBNullDoc.CreateREALButton[doc, "GravityExtent", 25.0]; [] _ EBNullDoc.CreateEnumeratedButton[doc, "GravityType", $PreferPoints, LIST[$PreferLines, $PreferPoints]]; [] _ EBNullDoc.CreateBOOLButton[doc, "Auto", FALSE]; EBNullDoc.Instantiate[doc]; }; <> pt: Imager.VEC; feature: FeatureData; ggData: GGData ¬ NARROW[doc.theDoc]; sliceD: SliceDescriptor ¬ NARROW[button]; sceneBag: GGAlign.TriggerBag ¬ GGAlign.CreateTriggerBag[]; completeD: SliceDescriptor ¬ GGSliceOps.NewParts[sliceD.slice, NIL, slice]; tsc: TIPScreenCoords ¬ NEW[TIPScreenCoordsRec ¬ [x, y, FALSE] ]; [] ¬ ViewerOps.MouseInViewer[tsc]; -- modifies tsc to viewer coords pt ¬ GGWindow.ViewerToWorld[[tsc.mouseX, tsc.mouseY], ggData]; [] ¬ GGAlign.AddSliceFeature[completeD, sceneBag]; feature ¬ GGMultiGravity.FacesPreferred[ pt, 18.0, GGAlign.emptyAlignBag, sceneBag, ggData].feature; IF feature = NIL THEN RETURN[FALSE] ELSE { <> <> <<[real[pt.x/scale]], [real[pt.y/scale]] ];>> RETURN[TRUE]; }; }; >> GGInButton: EBEditors.InButtonProc = { <> RETURN[TRUE]; }; GGGetDocName: EBEditors.GetDocNameProc ~ { <> ggData: GGData ¬ NARROW[doc.theDoc]; name ¬ GGState.GetFullName[ggData]; }; GGGetRef: EBEditors.GetRefProc = { <> sliceD: SliceDescriptor ¬ NARROW[button]; isUnique: BOOL ¬ TRUE; [ref, isUnique] ¬ GGProps.Get[sliceD.slice, sliceD.parts, key]; IF ref = NIL THEN { -- try inheritance ggData: GGData ¬ NARROW[doc.theDoc]; rootSlice: Slice ¬ ggData.rootSlice; [ref, isUnique] ¬ GGProps.Get[rootSlice, NIL, key]; }; }; GGSetRef: EBEditors.SetRefProc = { <> sliceD: SliceDescriptor ¬ NARROW[button]; GGProps.Put[sliceD.slice, sliceD.parts, key, ref]; }; GGMapRef: EBEditors.MapRefProc = { <> ForEachButton: PROC [leaf: Slice] RETURNS [done: BOOL ¬ FALSE] = { leafD: SliceDescriptor ¬ GGSliceOps.NewParts[leaf, NIL, slice]; buttonData: REF ¬ GGProps.Get[leaf, leafD.parts, $ButtonData].val; IF buttonData = NIL THEN RETURN; done ¬ mapProc[leafD, doc]; }; ggData: GGData ¬ NARROW[doc.theDoc]; aborted ¬ GGScene.WalkSlices[ggData.scene, leaf, ForEachButton]; }; GGButtonFeedback: EBEditors.FeedbackProc = { <> ggData: GGData ¬ NARROW[doc.theDoc]; feedbackRef: REF; WITH feedback SELECT FROM rope: Rope.ROPE => feedbackRef ¬ GGUIUtility.ParseFeedbackRope[rope]; ENDCASE => feedbackRef ¬ feedback; WITH feedbackRef SELECT FROM a: ATOM => QueueButtonEvent[LIST[a], ggData, button]; l: LIST OF REF => HandleList[l, ggData, button]; ENDCASE => ERROR; RETURN[feedbackRef]; }; <> NoOpFileIn: GGProps.FileinProc = { <> val ¬ IO.GetRope[s]; }; ButtonDataFileOut: GGProps.FileoutProc = { <> rope: Rope.ROPE; rope ¬ EBEditors.RopeFromButtonData[val]; s.PutRope[rope]; }; ButtonDataFileIn: GGProps.FileinProc = { <> rope: Rope.ROPE ¬ IO.GetRope[s]; val ¬ EBEditors.ButtonDataFromRope[rope, FALSE]; }; ButtonDataCopy: GGProps.CopyProc = { <> valAsRope: Rope.ROPE ¬ EBEditors.RopeFromButtonData[val]; copy ¬ EBEditors.ButtonDataFromRope[valAsRope, FALSE]; }; <> < occurs, the named value is retrieved from the active document. This allows a button to base its operation on any of the values in the document.>> <<>> GargoyleHandler: EmbeddedButtons.RegisteredNotifyProc = { <> <> viewer: ViewerClasses.Viewer; inputFocus: InputFocus.Focus; inputFocus ¬ InputFocus.GetInputFocus[]; IF inputFocus # NIL THEN { viewer ¬ inputFocus.owner; IF viewer # NIL AND viewer.class.flavor = $ActionArea THEN { ggData: GGData ¬ NARROW[BiScrollers.ClientDataOfViewer[viewer]]; HandleList[events, ggData, buttonInfo.button]; }; }; }; GargoyleAsControlPanelHandler: EmbeddedButtons.RegisteredNotifyProc = { <> <> ggData: GGData ¬ NARROW[buttonInfo.doc.theDoc]; HandleList[events, ggData, buttonInfo.button]; }; ControlPanelButtonHandler: PUBLIC PROC [ggData: GGData, events: LIST OF REF, buttonInfo: EBTypes.ButtonInfo] = { <> HandleList[events, ggData, buttonInfo.button]; }; TransferButtonDashes: UserInputProc = { <> sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; dashed: BOOL ¬ FALSE; pattern: SequenceOfReal ¬ NIL; offset, length: REAL ¬ 0.0; isUnique: BOOL ¬ TRUE; targetGGData: GGData ¬ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [dashed, pattern, offset, length, isUnique] ¬ GGSliceOps.GetDashed[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetDashed[ggData, dashed, pattern, offset, length]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonDashes failed: the button has multiple dash patterns"]; }; TransferButtonStrokeWidth: UserInputProc = { <> sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; strokeWidth: REAL; isUnique: BOOL ¬ TRUE; targetGGData: GGData ¬ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [strokeWidth, isUnique] ¬ GGSliceOps.GetStrokeWidth[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetStrokeWidth[ggData, strokeWidth]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonStrokeWidth failed: the button has multiple dash patterns"]; }; TransferButtonStrokeEnd: UserInputProc = { <> sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; strokeEnd: StrokeEnd; isUnique: BOOL ¬ TRUE; targetGGData: GGData ¬ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [strokeEnd, isUnique] ¬ GGSliceOps.GetStrokeEnd[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetStrokeEnd[ggData, strokeEnd]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonStrokeEnd failed: the button has multiple dash patterns"]; }; <> ActiveInputHandler: PUBLIC PROC [self: Viewer, ggData: GGData, input: LIST OF REF, notify: PROC [LIST OF REF]] = { event: EBTypes.Event; IF ggData.embed.beingBorn THEN RETURN; -- don't handle input while window is being built CodeTimer.StartInt[$RawInputNotify, $Gargoyle]; IF EBEditors.ValidEvent[event ¬ EBEditors.GetEvent[input]] THEN { -- raw input bs: BiScrollers.BiScroller ~ BiScrollers.QuaBiScroller[self]; mouseAction: BOOL ~ EBEditors.MouseAction[event]; mouseAllUp: BOOL ~ EBEditors.MouseAllUp[event]; button: ActiveButton ¬ NIL; -- will be set to the active button, if any doc: ActiveDoc ¬ NIL; IF GGState.GetActive[ggData] -- activity enabled AND ggData.behavior.rawInputMode=$None -- not in $Gargoyle mode AND mouseAction -- it's some kind of mouse action THEN { -- test for an active button at the cursor position GetMousePosition: PROC [mc: TIPScreenCoordsRec] RETURNS [mouseGG: Point] ~ { viewerToClient: Transformation ~ bs.class.style.GetTransforms[bs].viewerToClient; mouseBiScroller: TIPScreenCoords ~ NEW[TIPScreenCoordsRec ¬ mc]; v: Viewer; inClient: BOOL; [v, inClient] ¬ ViewerOps.MouseInViewer[mouseBiScroller]; IF NOT inClient THEN Feedback.PutFL[ggData.router, oneLiner, $Debug, "GG: %g,%g; ", LIST[[integer[mouseBiScroller.mouseX]], [integer[mouseBiScroller.mouseY]]] ]; mouseGG ¬ ImagerTransformation.Transform[m: viewerToClient, v: [mouseBiScroller.mouseX, mouseBiScroller.mouseY]]; }; mouseGG: Point ~ GetMousePosition[EBEditors.MouseCoords[event]]; feature: FeatureData; IF EBEditorsExtras.MouseMotion[event] AND EBEditors.MouseAllUp[event] THEN { <> feature ¬ NIL; <> <> <> <> <> <> <> <> <> } ELSE { feature ¬ GGMultiGravity.FacesPreferred[mouseGG, 18.0, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData].feature; }; IF feature#NIL THEN { doc ¬ LookupDoc[ggData]; WITH feature.shape SELECT FROM sliceD: SliceDescriptor => { buttonData: REF ~ GGGetRef[$ButtonData, sliceD, doc]; IF buttonData#NIL THEN button ¬ sliceD; -- found an active button }; ENDCASE; }; }; IF button#NIL THEN { EBEditors.HandleEvent[event, button, doc]; } ELSE { IF GGState.GetReadOnly[ggData] THEN { <> IF MultiCursors.GetACursor[NIL]#textPointer THEN MultiCursors.SetACursor[textPointer, NIL]; } ELSE { result: LIST OF REF ¬ EBEditors.ParseEvent[ggData.tipTable, event]; result _ CopyEventCoordinates[result]; IF mouseAction THEN { cursorType: MultiCursors.CursorType; cursorType ¬ ggData.controls.cursor; IF cursorType#MultiCursors.GetACursor[NIL] THEN MultiCursors.SetACursor[cursorType, NIL]; <> }; IF NOT mouseAllUp THEN ggData.behavior.rawInputMode ¬ $Gargoyle; IF result#NIL THEN notify[result]; }; }; IF mouseAllUp THEN ggData.behavior.rawInputMode ¬ $None; } ELSE notify[input]; CodeTimer.StopInt[$RawInputNotify, $Gargoyle]; }; CopyEventCoordinates: PROC [event: LIST OF REF] RETURNS [newEvent: LIST OF REF _ NIL] = { <> tail: LIST OF REF _ NIL; FOR list: LIST OF REF _ event, list.rest UNTIL list = NIL DO IF tail = NIL THEN tail _ newEvent _ CONS[list.first, NIL] ELSE tail _ tail.rest _ CONS[list.first, NIL]; WITH list.first SELECT FROM z: TIPScreenCoords => tail.first _ NEW [Imager.VEC _ [z.mouseX, z.mouseY] ]; ENDCASE; ENDLOOP; }; QueueButtonEvent: PROC [event: LIST OF REF, ggData: GGData, button: REF] = { newEvent: LIST OF REF; IF event.first = $SelectButton OR event.first = $BeginButton OR event.first = $TransferButtonDashes OR event.first = $TransferButtonStrokeWidth OR event.first = $TransferButtonStrokeEnd THEN { sliceD: SliceDescriptor ¬ NARROW[button]; newEvent ¬ LIST[event.first, sliceD]; -- make a copy of the eventList, because EmbeddedButtons resuses the original } ELSE IF event.first = $ButtonFillColorFromIntensity OR event.first = $ButtonStrokeColorFromIntensity THEN { sliceD: SliceDescriptor ¬ NARROW[button]; newEvent ¬ LIST[event.first, event.rest.first, sliceD]; -- make a copy of the eventList, because EmbeddedButtons resuses the original } ELSE newEvent ¬ List.Append[event]; -- make a copy of the eventList, because EmbeddedButtons resuses the original GGUserInput.EventNotify[ggData, newEvent]; }; HandleList: PROC [list: LIST OF REF, ggData: GGData, button: REF] = { WITH list.first SELECT FROM l: LIST OF REF => { FOR list ¬ list, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM childList: LIST OF REF => HandleList[childList, ggData, button]; ENDCASE => SIGNAL SyntaxError[msg: "Input list mixes LIST elements and ATOM elements"]; ENDLOOP; }; ENDCASE => QueueButtonEvent[list, ggData, button]; }; SyntaxError: PUBLIC SIGNAL[msg: Rope.ROPE] = CODE; GGActiveHandler: Commander.CommandProc = {}; <> ggActionAreaClass: ViewerClasses.ViewerClass ~ ViewerOps.FetchViewerClass[$ActionArea]; ggActionAreaClass.tipTable ¬ TIPUser.TransparentTIPTable[]; -- all viewers made after this call will be able to be active GGProps.Register[$ButtonData, ButtonDataFileIn, ButtonDataFileOut, ButtonDataCopy]; InitPaletteApplication[]; GGUserInput.RegisterAction[$TransferButtonDashes, TransferButtonDashes, none]; GGUserInput.RegisterAction[$TransferButtonStrokeWidth, TransferButtonStrokeWidth, none]; GGUserInput.RegisterAction[$TransferButtonStrokeEnd, TransferButtonStrokeEnd, none]; Commander.Register[key: "GGActive", proc: GGActiveHandler, doc: "Does nothing. Active Gargoyle is already loaded", clientData: NIL, interpreted: TRUE]; EmbeddedButtons.RegisterApplication[$Gargoyle, GargoyleHandler]; EmbeddedButtons.RegisterApplication[$GargoyleAsControlPanel, GargoyleAsControlPanelHandler]; END.