DIRECTORY Atom, Basics, CodeTimer, ColorTool, Feedback, FeedbackTypes, GGAlign, GGBasicTypes, GGCaret, GGDescribe, GGDragTypes, GGEvent, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGRefreshTypes, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUserInput, GGUtility, GGWindow, Imager, ImagerTransformation, InputFocus, IO, Menus, RealFns, Rope, Vectors2d; GGMouseEventImplB: CEDAR PROGRAM IMPORTS Basics, CodeTimer, ColorTool, Feedback, GGAlign, GGCaret, GGDescribe, GGEvent, GGHistory, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUserInput, GGUtility, GGWindow, ImagerTransformation, IO, Rope, Vectors2d EXPORTS GGMouseEvent, GGInterfaceTypes = BEGIN DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; AlignBag: TYPE = GGInterfaceTypes.AlignBag; AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; Color: TYPE = Imager.Color; DefaultData: TYPE = GGModelTypes.DefaultData; ExtendMode: TYPE = GGInterfaceTypes.ExtendMode; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; FeatureData: TYPE = GGModelTypes.FeatureData; GravityType: TYPE = GGInterfaceTypes.GravityType; MsgRouter: TYPE = FeedbackTypes.MsgRouter; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Joint: TYPE = GGModelTypes.Joint; MouseButton: TYPE = Menus.MouseButton; MouseProc: TYPE = GGMouseEvent.MouseProc; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; ROPE: TYPE = Rope.ROPE; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SelectMode: TYPE = GGModelTypes.SelectMode; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; SliceClass: TYPE = GGModelTypes.SliceClass; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; StartProc: TYPE = GGMouseEvent.StartProc; TouchGroup: TYPE = GGSegmentTypes.TouchGroup; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajParts: TYPE = GGModelTypes.TrajParts; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGAlign.TriggerBag; UserInputProc: TYPE = GGUserInput.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem; SetCaretAttractorEndpoint: PUBLIC PROC [ggData: GGData, mapPoint: Point, normal: Vector, testPoint: Point, feature: FeatureData, hitData: REF ANY] = { IF feature=NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, normal, NIL] ELSE { shape: REF ANY ¬ feature.shape; SELECT feature.type FROM slice => { pos: Point; sliceD: SliceDescriptor ¬ NARROW[shape]; jointD: SliceDescriptor; [jointD, pos, normal] ¬ GGSliceOps.ClosestJointToHitData[sliceD, mapPoint, testPoint, hitData]; IF Vectors2d.MagnitudeSquared[normal] = 0 THEN { normal ¬ [0, -1];}; GGCaret.SetAttractor[ggData.caret, pos, normal, jointD]; }; ENDCASE => GGCaret.SetAttractor[ggData.caret, mapPoint, normal, NIL]; }; }; SetStrokeColorRemote: PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor] RETURNS [doNormalBehavior: BOOL ¬ FALSE, done: BOOL ¬ FALSE] = { theirData: GGData ¬ GGState.GetGGInputFocus[]; theirScene: Scene; ourColor: Imager.Color; success: BOOL ¬ FALSE; IF theirData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; } ELSE theirScene ¬ theirData.scene; [ourColor, success] ¬ GGSliceOps.GetStrokeColor[sliceD.slice, sliceD.parts]; IF success THEN { GGEvent.LineColorAux[theirData, ourColor, GGDescribe.ColorToRope[ourColor], TRUE, $Set]; SendColorToColorTool[ggData, ourColor]; } ELSE { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote: Select an object with a unique stroke color"]; }; }; SetFillColorRemote: PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor] RETURNS [doNormalBehavior: BOOL ¬ FALSE, done: BOOL ¬ FALSE] = { theirData: GGData ¬ GGState.GetGGInputFocus[]; theirScene: Scene; ourColor: Imager.Color; success: BOOL ¬ FALSE; IF theirData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; } ELSE theirScene ¬ theirData.scene; [ourColor, success] ¬ GGSliceOps.GetFillColor[sliceD.slice, sliceD.parts]; IF success THEN { GGEvent.AreaColorAux[theirData, ourColor, GGDescribe.ColorToRope[ourColor], TRUE, $Set]; SendColorToColorTool[ggData, ourColor]; } ELSE { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Select an object with a unique fill color"]; }; }; LocalSymbols: TYPE = REF LocalSymbolsObj; LocalSymbolsObj: TYPE = RECORD [ sliceD: SliceDescriptor, rotation: ImagerTransformation.Transformation, ggData: GGData, currentEvent: HistoryEvent ]; SendColorToColorTool: PROC [ggData: GGData, color: Color] = { IF Basics.IsBound[ColorTool.SetColor] THEN { IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NIL fill colornot sent to ColorTool"] ELSE { noColorTool: BOOL ¬ FALSE; ColorTool.SetColor[color, NIL ! ColorTool.NoColorToolViewer => { noColorTool ¬ TRUE; CONTINUE }]; }; }; }; StartSelect: PUBLIC StartProc = { SELECT input.first FROM $StartSelectJoint => ggData.drag.selectState ¬ joint; $StartSelectSegment => ggData.drag.selectState ¬ segment; $StartSelectTrajectory => ggData.drag.selectState ¬ traj; $StartSelectTopLevel => ggData.drag.selectState ¬ topLevel; ENDCASE => ERROR; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; GGMouseEvent.SaveSavedState[ggData]; DuringSelect[NIL, ggData, worldPt]; }; DuringSelect: PUBLIC MouseProc = { opRope: Rope.ROPE = "Selecting"; resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; CodeTimer.StartInt[$DuringSelect, $Gargoyle]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE]; SELECT GGState.GetSelectMode[ggData] FROM joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment, segmentRange => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; traj, topLevel, slice => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FacesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; none => { resultPoint ¬ worldPt; normal ¬ [0, -1]; feature ¬ NIL; hitData ¬ NIL; }; ENDCASE => ERROR; GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; GGSelect.DeselectAll[ggData.scene, normal]; IF feature#NIL THEN [] ¬ SelectAndDescribeSlicePart[feature, hitData, ggData, ggData.drag.selectState, opRope] ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: TRUE]; CodeTimer.StopInt[$DuringSelect, $Gargoyle]; }; -- end DuringSelect EndSelect: PUBLIC MouseProc = { resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; featureCycler: GGInterfaceTypes.FeatureCycler; SELECT GGState.GetSelectMode[ggData] FROM joint => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; GGState.SetSelectionCycler[ggData, featureCycler]; [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FirstFeature[featureCycler]; GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; GGWindow.NewCaretPos[ggData]; GGSelect.DeselectAll[ggData.scene, normal]; EndSelectAux[ggData, resultPoint, feature, hitData, ggData.drag.selectState, RopeFromSelectMode[ggData.drag.selectState]]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly }; -- end EndSelect SelectModeFromAtom: PROC [atom: ATOM] RETURNS [selectMode: SelectMode] = { selectMode ¬ SELECT atom FROM $Joint => joint, $Segment => segment, $Traj => traj, ENDCASE => topLevel; }; RopeFromSelectMode: PROC [selectMode: SelectMode] RETURNS [rope: ROPE] = { rope ¬ SELECT selectMode FROM joint => "joint", segment => "segment", traj => "trajectory", ENDCASE => "object"; }; < worldPt ¬ p^; ENDCASE => worldPt ¬ [0,0]; IF event.rest.rest # NIL THEN { WITH event.rest.rest.first SELECT FROM a: ATOM => selectMode ¬ SelectModeFromAtom[a]; ENDCASE => selectMode ¬ topLevel; }; }; SELECT selectMode FROM joint => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; GGState.SetSelectionCycler[ggData, featureCycler]; [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FirstFeature[featureCycler]; GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; GGWindow.NewCaretPos[ggData]; GGSelect.DeselectAll[ggData.scene, normal]; EndSelectAux[ggData, resultPoint, feature, hitData, selectMode, RopeFromSelectMode[selectMode]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly }; -- Select >> Select: GGUserInput.UserInputProc = { resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; featureCycler: GGInterfaceTypes.FeatureCycler; worldPt: Point ¬ [0,0]; selectMode: SelectMode ¬ topLevel; gravExtent: REAL ¬ ggData.hitTest.t; gravType: GravityType ¬ ggData.hitTest.gravityType; gravTypeSpecified: BOOL ¬ FALSE; listIndex: NAT ¬ 0; FOR loe: LIST OF REF ¬ event, loe.rest UNTIL loe=NIL DO el: REF _ loe.first; SELECT listIndex FROM 0 => IF NARROW[el, ATOM]#$Select THEN ERROR; 1 => { WITH el SELECT FROM p: REF Point => worldPt ¬ p^; ENDCASE => worldPt ¬ [0,0]; }; 2 => { WITH el SELECT FROM a: ATOM => selectMode ¬ SelectModeFromAtom[a]; ENDCASE => selectMode ¬ topLevel; }; 3 => { WITH el SELECT FROM g: ATOM => {gravType ¬ GravTypeFromAtom[g]; gravTypeSpecified¬ TRUE;}; ENDCASE => NULL; -- don't change type }; 4 => { WITH el SELECT FROM r: REF REAL => gravExtent ¬ r^; ENDCASE => NULL; -- don't change extent }; ENDCASE => EXIT; listIndex ¬ listIndex+1; ENDLOOP; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE]; IF gravTypeSpecified THEN SELECT gravType FROM pointsPreferred => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; linesPreferred => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData] ELSE SELECT selectMode FROM joint => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; GGState.SetSelectionCycler[ggData, featureCycler]; [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FirstFeature[featureCycler]; GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; GGWindow.NewCaretPos[ggData]; GGSelect.DeselectAll[ggData.scene, normal]; EndSelectAux[ggData, resultPoint, feature, hitData, selectMode, RopeFromSelectMode[selectMode]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly }; -- Select GravTypeFromAtom: PROC [atom: ATOM] RETURNS [gravType: GravityType] = { gravType ¬ SELECT atom FROM $PreferLines => linesPreferred, $PreferPoints => pointsPreferred, $PreferFaces => facesPreferred, ENDCASE => facesPreferred; }; SelectFromFeature: PUBLIC PROC [ggData: GGData, testPoint: Point, point: Point, normal: Vector, feature: FeatureData, hitData: REF ANY] = { selectMode: SelectMode ¬ GGState.GetSelectMode[ggData]; IF selectMode = none THEN GOTO NoSelectMode; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedParts: TRUE, selectedCPs: TRUE, attractor: TRUE]; SetCaretAttractorEndpoint[ggData, point, normal, point, feature, hitData]; GGWindow.NewCaretPos[ggData]; GGSelect.DeselectAll[ggData.scene, normal]; SELECT selectMode FROM joint => EndSelectAux[ggData, point, feature, hitData, joint, "joint"]; segment => EndSelectAux[ggData, point, feature, hitData, segment, "segment"]; traj => EndSelectAux[ggData, point, feature, hitData, traj, "trajectory"]; topLevel => EndSelectAux[ggData, point, feature, hitData, topLevel, "object"]; ENDCASE => ERROR; GGWindow.RestoreScreenAndInvariants[paintAction: $EndSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE]; EXITS NoSelectMode => { Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectFromFeature failed: unknown selection grain. Make a selection first."]; }; }; EndSelectAux: PROC [ggData: GGData, resultPoint: Point, feature: FeatureData, hitData: REF ANY, mode: SelectMode, opName: Rope.ROPE] = { IF feature = NIL THEN { GGCaret.SitOn[ggData.caret, NIL]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "No near %g found.", [rope[opName]]]; } ELSE { SELECT mode FROM joint => GGState.SetExtendMode[ggData, joint]; segment => GGState.SetExtendMode[ggData, segmentRange]; traj => GGState.SetExtendMode[ggData, traj]; topLevel => GGState.SetExtendMode[ggData, topLevel]; ENDCASE => ERROR; BEGIN sliceD: SliceDescriptor ¬ SelectSlicePart[feature, hitData, ggData, mode]; featureCycler: FeatureCycler ¬ GGState.GetSelectionCycler[ggData]; cycleCount: NAT ¬ featureCycler.count; sliceType: ATOM ¬ GGSliceOps.GetType[sliceD.slice]; SitTheCaret[ggData.caret, sliceD, hitData, mode]; GGState.SetSliceToExtend[ggData, sliceD]; IF cycleCount > 1 THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, Rope.Concat[GGSliceOps.Describe[sliceD], " selected (%g more)"], [integer[cycleCount-1]]] ELSE Feedback.Append[ggData.router, oneLiner, $Feedback, Rope.Concat[GGSliceOps.Describe[sliceD], " selected"]]; END; }; }; -- end EndSelectAux SelectAndDescribeSlicePart: PROC [feature: FeatureData, hitData: REF ANY, ggData: GGData, mode: SelectMode, opRope: Rope.ROPE] RETURNS [selectedD: SliceDescriptor]= { selectedD ¬ SelectSlicePart[feature, hitData, ggData, mode]; Feedback.PutFL[ggData.router, oneLiner, $Feedback, "%g %g", LIST[[rope[opRope]], [rope[GGSliceOps.Describe[selectedD]]]] ]; }; SelectSlicePart: PUBLIC PROC [feature: FeatureData, hitData: REF ANY, ggData: GGData, mode: SelectMode] RETURNS [selectedD: SliceDescriptor] = { SELECT feature.type FROM slice => { slice: Slice ¬ NARROW[feature.shape, SliceDescriptor].slice; selectedD ¬ GGSliceOps.NewParts[slice, hitData, mode]; GGSelect.SelectSlice[selectedD, ggData.scene, normal]; }; slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR; ENDCASE => ERROR Problem[msg: "unimplemented result type"]; }; SitTheCaret: PROC [caret: Caret, sliceD: SliceDescriptor, hitData: REF ANY, mode: SelectMode] = { childD: SliceDescriptor ¬ NIL; IF GGParent.IsParent[sliceD.slice] THEN { childD ¬ GGParent.FirstIncludedChild[sliceD.slice, sliceD.parts, leaf, $Traj]; }; IF childD = NIL THEN GGCaret.SitOn[caret, NIL] ELSE { SELECT mode FROM joint => { GGCaret.SitOn[caret, sliceD]; }; segment, traj, topLevel => { jointD: SliceDescriptor; jointD ¬ GGSliceOps.ClosestJointToHitData[sliceD, caret.point, caret.point, hitData].jointD; GGCaret.SitOn[caret, jointD]; }; ENDCASE => ERROR; }; }; StartDeselect: PUBLIC StartProc = { SELECT input.first FROM $StartDeselectJoint => ggData.drag.selectState ¬ joint; $StartDeselectSegment => ggData.drag.selectState ¬ segment; $StartDeselectTrajectory => ggData.drag.selectState ¬ traj; $StartDeselectTopLevel => ggData.drag.selectState ¬ topLevel; ENDCASE => ERROR; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay GGMouseEvent.SaveSavedState[ggData]; ggData.drag.transform ¬ ImagerTransformation.Scale[1.0]; -- needed for DuringDeselect to work properly DuringDeselect[NIL, ggData, worldPt]; }; DuringDeselect: PUBLIC MouseProc = { opRope: Rope.ROPE = "Deselecting"; resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; ggData.drag.currentPoint ¬ worldPt; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedParts: TRUE, selectedCPs: TRUE, attractor: TRUE]; SELECT GGState.GetSelectMode[ggData] FROM joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; GGScene.RestoreSelections[ggData.scene]; IF feature#NIL THEN DuringDeselectAux[feature, hitData, resultPoint, ggData, ggData.drag.selectState, opRope] ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: TRUE]; }; -- end DuringDeselect DuringDeselectAux: PROC [feature: FeatureData, hitData: REF ANY, caretPt: Point, ggData: GGData, mode: GGModelTypes.SelectMode, opRope: Rope.ROPE] = { description: Rope.ROPE; SELECT feature.type FROM slice => { deselectedD: SliceDescriptor ¬ SliceDeselectAux[feature, hitData, mode, ggData.scene]; description ¬ IF deselectedD=NIL THEN "nothing" ELSE GGSliceOps.Describe[deselectedD]; Feedback.PutFL[ggData.router, oneLiner, $DuringMouse, "%g %g", LIST[[rope[opRope]], [rope[description]]] ]; }; slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR; ENDCASE => ERROR; }; SliceDeselectAux: PROC [feature: FeatureData, hitData: REF ANY, mode: GGModelTypes.SelectMode, scene: Scene] RETURNS [deselectedD: SliceDescriptor] = { shapeD: SliceDescriptor ¬ NARROW[feature.shape]; sliceD: SliceDescriptor ¬ GGSelect.FindSelectedSlice[shapeD.slice, normal]; IF sliceD#NIL THEN { newD: SliceDescriptor ¬ GGSliceOps.NewParts[sliceD.slice, hitData, mode]; diffD: SliceDescriptor ¬ GGSliceOps.DifferenceParts[sliceD, newD]; GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal]; IF NOT GGSliceOps.IsEmptyParts[diffD] THEN GGSelect.SelectSlice[diffD, scene, normal]; deselectedD ¬ newD; } ELSE deselectedD ¬ shapeD; -- must have already deselected the slice DuringDeselect }; EndDeselect: PUBLIC MouseProc = { resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; SELECT GGState.GetSelectMode[ggData] FROM joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; SELECT ggData.drag.selectState FROM joint => EndDeselectAux[ggData, resultPoint, feature, hitData, joint, "joint"]; segment => EndDeselectAux[ggData, resultPoint, feature, hitData, segment, "segment"]; traj => EndDeselectAux[ggData, resultPoint, feature, hitData, traj, "trajectory"]; topLevel => EndDeselectAux[ggData, resultPoint, feature, hitData, topLevel, "top level object"]; ENDCASE => ERROR; }; EndDeselectAux: PROC [ggData: GGData, resultPoint: Point, feature: FeatureData, hitData: REF ANY, mode: SelectMode, selectRope: Rope.ROPE] = { GGCaret.SitOn[ggData.caret, NIL]; IF feature = NIL THEN { Feedback.PutF[ggData.router, oneLiner, $Feedback, "No near %g found", [rope[selectRope]]]; } ELSE { SELECT feature.type FROM slice => { gone: SliceDescriptor ¬ SliceDeselectAux[feature, hitData, mode, ggData.scene]; IF gone#NIL AND gone.parts#NIL THEN Feedback.PutFL[ggData.router, oneLiner, $Feedback, "%g %g", LIST[[rope[GGSliceOps.Describe[gone]]], [rope["deselected"]]]]; }; ENDCASE => SIGNAL Problem [msg: IO.PutFR1["Unexpected feature type for deselect %g.", [rope[selectRope]]]]; }; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE]; ggData.drag.selectState ¬ none; -- added to help DescribeFeature work. KAP. }; StartExtendSelect: PUBLIC StartProc = { SELECT input.first FROM $StartExtSelectJoint => GGState.SetExtendMode[ggData, joint]; $StartExtSelectSegment => GGState.SetExtendMode[ggData, segment]; $StartExtSelectTrajectory => GGState.SetExtendMode[ggData, traj]; $StartExtSelectTopLevel => GGState.SetExtendMode[ggData, topLevel]; ENDCASE => ERROR; RETURN StartExtendSelection[input, ggData, worldPt]; }; StartExtendSelection: PUBLIC StartProc = { IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; GGMouseEvent.SaveSavedState[ggData]; -- must do this before any possible aborts occur ggData.drag.currentPoint ¬ worldPt; ggData.drag.transform ¬ ImagerTransformation.Scale[1.0]; DuringExtendSelection[LIST[$StartExtendSelection], ggData, worldPt]; }; DuringExtendSelection: PUBLIC MouseProc= { opRope: Rope.ROPE = IF input.first=$EndExtendSelection THEN "Extended to" ELSE "Extending to"; resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; extendMode: ExtendMode ¬ GGState.GetExtendMode[ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE]; SELECT extendMode FROM joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment, segmentRange => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; traj, topLevel, slice => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FacesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; none => { resultPoint ¬ worldPt; normal ¬ [0, -1]; feature ¬ NIL; hitData ¬ NIL; }; ENDCASE => ERROR; SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; GGSelect.DeselectAll[ggData.scene, normal]; GGScene.RestoreSelections[ggData.scene]; IF feature#NIL THEN { SELECT extendMode FROM joint, segment, segmentRange, traj, topLevel => DuringExtendSelectionFeedback[feature, hitData, resultPoint, ggData, opRope]; slice, none => NULL; -- NoOp ENDCASE => ERROR; } ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: TRUE]; }; -- end DuringExtendSelection DuringExtendSelectionFeedback: PROC [feature: FeatureData, hitData: REF ANY, caretPt: Point, ggData: GGData, opRope: Rope.ROPE] = { extendMode: ExtendMode ¬ GGState.GetExtendMode[ggData]; SELECT extendMode FROM joint, segment, traj, topLevel => [] ¬ SelectAndDescribeSlicePart[feature, hitData, ggData, extendMode, opRope]; segmentRange => { IF ISTYPE[feature.shape, SliceDescriptor] THEN { sliceD: SliceDescriptor ¬ NARROW[feature.shape]; -- clumsy code traj: Traj; trajToExtend: SliceDescriptor; hitType: GGModelTypes.TrajPartType; segNum, cpNum, jointNum, segToExtendNum: INT; hitPoint: Point; SELECT GGSliceOps.GetType[sliceD.slice] FROM $Outline => [traj, hitType, segNum, cpNum, jointNum, hitPoint] ¬ GGOutline.UnpackHitData[hitData]; $Traj => { traj ¬ sliceD.slice; [hitType, segNum, cpNum, jointNum, hitPoint] ¬ GGTraj.UnpackHitData[hitData]; }; ENDCASE => GOTO RegularSelectMechanism; IF (hitType = segment OR hitType = controlPoint) THEN { [trajToExtend, segToExtendNum] ¬ GGOutline.UnpackOneSegmentDescriptor[GGState.GetSliceToExtend[ggData]]; IF trajToExtend#NIL AND trajToExtend.slice = traj THEN { -- we're extending segments in the same trajectory seq: TrajParts ¬ GGSequence.CreateFromSegments[NARROW[traj.data], segToExtendNum, segNum]; GGSelect.SelectSlice[GGSlice.DescriptorFromParts[traj, seq], ggData.scene, normal]; -- select current hit Feedback.PutFL[ggData.router, oneLiner, $DuringMouse, "%g %g", LIST[[rope[opRope]], [rope[GGUtility.DescribeSegment[traj, segNum] ]]] ]; } ELSE { -- you extended to a different traj GGState.SetSliceToExtend[ggData, SelectAndDescribeSlicePart[feature, hitData, ggData, segment, opRope]]; } } ELSE GOTO RegularSelectMechanism; } ELSE GOTO RegularSelectMechanism; EXITS RegularSelectMechanism => [] ¬ SelectAndDescribeSlicePart[feature, hitData, ggData, segment, opRope]; }; ENDCASE => ERROR; -- should have been weeded about before calling this Proc }; EndExtendSelection: PUBLIC MouseProc = { DuringExtendSelection[LIST[$EndExtendSelection], ggData, worldPt]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; -- DuringExtendSelection did the final repaint }; -- end EndExtendSelection StartAddBezier: PUBLIC StartProc = { IF GGCaret.Exists[ggData.caret] THEN { caret: Caret; oldTraj, newTraj: Traj; ancestor, oldOutline: Slice; trajEnd: TrajEnd; newNormalD, newHotD: SliceDescriptor; startBox: BoundBox _ GGRefresh.GetABox[ggData: ggData, dragInProgress: TRUE, selectedCPs: TRUE, into: NIL]; -- because UpdateSceneForAddBezier will DeselectAll GGHistory.NewCapture["Add Bezier spline", ggData]; -- capture scene BEFORE UPDATE caret ¬ ggData.caret; GGMouseEvent.SaveSavedState[ggData]; -- used for abort [oldTraj, newTraj, trajEnd, ancestor] ¬ UpdateSceneForAddBezier[ggData.scene, worldPt, caret, ggData.defaults, ggData]; oldOutline ¬ IF oldTraj=NIL THEN NIL ELSE GGParent.GetTopLevelAncestor[oldTraj]; [newNormalD, newHotD] ¬ GGMouseEvent.UpdateSelectionsForAdd[ggData.scene, oldTraj, newTraj, trajEnd]; GGMouseEvent.UpdateCaretForAdd[caret, ancestor, newNormalD, worldPt]; [] ¬ GGAlign.UpdateBagsForAdd[oldOutline, newNormalD, trajEnd, ggData]; -- should be ok [] ¬ GGMouseEvent.StartMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE, startBox: startBox]; -- boundbox may be wrong DuringBezierDrag[NIL, ggData, worldPt]; -- draw it the first time } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Start Bezier failed: caret required for adding new Bezier"]; }; UpdateSceneForAddBezier: PROC [scene: Scene, worldPt: Point, caret: Caret, defaults: DefaultData, ggData: GGData] RETURNS [oldTraj, newTraj: Traj, trajEnd: TrajEnd, newOutline: Slice] = { caretPoint, newCaretPoint: Point; jointNum: NAT; newSeg, extendFromSeg, newBezier: Segment; chair: SliceDescriptor; thisTraj: SliceDescriptor; thisTrajParts: TrajParts; thisTrajData: TrajData; thisOutlineSlice: Slice; addToBezier: BOOL ¬ FALSE; sliceDesc: SliceDescriptor ¬ GGScene.FirstSelectedSlice[scene, leaf, normal, $Outline]; caretPoint ¬ GGCaret.GetPoint[caret]; newCaretPoint ¬ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"]; IF sliceDesc # NIL THEN { -- otherwise nothing selected. thisOutlineSlice ¬ sliceDesc.slice; thisTraj ¬ GGParent.FirstIncludedChild[sliceDesc.slice, sliceDesc.parts, first, $Traj]; thisTrajParts ¬ NARROW[thisTraj.parts]; thisTrajData ¬ NARROW[thisTraj.slice.data]; IF thisTrajData.role = open AND GGSequence.ContainsJoint[thisTrajParts, 0] THEN { IF GGTraj.FetchSegment[thisTraj.slice, 0].class.type = $Bezier THEN { trajEnd ¬ lo; addToBezier ¬ TRUE; }; } ELSE { IF thisTrajData.role = open AND GGSequence.ContainsJoint[thisTrajParts, GGTraj.HiJoint[thisTraj.slice]] THEN { IF GGTraj.FetchSegment[thisTraj.slice, GGTraj.HiSegment[thisTraj.slice]].class.type = $Bezier THEN { trajEnd ¬ hi; addToBezier ¬ TRUE; }; }; }; }; IF addToBezier AND ggData.drag.editConstraints # none THEN { -- Lay down a new Bezier and a tangent guide. oldCtrlPt, newCtrlPt: Point; oldTraj ¬ newTraj ¬ thisTraj.slice; -- the traj that the Bezier belongs to. extendFromSeg ¬ GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]]; IF trajEnd = lo THEN { oldCtrlPt ¬ extendFromSeg.class.controlPointGet[extendFromSeg, 0]; newCtrlPt ¬ Vectors2d.Add[extendFromSeg.lo, Vectors2d.VectorFromPoints[oldCtrlPt, extendFromSeg.lo]]; newBezier ¬ GGSegment.MakeBezier[extendFromSeg.lo, newCtrlPt, newCaretPoint, newCaretPoint, NIL]; -- by the lo in AddSegment. } ELSE { oldCtrlPt ¬ extendFromSeg.class.controlPointGet[extendFromSeg, 1]; newCtrlPt ¬ Vectors2d.Add[extendFromSeg.hi, Vectors2d.VectorFromPoints[oldCtrlPt, extendFromSeg.hi]]; newBezier ¬ GGSegment.MakeBezier[extendFromSeg.hi, newCtrlPt, newCaretPoint, newCaretPoint, NIL]; }; newSeg ¬ GGSegment.MakeLine[newCaretPoint, worldPt, NIL]; -- ctrl tangent. GGSegment.SetDefaults[newSeg, defaults]; GGSegment.CopyLooks[extendFromSeg, newBezier]; newSeg.strokeWidth ¬ 1; [] ¬ GGTraj.AddSegment[newTraj, trajEnd, newBezier, lo]; [] ¬ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo]; ggData.drag.bezierDrag.draggingBezier ¬ TRUE; ggData.drag.bezierDrag.trajEnd ¬ trajEnd; ggData.drag.bezierDrag.bezierNum ¬ IF trajEnd=lo THEN 1 ELSE GGTraj.HiSegment[newTraj] - 1; ggData.drag.bezierDrag.traj ¬ newTraj; ggData.drag.bezierDrag.outlineSlice ¬ thisOutlineSlice; newOutline ¬ thisOutlineSlice; -- that the Bezier was in. } ELSE { -- We are not adding to a Bezier in constrained mode, so can only add first tangent guide. newSeg ¬ GGSegment.MakeLine[caretPoint, worldPt, NIL]; -- maybe drag caret first!!!? GGSegment.SetDefaults[newSeg, defaults]; newSeg.strokeWidth ¬ 1; IF GGCaret.SittingOnEnd[caret] THEN { -- Extending a Traj [chair, newTraj, jointNum] ¬ GGMouseEvent.SafelyGetCaretTraj[caret]; oldTraj ¬ newTraj; trajEnd ¬ SELECT jointNum FROM 0 => lo, GGTraj.HiJoint[newTraj] => hi, ENDCASE => ERROR; extendFromSeg ¬ GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]]; [] ¬ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo]; newOutline ¬ chair.slice; } ELSE { -- create a new traj. oldTraj ¬ NIL; trajEnd ¬ hi; newTraj ¬ GGTraj.CreateTraj[caretPoint]; GGSliceOps.SetStrokeJoint[newTraj, NIL, defaults.strokeJoint, NIL]; [] ¬ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo]; newOutline ¬ GGOutline.CreateOutline[newTraj, defaults.fillColor]; GGScene.AddSlice[scene, newOutline, -1]; }; ggData.drag.bezierDrag.trajEnd ¬ trajEnd; -- here used to track tangent. ggData.drag.bezierDrag.bezierNum ¬ IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]; ggData.drag.bezierDrag.traj ¬ newTraj; ggData.drag.bezierDrag.outlineSlice ¬ newOutline; }; }; DuringBezierDrag: PUBLIC MouseProc = { totalDragVector: Vector; mapPoint: Point; mapPoint ¬ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"]; totalDragVector ¬ Vectors2d.Sub[mapPoint, ggData.drag.startPoint]; ggData.drag.transform ¬ ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]]; IF ggData.drag.bezierDrag.draggingBezier THEN { newCP: Point; dragCP: Vector; bezier: Segment ¬ GGTraj.FetchSegment[ggData.drag.bezierDrag.traj, ggData.drag.bezierDrag.bezierNum]; IF ggData.drag.bezierDrag.trajEnd = lo THEN { dragCP ¬ Vectors2d.Sub[mapPoint, bezier.lo]; newCP ¬ Vectors2d.Sub[bezier.lo, dragCP] } ELSE { dragCP ¬ Vectors2d.Sub[mapPoint, bezier.hi]; newCP ¬ Vectors2d.Sub[bezier.hi, dragCP]; }; GGSegment.BZControlPointMovedTo[bezier, newCP, IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE 1]; }; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; -- end DuringBezierDrag ContinueBezierAdd: PUBLIC StartProc = { bezierSetCP, bezierSetJoint, caretPoint, newCaretPoint: Point; trajEnd: TrajEnd; extendFromSeg, newBezier, newSeg: Segment; oldTraj: Traj ¬ ggData.drag.bezierDrag.traj; tangentSeg: Segment; thisTraj: Traj; hiSegNum: NAT ¬ GGTraj.HiSegment[oldTraj]; priority: INT; newNormal, newHot: Sequence; delSeq: TrajParts; newOutlines: LIST OF Slice; thisOutlineSlice, oldOutlineSlice: Slice; thisOutline: GGOutline.OutlineData; ancestorD: SliceDescriptor; GGMouseEvent.TransformObjectsAfterMove[ggData.scene, ggData.drag.transform, ggData.drag.editConstraints, NIL]; -- update the tangent line. don't try to record undoable transforms here. A capture event is the current event. GGHistory.PushCurrent[ggData]; -- push captured history event onto history list GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; GGMouseEvent.SaveSavedState[ggData]; -- used for abort newCaretPoint ¬ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"]; -- use Gravity for now. caretPoint ¬ GGCaret.GetPoint[ggData.caret]; IF ggData.drag.bezierDrag.draggingBezier THEN { IF ggData.drag.bezierDrag.trajEnd = hi THEN tangentSeg ¬ GGTraj.FetchSegment[oldTraj, ggData.drag.bezierDrag.bezierNum + 1] ELSE tangentSeg ¬ GGTraj.FetchSegment[oldTraj, 0]; } ELSE tangentSeg ¬ GGTraj.FetchSegment[oldTraj, ggData.drag.bezierDrag.bezierNum]; IF ggData.drag.bezierDrag.trajEnd = hi THEN { trajEnd ¬ hi; bezierSetJoint ¬ tangentSeg.lo; bezierSetCP ¬ tangentSeg.hi; IF hiSegNum # 0 THEN { extendFromSeg ¬ GGTraj.FetchSegment[oldTraj, hiSegNum -1]; }; } ELSE { trajEnd ¬ lo; bezierSetJoint ¬ tangentSeg.hi; bezierSetCP ¬ tangentSeg.lo; IF hiSegNum # 0 THEN { extendFromSeg ¬ GGTraj.FetchSegment[oldTraj, 1]; }; }; oldOutlineSlice ¬ ggData.drag.bezierDrag.outlineSlice; priority ¬ GGScene.GetPriority[scene: ggData.scene, slice: oldOutlineSlice]; newBezier ¬ GGSegment.MakeBezier[bezierSetJoint, bezierSetCP, newCaretPoint, newCaretPoint, NIL]; newSeg ¬ GGSegment.MakeLine[newCaretPoint, worldPt, NIL]; IF hiSegNum # 0 THEN { -- The traj contains more than just control vector. GGSegment.CopyLooks[extendFromSeg, newBezier]} ELSE GGSegment.SetDefaults[newBezier, ggData.defaults]; GGSegment.SetDefaults[newSeg, ggData.defaults]; newSeg.strokeWidth ¬ 1; delSeq ¬ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment. newOutlines ¬ GGScene.DeleteSequence[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq], ggData.scene].newOutlines; ancestorD ¬ GGParent.TopLevelDescriptorFromChildDescriptor[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq]]; IF newOutlines = NIL THEN { -- Only a control vector in this outline. thisTraj ¬ GGTraj.CreateTraj[newBezier.lo]; thisOutlineSlice ¬ GGOutline.CreateOutline[thisTraj, ggData.defaults.fillColor]; GGSliceOps.SetStrokeJoint[thisTraj, NIL, ggData.defaults.strokeJoint, NIL]; [] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newBezier, lo]; [] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newSeg, lo]; GGScene.AddSlice[ggData.scene, thisOutlineSlice, priority]; } ELSE { thisOutlineSlice ¬ newOutlines.first; thisOutline ¬ NARROW[thisOutlineSlice.data]; thisTraj ¬ thisOutline.children.first; [] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newBezier, lo]; [] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newSeg, lo]; }; ggData.drag.bezierDrag.draggingBezier ¬ TRUE; ggData.drag.bezierDrag.bezierNum ¬ IF trajEnd=lo THEN 1 ELSE hiSegNum; ggData.drag.bezierDrag.traj ¬ thisTraj; ggData.drag.bezierDrag.outlineSlice ¬ thisOutlineSlice; GGHistory.NewCapture["Add Bezier spline", ggData]; -- capture scene BEFORE UPDATE [newNormal, newHot] ¬ GGMouseEvent.UpdateSelectionsForAdd[ggData.scene, oldTraj, thisTraj, trajEnd]; GGMouseEvent.UpdateCaretForAdd[ggData.caret, thisOutlineSlice, newNormal, worldPt]; [] ¬ GGAlign.UpdateBagsForAdd[oldOutlineSlice, ancestorD, trajEnd, ggData]; [] ¬ GGMouseEvent.ContinueMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, startBox: NIL]; DuringBezierDrag[NIL, ggData, worldPt]; -- draw it the first time }; EndBezierAdd: PUBLIC MouseProc = { delSeq: TrajParts; newOutlines: LIST OF Slice; point: Point; normal: Vector; newEndJoint: NAT; CodeTimer.StartInt[$EndBezierAdd, $Gargoyle]; SELECT ggData.drag.bezierDrag.trajEnd FROM lo => newEndJoint ¬ 1; hi => newEndJoint ¬ GGTraj.HiJoint[ggData.drag.bezierDrag.traj] -1; ENDCASE; point ¬ GGTraj.FetchJointPos[ggData.drag.bezierDrag.traj, newEndJoint]; normal ¬ GGTraj.FetchJointNormal[ggData.drag.bezierDrag.traj, newEndJoint]; delSeq ¬ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment. [----, newOutlines] ¬ GGScene.DeleteSequence[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq], ggData.scene]; IF newOutlines = NIL THEN { -- we removed the last segment GGCaret.SitOn[ggData.caret, NIL]; } ELSE { jointParts: TrajParts; jointD, outD: SliceDescriptor; shortOutline: Slice; shortTraj: Traj; shortOutline ¬ newOutlines.first; shortTraj ¬ GGParent.FirstChild[shortOutline, first, $Traj]; jointParts ¬ GGSequence.CreateFromJoint[NARROW[shortTraj.data], IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE newEndJoint]; jointD ¬ GGSlice.DescriptorFromParts[shortTraj, jointParts]; -- now have traj descriptor. Make an outline descriptor outD ¬ GGParent.TopLevelDescriptorFromChildDescriptor[jointD]; GGCaret.SitOn[ggData.caret, outD]; GGSelect.SelectSlice[sliceD: outD, scene: ggData.scene, selectClass: normal]; }; GGCaret.SetAttractor[ggData.caret, point, normal, NIL]; GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[ggData.drag.bezierDrag.traj], NIL]; ggData.drag.bezierDrag.draggingBezier ¬ FALSE; ggData.drag.bezierDrag.outlineSlice ¬ NIL; ggData.drag.bezierDrag.traj ¬ NIL; GGHistory.PushCurrent[ggData]; -- put captured scene on history list GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$EndBezierAdd, $Gargoyle]; }; -- end EndBezierAdd AbortBezierAdd: PUBLIC PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { delSeq, partSeq: TrajParts; delD: SliceDescriptor; newOutlines: LIST OF Slice; newEndJoint: NAT; SELECT ggData.drag.bezierDrag.trajEnd FROM lo => newEndJoint ¬ 1; hi => newEndJoint ¬ GGTraj.HiJoint[ggData.drag.bezierDrag.traj] -1; ENDCASE; delSeq ¬ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment. delD ¬ GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq]; IF ggData.drag.bezierDrag.draggingBezier THEN { -- also delete the bezier ggData.drag.bezierDrag.draggingBezier ¬ FALSE; partSeq ¬ GGSequence.CreateFromSegment[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.bezierNum]; delD ¬ GGSequence.Union[delD, GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, partSeq]]; IF ggData.drag.bezierDrag.trajEnd = hi THEN newEndJoint ¬ newEndJoint -1 ELSE newEndJoint ¬ newEndJoint +1; }; [----, newOutlines] ¬ GGScene.DeleteSequence[delD, ggData.scene]; IF newOutlines = NIL THEN { -- we removed the last segment GGCaret.SitOn[ggData.caret, NIL]; } ELSE { jointParts: TrajParts; jointD, outD: SliceDescriptor; shortOutline: Slice; shortTraj: Traj; IF newOutlines.rest # NIL THEN SIGNAL Problem[msg: "failed assertion"]; shortOutline ¬ newOutlines.first; shortTraj ¬ GGParent.FirstChild[shortOutline, first, $Traj]; jointParts ¬ GGSequence.CreateFromJoint[NARROW[shortTraj.data], IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE newEndJoint]; jointD ¬ GGSlice.DescriptorFromParts[shortTraj, jointParts]; -- traj descriptor outD ¬ GGParent.TopLevelDescriptorFromChildDescriptor[jointD]; GGCaret.SitOn[ggData.caret, outD]; GGSelect.SelectSlice[sliceD: outD, scene: ggData.scene, selectClass: normal]; }; GGCaret.SetAttractor[ggData.caret, GGTraj.FetchJointPos[ggData.drag.bezierDrag.traj, newEndJoint], GGTraj.FetchJointNormal[ggData.drag.bezierDrag.traj, newEndJoint], NIL]; ggData.drag.bezierDrag.outlineSlice ¬ NIL; ggData.drag.bezierDrag.traj ¬ NIL; GGMouseEvent.FinishAbort[ggData]; }; GGUserInput.RegisterAction[$Select, Select, none, FALSE]; END.  GGMouseEventImplB.mesa Copyright Σ 1986, 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved. Contents: Once a mouse event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Kurlander July 17, 1986 2:40:45 pm PDT Eisenman, August 7, 1987 1:41:04 pm PDT Maureen Stone, October 2, 1987 3:57:53 pm PDT Pier, October 2, 1992 12:37 pm PDT Bier, July 5, 1993 1:17 pm PDT Doug Wyatt, April 17, 1992 4:19 pm PDT Special Behaviors ELSE IF theirData = ggData THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a non-palette Gargoyle viewer"]; RETURN; } ELSE IF theirData = ggData THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Place input focus in a non-palette Gargoyle viewer"]; RETURN; } Selection Procs While a joint, segment, traj, or top level object is being selected, gravity is forced to be LinesPreferred. The object bag should consist only of trajectories and slices. The caret is moved to the segment endpoint of the nearest segment or traj as appropriate or tracks the cursor if none are nearby. Feedback is in the form of highlighted joints. Put Caret on a joint (if any). Deselect all. Dispatch to the proper EndSelect handler for final selection. SELECT ggData.drag.selectState FROM joint => EndSelectAux[ggData, resultPoint, feature, hitData, joint, "joint"]; segment => EndSelectAux[ggData, resultPoint, feature, hitData, segment, "segment"]; traj => EndSelectAux[ggData, resultPoint, feature, hitData, traj, "trajectory"]; topLevel => EndSelectAux[ggData, resultPoint, feature, hitData, topLevel, "object"]; ENDCASE => ERROR; PROC [ggData: GGData, event: LIST OF REF ANY]; PROC [ggData: GGData, event: LIST OF REF ANY]; event: LIST[$Select, worldPt: REF Point, selectMode: {$Joint, $Segment, $Traj, $TopLevel}, gravType: {$PreferPoints, $PreferLines, $PreferFaces}, gravExtent: REF REAL] ggData.hitTest.t is the gravity extent ggData.hitTest.gravityType is the gravity type This routine is used, for instance, for CycleSelection Prepare for a subsequent Add operation and for Extend. Make Selection and Prepare for Extend. Find out which point is being selected and highlight it. The only use for the chair is the StartAdd command, which is a favor to the $Traj class. Thus, we only sit on $Outline and $Traj slices, or $Cluster slices that contain such slices. Deselection Procs While a joint, cp, segment, traj, or top level object is being deselected, gravity is forced to be LinesPreferred. The object bag should consist only of trajectories and slices. The caret is moved to the segment endpoint of the nearest segment or traj as appropriate or tracks the cursor if none are nearby. Feedback is in the form of removing highlighted joints or cps. Reselect all. Clear the overlay plane. GGRefresh.MoveOverlayToBackground[ggData]; -- WHY? KAP. April 17, 1992 And Dispatch to the proper EndDeselect handler. Extend selection procs Put Caret on a joint (if any). Reselect all. depending on extend mode, check if feature is in that mode, then select the new feature. GGCaret.DoNotSit[ggData.caret]; -- simple but not always correct thing to do simply paint new selections directly Bezier Stuff If we have the EndPoint of a Bezier Selected then set up the conditions to drag out a new Bezier in its trajectory. Otherwise we will drag a single tangent for what will become a Bezier. We have added the NEW segment and the bags are correct and static. Some how must look through all selected stuff and see if I have a selected Bezier endjount. If so then addToBezier is set to true. Remember the endjoint of the tangent vector and delete the segment. Replace it with a Bezier using the information for one end and extend the traj. as in AddBezier. Get ready for the drag. Caret is sitting on a Line segment (the tangent segment) that has been appended to the trajectory that is being extended. The endpoint of the Line is selected. ggData.refresh.startBoundBox­ ¬ GGSliceOps.GetBoundBox[ggData.drag.bezierDrag.traj]­; Κ Έ–(cedarcode) style•NewlineDelimiter ™code™Kšœ ΟeœI™TKšΟnœ}™…Kšœ#Οk™&Kšœ'™'K™-K™"K™K™&K™—šŸ ˜ Jšœ΄Ÿœ"˜Ψ—K˜šžœŸœŸ˜ JšŸœ‘Ÿœ˜»KšŸœ"Ÿ˜.K˜Kšœ ŸœŸœ˜3K˜Kšœ Ÿœ˜+KšœŸœ#˜7Kšœ Ÿœ˜'KšœŸœ˜%KšœŸœ˜Kšœ Ÿœ˜-Kšœ Ÿœ˜/KšœŸœ"˜5Kšœ Ÿœ˜-Kšœ Ÿœ ˜1Kšœ Ÿœ˜*KšœŸœ˜'KšœŸœ˜1KšœŸœ˜!Kšœ Ÿœ˜&Kšœ Ÿœ˜)KšœŸœ˜!KšœŸœŸœ!˜˜>K˜Kšœ*˜*K˜,Kšœ˜K˜Kšœ Ÿœ˜*Kšœ Ÿœ˜Kšœ˜Kšœ˜Kšœ ŸœŸœ˜K˜)Kšœ#˜#K˜K˜KšœiŸœ‘p˜ίKš œ ‘0˜PKšœ*˜*Kšœ˜Kšœ%‘˜6K˜KšœJ‘˜aK˜,K˜šŸœ&Ÿœ˜/KšŸœ$ŸœP˜{KšŸœ.˜2K˜—KšŸœM˜QK˜šŸœ$Ÿœ˜-K˜ K˜K˜šŸœŸœŸ˜K˜:K˜—K˜—šŸœ˜K˜ K˜K˜šŸœŸœŸ˜K˜0K˜—K˜—K˜6K˜LKšœ\Ÿœ˜aKšœ4Ÿœ˜9šŸœŸœ‘3˜JKšœ.˜.—KšŸœ3˜7Kšœ/˜/K˜Kšœ$ŸœF‘˜‡K˜K˜}šŸœŸœŸœ‘)˜EK˜+K˜PKšœ$ŸœŸœ˜KK˜9K˜6Kšœ;˜;K˜—šŸœ˜K˜%KšœŸœ˜,K˜&K˜9K˜6K˜—Kšœ(Ÿœ˜-Kšœ#Ÿœ ŸœŸœ ˜FK˜'K˜7K˜Kšœ   œ‘˜QK˜K˜dKšœS˜SK˜KKšœlŸœ˜qKšœŸœ‘˜AK˜K˜—šž œŸœ˜"K™yK™%Kšœ˜Kšœ ŸœŸœ˜Kšœ ˜ Kšœ˜Kšœ Ÿœ˜K˜Kšœ-˜-šŸœ Ÿ˜*K˜K˜CKšŸœ˜—K˜GK˜KKšœ$ŸœF‘˜‡Kšœ‘œx˜}šŸœŸœŸœ‘˜:KšœŸœ˜!K˜—šŸœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜!K˜Kšœ"˜"KšœM˜MK˜—Kšœ2Ÿœ˜7K™UKšœ   œ ˜Kšœ  œ>Ÿœ˜\Kšœ(Ÿœ˜.Kšœ&Ÿœ˜*KšœŸœ˜"K˜Kš œ ‘%˜EK˜Kšœ*˜*Kšœ˜Kšœ}ŸœŸœ˜›Kšœ,˜,Kšœ‘˜K˜—š žœŸ œ ŸœŸœŸœŸœ%˜XKšœ˜K˜Kšœ ŸœŸœ˜Kšœ Ÿœ˜K˜šŸœ Ÿ˜*K˜K˜CKšŸœ˜—Kšœ$ŸœF‘˜‡K˜HšŸœ'Ÿœ‘˜JKšœ(Ÿœ˜.Kšœ'ŸœF˜sK˜aKšŸœ$Ÿœ˜HKšŸœ˜"K˜K˜—Kšœ‘œ<˜AšŸœŸœŸœ‘˜:KšœŸœ˜!K˜—šŸœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜KšŸœŸœŸœŸœ"˜GK˜!K˜Kšœ"˜"KšœM˜MK˜—Kšœ¦Ÿœ˜«Kšœ&Ÿœ˜*KšœŸœ˜"Kšœ!˜!K˜—K˜Kšœ2Ÿœ˜9KšŸœ˜—…—₯–Φf