<<>> <> <> <> <> <<>> <> <> <<>> DIRECTORY BiScrollers, CodeTimer, Commander, EBEditors, 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, KeyGlyphs, List, MultiCursors, Rope, TIPPrivate, TIPTypes, TIPUser, TIPUserExtras, UserInput, ViewerClasses, ViewerOps; GGActiveImpl: CEDAR PROGRAM IMPORTS InputFocus, BiScrollers, CodeTimer, Commander, EBEditors, EmbeddedButtons, Feedback, GGActive, GGAlign, GGEventExtras, GGMouseEvent, GGMultiGravity, GGProps, GGScene, GGSliceOps, GGState, GGUIUtility, GGUserInput, GGWindow, ImagerTransformation, IO, List, MultiCursors, TIPPrivate, TIPUserExtras, UserInput, ViewerOps EXPORTS EBTypes, GGActive, GGInterfaceTypes = BEGIN ActionQueue: TYPE ~ REF ActionQueueRep; ActionQueueRep: PUBLIC TYPE ~ UserInput.Rep; RawAction: TYPE ~ REF RawActionRep; RawActionRep: PUBLIC TYPE ~ UserInput.ActionBody; RawInput: TYPE ~ GGActive.RawInput; 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; 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 ANY _ 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] }; GGInButton: EBEditors.InButtonProc = { <> 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: TIPTypes.TIPScreenCoords _ NEW[TIPTypes.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]; }; }; 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 ANY; 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 ANY => 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]; }; }; }; 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: PROC [self: Viewer, ggData: GGData, input: LIST OF REF ANY] = { IF ggData.embed.beingBorn THEN RETURN; -- don't handle input while window is being built IF GGActive.IsRawInput[input] THEN { -- raw input CodeTimer.StartInt[$RawInputNotify, $Gargoyle]; { rawInput: GGActive.RawInput ~ GGActive.NarrowRawInput[input]; mouseAllUp: BOOL ~ MouseAllUp[rawInput]; button: REF _ NIL; -- will be set to the active button, if any doc: ActiveDoc; mouseAction: BOOL _ MouseAction[rawInput]; bs: BiScrollers.BiScroller ~ BiScrollers.QuaBiScroller[self]; 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: TIPUser.TIPScreenCoordsRec] RETURNS [mouseGG: Point] ~ { viewerToClient: Transformation ~ bs.class.style.GetTransforms[bs].viewerToClient; mouseBiScroller: TIPUser.TIPScreenCoords ~ NEW[TIPUser.TIPScreenCoordsRec _ mc]; v: Viewer; inClient: BOOL; [v, inClient] _ ViewerOps.MouseInViewer[mouseBiScroller]; IF NOT inClient THEN Feedback.PutF[ggData.router, oneLiner, $Debug, "GG: %g,%g; ", [integer[mouseBiScroller.mouseX]], [integer[mouseBiScroller.mouseY]] ]; mouseGG _ ImagerTransformation.Transform[m: viewerToClient, v: [mouseBiScroller.mouseX, mouseBiScroller.mouseY]]; }; mouseGG: Point ~ GetMousePosition[MouseCoords[rawInput]]; feature: FeatureData ~ 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[rawInput.rawAction, rawInput.actionQueue, button, doc]; } ELSE { IF GGState.GetReadOnly[ggData] THEN { <> IF MultiCursors.GetACursor[NIL]#textPointer THEN MultiCursors.SetACursor[textPointer, NIL]; } ELSE { event: LIST OF REF; IF mouseAction THEN { cursorType: MultiCursors.CursorType; cursorType _ ggData.controls.cursor; IF cursorType#MultiCursors.GetACursor[NIL] THEN MultiCursors.SetACursor[cursorType, NIL]; <> }; event _ ParseEvent[ggData.parseInfo, rawInput]; <> IF NOT mouseAllUp THEN ggData.behavior.rawInputMode _ $Gargoyle; IF event # NIL THEN { actionArea: Viewer _ BiScrollers.QuaViewer[bs: bs, inner: TRUE]; <> actionArea.class.notify[actionArea, event]; }; }; }; IF mouseAllUp THEN ggData.behavior.rawInputMode _ $None; }; CodeTimer.StopInt[$RawInputNotify, $Gargoyle]; } ELSE ERROR; -- should never be called if the input isn't from the Transparent TIP table }; QueueButtonEvent: PROC [event: LIST OF REF ANY, ggData: GGData, button: REF ANY] = { newEvent: LIST OF REF ANY; 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 ANY, ggData: GGData, button: REF ANY] = { WITH list.first SELECT FROM l: LIST OF REF ANY => { FOR list _ list, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM childList: LIST OF REF ANY => 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 = {}; <> IsRawInput: PUBLIC PROC [input: LIST OF REF ANY] RETURNS [BOOL] ~ { RETURN[ISTYPE[input.first, ActionQueue]]; }; NarrowRawInput: PUBLIC PROC [input: LIST OF REF ANY] RETURNS [RawInput] ~ { actionQueue: ActionQueue ~ NARROW[input.first]; rawAction: RawAction ~ NARROW[input.rest.first]; actionQueueEB: EBTypes.ActionQueue ~ actionQueue; rawActionEB: EBTypes.RawAction ~ rawAction; RETURN[[actionQueue: actionQueueEB, rawAction: rawActionEB]]; }; mouseButton1: UserInput.KeySym ~ KeyGlyphs.LeftMouse; mouseButton2: UserInput.KeySym ~ KeyGlyphs.MiddleMouse; mouseButton3: UserInput.KeySym ~ KeyGlyphs.RightMouse; MouseAction: PUBLIC PROC [input: RawInput] RETURNS [BOOL] ~ { actionRef: REF UserInput.ActionBody ~ input.rawAction; WITH a: actionRef^ SELECT FROM keyDown, keyStillDown, keyUp => { keySym: UserInput.KeySym ~ UserInput.GetKeySym[input.actionQueue, a.keyCode, 0]; SELECT keySym FROM mouseButton1, mouseButton2, mouseButton3 => RETURN[TRUE]; ENDCASE; }; mousePosition, fakeMouseMotion => RETURN[TRUE]; -- does fakeMouseMotion count ??? ENDCASE; RETURN[FALSE]; }; MouseAllUp: PUBLIC PROC [input: RawInput] RETURNS [BOOL] ~ { RETURN[ UserInput.GetKeySymState[input.actionQueue, mouseButton1]=up AND UserInput.GetKeySymState[input.actionQueue, mouseButton2]=up AND UserInput.GetKeySymState[input.actionQueue, mouseButton3]=up ]; }; MouseCoords: PUBLIC PROC [input: RawInput] RETURNS [TIPUser.TIPScreenCoordsRec] ~ { handle: UserInput.Handle ~ input.actionQueue; RETURN[handle.mousePosition]; }; MapCoordinateResults: PROC [results: LIST OF REF ANY] = { FOR lst: LIST OF REF _ results, lst.rest UNTIL lst = NIL DO SELECT lst.first FROM TIPPrivate.stdCoords => [] _ ViewerOps.MouseInViewer[TIPPrivate.stdCoords]; ENDCASE; ENDLOOP; }; ParseEvent: PUBLIC PROC [parseInfo: TIPPrivate.TIPParseInfo, input: RawInput] RETURNS [event: LIST OF REF] ~ { actionRef: REF UserInput.ActionBody ~ input.rawAction; parseInfo.inCreek _ input.actionQueue; event _ TIPPrivate.MatchEvent[parseInfo, actionRef^]; MapCoordinateResults[event]; }; <> ggActionAreaClass: ViewerClasses.ViewerClass ~ ViewerOps.FetchViewerClass[$ActionArea]; ggActionAreaClass.tipTable _ TIPUserExtras.TransparentTIPTable[]; -- all viewers made after this call will be able to be active GGUserInput.RegisterRawInputHandler[ActiveInputHandler]; 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]; END.