<> <> <> <> <> <> <> <> <> <<>> DIRECTORY Ascii, Atom, AtomButtonsTypes, BasicTime, CodeTimer, ColorTool, Convert, Cubic2, CubicSplines, Feedback, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGDragTypes, GGEvent, GGEventExtras, GGFileOps, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGOutline, GGParent, GGParseIn, GGProps, GGRefresh, GGRefreshTypes, GGScene, GGSceneType, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, IO, Prop, Real, Rope, SF, SlackProcess, Vectors2d; GGEventImplC: CEDAR PROGRAM IMPORTS Atom, BasicTime, CodeTimer, ColorTool, Convert, Feedback, GGAlign, GGBoundBox, GGCaret, GGEvent, GGEventExtras, GGFileOps, GGHistory, GGMouseEvent, GGOutline, GGParent, GGParseIn, GGProps, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, IO, Prop, SlackProcess, Rope, Vectors2d EXPORTS GGEvent, GGModelTypes, GGInterfaceTypes = BEGIN ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes BitVector: TYPE = GGModelTypes.BitVector; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; Color: TYPE = Imager.Color; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; FeatureData: TYPE = GGModelTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; ROPE: TYPE = Rope.ROPE; Scene: TYPE = GGModelTypes.Scene; SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type SceneRef: TYPE = REF SceneObj; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorWalkProc: TYPE = GGModelTypes.SliceDescriptorWalkProc; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajPartType: TYPE = GGModelTypes.TrajPartType; TrajParts: TYPE = GGModelTypes.TrajParts; UserInputProc: TYPE = GGEvent.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; WalkProc: TYPE = GGModelTypes.WalkProc; reallyBigReal: REAL = 1.0e37; <<>> PaintActionArea: PUBLIC UserInputProc = { GGState.PaintActionArea[ggData]; }; <> UpdateCursor: UserInputProc = { <> <> Feedback.Append[ggData.router, oneLiner, $Error, "GGEventImplC.UpdateCursor was called. Please notify a Gargoyle implementor."]; }; ToggleActive: UserInputProc = { <> GGState.SetActive[ggData, NOT GGState.GetActive[ggData]]; }; SetActive: UserInputProc = { <> IF event.rest # NIL THEN WITH event.rest.first SELECT FROM r: ROPE => { b: BOOL _ Convert.BoolFromRope[r]; GGState.SetActive[ggData, b]; }; ENDCASE => Feedback.PutF[ggData.router, oneLiner, $Complaint, "SetActive got unexpected value %g", [refAny[event.rest.first]] ]; }; TogglePalette: UserInputProc = { <> GGState.SetPalette[ggData, NOT GGState.GetPalette[ggData]]; }; SaveSelections: UserInputProc = { ggData.behavior.savedSelections _ GGUtility.CopySliceDescriptorList[GGSelect.ListSelected[ggData.scene, normal]]; }; RestoreSelections: UserInputProc = { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SetSelected[ggData.scene, normal, ggData.behavior.savedSelections]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; SelectButton: UserInputProc = { sliceD: SliceDescriptor _ NARROW[event.rest.first]; GGSelect.SelectSlice[sliceD, ggData.scene, normal]; }; FeedbackOn: UserInputProc = { IF ggData.router#NIL THEN Feedback.SetRouterOn[ggData.router, TRUE]; }; FeedbackOff: UserInputProc = { IF ggData.router#NIL THEN Feedback.SetRouterOn[ggData.router, FALSE]; }; BeginButton: UserInputProc = { sliceD: SliceDescriptor _ NARROW[event.rest.first]; <> ggData.behavior.savedSelections _ GGUtility.CopySliceDescriptorList[GGSelect.ListSelected[ggData.scene, normal]]; <> GGSelect.DeselectAll[ggData.scene, normal]; <> GGSelect.SelectSlice[sliceD, ggData.scene, normal]; <> IF ggData.router#NIL THEN { Feedback.SetRouterOn[ggData.router, FALSE]; }; <> <> DisableRefresh[ggData, event]; }; EndButton: UserInputProc = { <> GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SetSelected[ggData.scene, normal, ggData.behavior.savedSelections]; <> IF ggData.router#NIL THEN { Feedback.SetRouterOn[ggData.router, TRUE]; }; <> <> EnableRefresh[ggData, event]; }; ButtonFillColorFromIntensity: UserInputProc = { <> buttonEvent: LIST OF REF ANY _ LIST[event.first, event.rest.rest.first]; CodeTimer.StartInt[$ButtonFillColorFromIntensity, $Gargoyle]; BeginButton[ggData, buttonEvent]; GGEventExtras.SetFillColorFromIntensity[ggData, NARROW[event.rest.first, REF REAL]^]; EndButton[ggData, buttonEvent]; CodeTimer.StopInt[$ButtonFillColorFromIntensity, $Gargoyle]; }; ButtonStrokeColorFromIntensity: UserInputProc = { <> buttonEvent: LIST OF REF ANY _ LIST[event.first, event.rest.rest.first]; CodeTimer.StartInt[$ButtonStrokeColorFromIntensity, $Gargoyle]; BeginButton[ggData, buttonEvent]; GGEventExtras.SetStrokeColorFromIntensity[ggData, NARROW[event.rest.first, REF REAL]^]; EndButton[ggData, buttonEvent]; CodeTimer.StopInt[$ButtonStrokeColorFromIntensity, $Gargoyle]; }; <> <> SetProp: UserInputProc = { <> SetPropOnSlice: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { val _ IF valRope=NIL THEN NIL ELSE GGProps.FromRope[key, valRope]; -- must do this for every slice in case, for instance, EmbeddedButtons wants to give each button a unique name GGProps.Put[sliceD.slice, sliceD.parts, key, val]; }; rope: Rope.ROPE _ NARROW[event.rest.first]; s: IO.STREAM _ IO.RIS[rope]; keyRope, valRope: Rope.ROPE; val: REF; key: ATOM; keyRope _ GGParseIn.ReadWWord[s]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetProp failed: Please select a key name"]; RETURN; }; GGParseIn.ReadBlank[s]; valRope _ IO.GetRope[s]; key _ Atom.MakeAtom[keyRope]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, SetPropOnSlice, normal]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE]; }; GetSelectedVal: PROC [key: ATOM, ggData: GGData, opName: Rope.ROPE] RETURNS [val: REF, success: BOOL _ TRUE, sliceD: SliceDescriptor _ NIL] = { aborted: BOOL _ FALSE; scene: Scene _ ggData.scene; CheckVal: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisVal: REF; IF sliceD = NIL THEN { sliceD _ thisD; [val, success] _ GGProps.Get[thisD.slice, thisD.parts, key]; done _ NOT success; } ELSE { [thisVal, success] _ GGProps.Get[thisD.slice, thisD.parts, key]; IF NOT success OR thisVal # val THEN done _ TRUE }; }; aborted _ GGScene.WalkSelectedSlices[scene, first, CheckVal, normal]; IF aborted THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one value for this key", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; GetProp: UserInputProc = { keyRope: Rope.ROPE _ NARROW[event.rest.first]; key: ATOM _ Atom.MakeAtom[keyRope]; val: REF; success: BOOL _ FALSE; sliceD: SliceDescriptor; [val, success, sliceD] _ GetSelectedVal[key, ggData, "GetProp"]; IF NOT success THEN RETURN; IF val#NIL THEN { valRope: Rope.ROPE; valRope _ GGProps.ToRope[key, val].r; IF valRope = NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "%g ", [rope[keyRope]] ] ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "%g %g", [rope[keyRope]], [rope[valRope]]] } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]]; }; GetSelectedValExternal: PROC [key: ATOM, ggData: GGData, opName: Rope.ROPE] RETURNS [val: REF, success: BOOL _ TRUE, error: Rope.ROPE _ NIL] = { aborted: BOOL _ FALSE; scene: Scene _ ggData.scene; sliceD: SliceDescriptor; CheckVal: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisVal: REF; IF sliceD = NIL THEN { sliceD _ thisD; [val, success] _ GGProps.Get[thisD.slice, thisD.parts, key]; done _ NOT success; } ELSE { [thisVal, success] _ GGProps.Get[thisD.slice, thisD.parts, key]; IF NOT success OR thisVal # val THEN done _ TRUE }; }; aborted _ GGScene.WalkSelectedSlices[scene, first, CheckVal, normal]; IF aborted THEN { success _ FALSE; error _ IO.PutFR["%g failed: there is more than one value for this key", [rope[opName]] ]; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one value for this key", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { success _ FALSE; error _ IO.PutFR["%g failed: no objects are targeted", [rope[opName]] ]; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; GetPropExternal: UserInputProc = { returnList: LIST OF Rope.ROPE; errorRope, valRope: Rope.ROPE; extRef: GGUserInput.External _ NARROW[event.rest.first]; key: ATOM _ NARROW[extRef.results]; keyRope: Rope.ROPE _ Atom.GetPName[key]; val: REF; success: BOOL _ FALSE; [val, success, errorRope] _ GetSelectedValExternal[key, ggData, "GetProp"]; IF success THEN { IF val#NIL THEN { valRope _ GGProps.ToRope[key, val].r; IF valRope = NIL THEN { Feedback.PutF[ggData.router, oneLiner, $Show, "%g ", [rope[keyRope]] ]; errorRope _ IO.PutFR["%g ", [rope[keyRope]] ]; } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "%g %g", [rope[keyRope]], [rope[valRope]]] } ELSE { Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]]; valRope _ IO.PutFR["No value for key %g.", [rope[keyRope]]]; }; }; returnList _ LIST[valRope, errorRope]; extRef.results _ returnList; extRef.valid _ TRUE; GGUserInput.BroadcastExternal[extRef]; }; RemoveProp: UserInputProc = { RemPropOnSlice: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { [] _ GGProps.Rem[sliceD.slice, sliceD.parts, key]; }; keyRope: Rope.ROPE _ NARROW[event.rest.first]; key: ATOM _ Atom.MakeAtom[keyRope]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveProp failed: Please select a key name"]; RETURN; }; [] _ GGScene.WalkSelectedSlices[ggData.scene, first, RemPropOnSlice, normal]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Key %g removed from selected slices.", [rope[keyRope]] ]; }; ListProps: UserInputProc = { <> success: BOOL _ FALSE; propsList: Prop.PropList; first: BOOL _ TRUE; PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOL _ FALSE] = { atom: ATOM _ NARROW[key]; keyRope: Rope.ROPE _ Atom.GetPName[atom]; IF first THEN { Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ]; first _ FALSE; } ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ]; }; [propsList, success] _ GetSelectedPropsList[ggData, "ListProps"]; IF NOT success THEN RETURN; IF propsList#NIL THEN { [] _ Prop.Map[propsList, PrintKey]; Feedback.PutF[ggData.router, end, $Show, ""]; } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No properties."]; }; ListPropsExternal: UserInputProc = { <> extRef: GGUserInput.External _ NARROW[event.rest.first]; returnList: LIST OF ATOM; success: BOOL _ FALSE; propsList: Prop.PropList; first: BOOL _ TRUE; GetSelectedPropsExt: PROC [ggData: GGData, opName: Rope.ROPE] RETURNS [propsList: Prop.PropList, success: BOOL _ TRUE, error: Rope.ROPE] = { sliceD: SliceDescriptor _ NIL; aborted: BOOL _ FALSE; scene: Scene _ ggData.scene; CheckPropsList: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisList: Prop.PropList; IF sliceD = NIL THEN { sliceD _ thisD; propsList _ sliceD.slice.props; } ELSE { thisList _ thisD.slice.props; IF thisList # propsList THEN done _ TRUE }; }; aborted _ GGScene.WalkSelectedSlices[scene, leaf, CheckPropsList, normal]; IF aborted THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one properties list selected", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOL _ FALSE] = { atom: ATOM _ NARROW[key]; keyRope: Rope.ROPE _ Atom.GetPName[atom]; IF first THEN { Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ]; first _ FALSE; } ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ]; returnList _ CONS[atom, returnList]; }; [propsList, success] _ GetSelectedPropsExt[ggData, "ListProps"]; IF success THEN { IF propsList#NIL THEN { [] _ Prop.Map[propsList, PrintKey]; Feedback.PutF[ggData.router, end, $Show, ""]; } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No properties."]; }; extRef.results _ returnList; extRef.valid _ TRUE; GGUserInput.BroadcastExternal[extRef]; }; CopyProps: UserInputProc = { <> CopyAndSetProps: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF sliceD.slice#lastSliceD.slice THEN GGProps.CopyAll[lastSliceD.slice, sliceD.slice, lastSliceD.parts, sliceD.parts]; }; lastSliceD: SliceDescriptor _ GGScene.LastSelectedSlice[ggData.scene, leaf, normal]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CopyAndSetProps, normal]; }; SetRootProp: PUBLIC UserInputProc = { rope: Rope.ROPE _ NARROW[event.rest.first]; s: IO.STREAM _ IO.RIS[rope]; keyRope, valRope: Rope.ROPE; val: REF; key: ATOM; keyRope _ GGParseIn.ReadWWord[s]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetRootProp failed: Please select a key name"]; RETURN; }; GGParseIn.ReadBlank[s]; valRope _ IO.GetRope[s]; key _ Atom.MakeAtom[keyRope]; val _ IF valRope=NIL THEN NIL ELSE GGProps.FromRope[key, valRope]; GGProps.Put[ggData.rootSlice, NIL, key, val]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE]; }; GetRootProp: PUBLIC UserInputProc = { keyRope: Rope.ROPE _ NARROW[event.rest.first]; key: ATOM _ Atom.MakeAtom[keyRope]; val: REF; success: BOOL _ FALSE; [val, success] _ GGProps.Get[ggData.rootSlice, NIL, key]; IF NOT success THEN RETURN; IF val#NIL THEN { valRope: Rope.ROPE; valRope _ GGProps.ToRope[key, val].r; IF valRope = NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "%g ", [rope[keyRope]] ] ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "%g %g", [rope[keyRope]], [rope[valRope]]] } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]]; }; RemoveRootProp: PUBLIC UserInputProc = { keyRope: Rope.ROPE _ NARROW[event.rest.first]; key: ATOM _ Atom.MakeAtom[keyRope]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveRootProp failed: Please select a key name"]; RETURN; }; [] _ GGProps.Rem[ggData.rootSlice, NIL, key]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Key %g removed from root slice.", [rope[keyRope]] ]; }; ListRootProps: PUBLIC UserInputProc = { success: BOOL _ FALSE; propsList: Prop.PropList; first: BOOL _ TRUE; PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOL _ FALSE] = { atom: ATOM _ NARROW[key]; keyRope: Rope.ROPE _ Atom.GetPName[atom]; IF first THEN { Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ]; first _ FALSE; } ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ]; }; propsList _ ggData.rootSlice.props; IF propsList#NIL THEN { [] _ Prop.Map[propsList, PrintKey]; Feedback.PutF[ggData.router, end, $Show, ""]; } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No properties."]; }; GetSelectedPropsList: PROC [ggData: GGData, opName: Rope.ROPE] RETURNS [propsList: Prop.PropList, success: BOOL _ TRUE] = { sliceD: SliceDescriptor _ NIL; aborted: BOOL _ FALSE; scene: Scene _ ggData.scene; CheckPropsList: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisList: Prop.PropList; IF sliceD = NIL THEN { sliceD _ thisD; propsList _ sliceD.slice.props; } ELSE { thisList _ thisD.slice.props; IF thisList # propsList THEN done _ TRUE }; }; aborted _ GGScene.WalkSelectedSlices[scene, leaf, CheckPropsList, normal]; IF aborted THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one properties list selected", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; <<>> <> ApplyAllDefaults: UserInputProc = { newSelectList, ptr: LIST OF Slice; currentEvent: HistoryEvent; scene: Scene _ ggData.scene; DoApplyAllDefaults: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSliceOps.SetDefaults[sliceD.slice, sliceD.parts, ggData.defaults, currentEvent]; [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; IF GGSelect.NoSelections[ggData.scene, normal] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "ApplyAllDefaults failed: select some objects for ApplyAllDefaults"]; RETURN; }; [newSelectList, ptr] _ GGUtility.StartSliceList[]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent _ GGHistory.NewCurrent["Apply default looks", ggData]; -- start new history event [] _ GGScene.WalkSelectedSlices[scene, first, DoApplyAllDefaults, normal]; FOR list: LIST OF Slice _ newSelectList, list.rest UNTIL list = NIL DO GGSelect.SelectEntireSlice[list.first, ggData.scene, normal]; ENDLOOP; <> GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "ApplyAllDefaults: all defaults applied"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; SetAllDefaults: UserInputProc = { <> <> <> <> <> }; ShowAllDefaults: UserInputProc = { Feedback.Append[ggData.router, oneLiner, $Typescript, "--------------------------------------------"]; GGEvent.ShowDefaultFontValues[ggData, event]; GGEvent.ShowDefaultLineColor[ggData, event]; GGEvent.ShowDefaultFillColor[ggData, event]; GGEvent.ShowDefaultStrokeValues[ggData, event]; GGEvent.ShowDefaultTextLooks[ggData, event]; Feedback.Append[ggData.router, oneLiner, $Typescript, "--------------------------------------------"]; }; StandardDefaults: UserInputProc = { ggData.defaults^ _ [ strokeWidth: 2.0, strokeJoint: round, strokeEnd: round, dashed: FALSE, pattern: NIL, offset: 0.0, length: 0.0, strokeColor: Imager.black, fillColor: GGOutline.fillColor, font: GGUserProfile.GetDefaultDefaultFont[], textColor: Imager.black, dropShadowOn: FALSE, dropShadowOffset: [-0.1, -0.1], dropShadowColor: Imager.black ]; ShowAllDefaults[ggData, event]; }; WeldOrBackword: UserInputProc = { IF ggData.refresh.textInProgress=NIL THEN Weld[ggData, event] ELSE { myRefChar: REF CHAR _ NEW[CHAR _ Ascii.ControlW ]; event _ LIST[$AddChar, myRefChar]; GGEvent.AddChar[ggData, event]; }; }; Weld: UserInputProc = { <> firstTraj, secondTraj, newTraj: Traj; firstOutline, secondOutline, newOutline: Slice; weldPoint: Point; firstEnd, secondEnd: TrajEnd; success: BOOL; firstPriority: INT _ 0; cluster: Slice; scene: Scene _ ggData.scene; whichClustered: OfTwoArgs _ none; GGHistory.NewCapture["Weld", ggData]; -- capture scene BEFORE UPDATE [firstTraj, secondTraj, cluster, whichClustered, success] _ GetWeldArguments[ggData]; IF NOT success THEN RETURN; IF secondTraj = NIL THEN success _ WeldToSelf[ggData, firstTraj] ELSE { ancestor: Slice; IF cluster # NIL THEN { ancestor _ GGParent.GetTopLevelAncestor[cluster]; GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene]; GGSelect.DeselectEntityAllClasses[ancestor, scene]; }; [firstEnd, secondEnd] _ ClosestEnds[firstTraj, secondTraj]; firstOutline _ GGParent.GetParent[firstTraj]; secondOutline _ GGParent.GetParent[secondTraj]; newTraj _ GGTraj.Concat[firstTraj, firstEnd, secondTraj, secondEnd]; newOutline _ GGOutline.CreateOutline[newTraj, GGSliceOps.GetFillColor[firstOutline, NIL].color]; IF cluster = NIL THEN { -- both are top level firstPriority _ GGScene.GetPriority[scene, firstOutline]; GGScene.DeleteSlice[scene, firstOutline]; GGScene.DeleteSlice[scene, secondOutline]; GGScene.AddSlice[scene, newOutline, firstPriority]; } ELSE { -- one or both are in a cluster firstPriority _ GGParent.GetChildPriority[cluster, firstOutline]; GGSelect.DeselectEntityAllClasses[firstOutline, scene]; GGSelect.DeselectEntityAllClasses[secondOutline, scene]; IF whichClustered = first OR whichClustered = both THEN [] _ GGSlice.RemoveChild[cluster, firstOutline] ELSE GGScene.DeleteSlice[scene, firstOutline]; IF whichClustered = second OR whichClustered = both THEN [] _ GGSlice.RemoveChild[cluster, secondOutline] ELSE GGScene.DeleteSlice[scene, secondOutline]; GGSlice.AddChildToCluster[cluster, newOutline, firstPriority]; }; <