<> <> <> <> <> <<>> DIRECTORY GGAlign, GGBasicTypes, GGBoundBox, GGBoxCluster, GGCaret, GGDeselectEvent, GGEvent, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGObjects, GGRefresh, GGSegment, GGSelect, GGSequence, GGSlackProcess, GGTouch, GGTransform, GGVector, GGWindow, Imager, ImagerTransformation, InputFocus, IO, Menus, Rope, Rosary; GGMouseEventImpl: CEDAR PROGRAM IMPORTS GGAlign, GGBoundBox, GGBoxCluster, GGCaret, GGDeselectEvent, GGError, GGEvent, GGGravity, GGObjects, GGRefresh, GGSegment, GGSelect, GGSequence, GGSlackProcess, GGTouch, GGTransform, GGVector, GGWindow, Imager, ImagerTransformation, InputFocus, IO, Rosary EXPORTS GGMouseEvent = BEGIN BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; Cluster: TYPE = GGModelTypes.Cluster; ClusterParts: TYPE = GGModelTypes.ClusterParts; ClusterGenerator: TYPE = GGModelTypes.ClusterGenerator; ClusterDescriptor: TYPE = GGModelTypes.ClusterDescriptor; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; FeatureData: TYPE = GGGravity.FeatureData; Joint: TYPE = GGModelTypes.Joint; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; MouseButton: TYPE = Menus.MouseButton; ObjectBag: TYPE = GGGravity.ObjectBag; Outline: TYPE = GGModelTypes.Outline; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGModelTypes.Segment; Sequence: TYPE = GGModelTypes.Sequence; TouchGroup: TYPE = GGModelTypes.TouchGroup; TouchItem: TYPE = GGModelTypes.TouchItem; TouchItemGenerator: TYPE = GGTouch.TouchItemGenerator; Traj: TYPE = GGModelTypes.Traj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajGenerator: TYPE = GGObjects.TrajGenerator; TriggerBag: TYPE = GGAlign.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; MouseProc: TYPE = GGMouseEvent.MouseProc; <> StartProc: TYPE = GGMouseEvent.StartProc; <> UnexpectedType: PUBLIC ERROR = CODE; NotYetImplemented: PUBLIC SIGNAL = CODE; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE; EasyAbort: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> normalSelectedList: LIST OF REF ANY _ gargoyleData.drag.savedSelected.normal; GGSelect.DeselectAll[gargoyleData, normal]; -- get rid of any transient selections <> <> <> FOR list: LIST OF REF ANY _ normalSelectedList, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM cD: ClusterDescriptor => GGSelect.SelectCluster[cD.cluster, cD.parts, gargoyleData, normal]; ENDCASE => GGSelect.SelectEntity[list.first, gargoyleData, normal]; ENDLOOP; GGCaret.Copy[from: gargoyleData.drag.savedCaret, to: gargoyleData.caret]; --restore original caret RestoreInvariants[gargoyleData]; GGError.AppendHerald[". . . Aborted.", FALSE]; GGWindow.Painter[$PaintEntireScene, gargoyleData]; -- only way to accurately restore picture }; AbortAdd: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { FixupAbortedAdd[gargoyleData]; RestoreInvariants[gargoyleData]; GGError.AppendHerald[". . . Aborted.", TRUE]; }; AbortBox: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { FixupAbortedBox[gargoyleData]; RestoreInvariants[gargoyleData]; GGError.AppendHerald[". . . Aborted.", TRUE]; }; <> <> <> <> <> <> <<};>> <<>> <> <<};>> <<>> <> <<};>> RestoreInvariants: PROC [gargoyleData: GargoyleData] = { <> GGRefresh.MoveOverlayToBackground[gargoyleData]; }; ResetMouseMachinery: PUBLIC PROC [gargoyleData: GargoyleData] = { gargoyleData.mouseMode _ $None; gargoyleData.state _ $None; <> RestoreInvariants[gargoyleData]; }; SaveSavedState: PROC [gargoyleData: GargoyleData] = { gargoyleData.drag.savedSelected _ gargoyleData.selected; GGCaret.Copy[from: gargoyleData.caret, to: gargoyleData.drag.savedCaret]; }; <> <<>> HandleMouse: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { atom: ATOM; SELECT gargoyleData.mouseMode FROM $CaretPos => HandleGuarded[StartCaretPos, DuringCaretPos, EndCaretPos, EasyAbort, input, gargoyleData, worldPt]; $Add => HandleGuarded[StartAdd, DuringAdd, EndAdd, AbortAdd, input, gargoyleData, worldPt]; $Box => HandleGuarded[StartBox, DuringBox, EndBox, AbortBox, input, gargoyleData, worldPt]; $Drag => HandleGuarded[StartDrag, DuringDrag, EndDrag, EasyAbort, input, gargoyleData, worldPt]; $CopyAndDrag => HandleGuarded[CopySelected, DuringDrag, EndDrag, EasyAbort, input, gargoyleData, worldPt]; $Rotate => HandleGuarded[StartRotate, DuringRotate, EndRotate, EasyAbort, input, gargoyleData, worldPt]; $Scale => HandleGuarded[StartScale, DuringScale, EndScale, EasyAbort, input, gargoyleData, worldPt]; $SelectJoint => HandleUnGuarded[StartSelectJoint, DuringSelect, EndSelect, EasyAbort, input, gargoyleData, worldPt]; $SelectSegment => HandleUnGuarded[StartSelectSegment, DuringSelect, EndSelect, EasyAbort, input, gargoyleData, worldPt]; $SelectTrajectory => HandleUnGuarded[StartSelectTrajectory, DuringSelect, EndSelect, EasyAbort, input, gargoyleData, worldPt]; $SelectTopLevel => HandleUnGuarded[StartSelectTopLevel, DuringSelect, EndSelect, EasyAbort, input, gargoyleData, worldPt]; $Extend => HandleUnGuarded[GGDeselectEvent.StartExtendSelection, GGDeselectEvent.DuringExtendSelection, GGDeselectEvent.EndExtendSelection, EasyAbort, input, gargoyleData, worldPt]; $DeselectJoint => HandleGuarded[GGDeselectEvent.StartDeselectJoint, GGDeselectEvent.DuringDeselect, GGDeselectEvent.EndDeselect, EasyAbort, input, gargoyleData, worldPt]; $DeselectSegment => HandleGuarded[GGDeselectEvent.StartDeselectSegment, GGDeselectEvent.DuringDeselect, GGDeselectEvent.EndDeselect, EasyAbort, input, gargoyleData, worldPt]; $DeselectTrajectory => HandleGuarded[GGDeselectEvent.StartDeselectTrajectory, GGDeselectEvent.DuringDeselect, GGDeselectEvent.EndDeselect, EasyAbort, input, gargoyleData, worldPt]; $DeselectTopLevel => HandleGuarded[GGDeselectEvent.StartDeselectTopLevel, GGDeselectEvent.DuringDeselect, GGDeselectEvent.EndDeselect, EasyAbort, input, gargoyleData, worldPt]; $None => { WITH input.first SELECT FROM refChar: REF CHAR => { GGSlackProcess.QueueInputActionNoPoint[GGEvent.AddChar, input, gargoyleData]; RETURN; }; ENDCASE; atom _ NARROW[input.first]; SELECT atom FROM $StartCaretPos => { gargoyleData.mouseMode _ $CaretPos; HandleMouse[input, gargoyleData, worldPt]; }; $StartAdd => { gargoyleData.mouseMode _ $Add; HandleMouse[input, gargoyleData, worldPt]; }; $StartBox => { gargoyleData.mouseMode _ $Box; HandleMouse[input, gargoyleData, worldPt]; }; $StartDrag => { gargoyleData.mouseMode _ $Drag; HandleMouse[input, gargoyleData, worldPt]; }; $StartCopyAndDrag => { gargoyleData.mouseMode _ $CopyAndDrag; HandleMouse[input, gargoyleData, worldPt]; }; $StartRotate => { gargoyleData.mouseMode _ $Rotate; HandleMouse[input, gargoyleData, worldPt]; }; $StartScale => { gargoyleData.mouseMode _ $Scale; HandleMouse[input, gargoyleData, worldPt]; }; $StartSelectJoint => { gargoyleData.mouseMode _ $SelectJoint; HandleMouse[input, gargoyleData, worldPt]; }; $StartSelectSegment => { gargoyleData.mouseMode _ $SelectSegment; HandleMouse[input, gargoyleData, worldPt]; }; $StartSelectTrajectory => { gargoyleData.mouseMode _ $SelectTrajectory; HandleMouse[input, gargoyleData, worldPt]; }; $StartSelectTopLevel => { gargoyleData.mouseMode _ $SelectTopLevel; HandleMouse[input, gargoyleData, worldPt]; }; $StartExtendSelection => { gargoyleData.mouseMode _ $Extend; HandleMouse[input, gargoyleData, worldPt]; }; $StartDeselectJoint => { gargoyleData.mouseMode _ $DeselectJoint; HandleMouse[input, gargoyleData, worldPt]; }; $StartDeselectSegment => { gargoyleData.mouseMode _ $DeselectSegment; HandleMouse[input, gargoyleData, worldPt]; }; $StartDeselectTrajectory => { gargoyleData.mouseMode _ $DeselectTrajectory; HandleMouse[input, gargoyleData, worldPt]; }; $StartDeselectTopLevel => { gargoyleData.mouseMode _ $DeselectTopLevel; HandleMouse[input, gargoyleData, worldPt]; }; ENDCASE; -- ignore other actions }; ENDCASE => SIGNAL Problem[msg: "Unimplemented Mode"]; }; <<>> HandleGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { genericAction, atom: ATOM; WITH input.first SELECT FROM refChar: REF CHAR => RETURN; -- ignore characters during mouse actions ENDCASE; atom _ NARROW[input.first]; SELECT atom FROM $StartCaretPos, $StartAdd, $StartBox, $StartDrag, $StartCopyAndDrag, $StartRotate, $StartScale, $StartSelectJoint, $StartSelectSegment, $StartSelectTrajectory, $StartSelectTopLevel, $StartExtendSelection, $StartDeselectJoint, $StartDeselectSegment, $StartDeselectTrajectory, $StartDeselectTopLevel => genericAction _ $Start; ENDCASE => genericAction _ atom; HandleGuardedAux[startProc, duringProc, endProc, abortProc, genericAction, input, gargoyleData, worldPt]; }; HandleUnGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { genericAction, atom: ATOM; WITH input.first SELECT FROM refChar: REF CHAR => RETURN; -- ignore characters during mouse actions ENDCASE; atom _ NARROW[input.first]; SELECT atom FROM $StartCaretPos, $StartAdd, $StartDrag, $StartCopyAndDrag, $StartRotate, $StartScale, $StartSelectJoint, $StartSelectSegment, $StartSelectTrajectory, $StartSelectTopLevel, $StartExtendSelection => genericAction _ $Start; ENDCASE => genericAction _ atom; HandleUnGuardedAux[startProc, duringProc, endProc, abortProc, genericAction, input, gargoyleData, worldPt]; }; HandleGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction: ATOM, input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { SELECT gargoyleData.state FROM $None => { SELECT genericAction FROM $Start => {IF startProc[input, gargoyleData, worldPt] THEN gargoyleData.state _ $Main ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted;}; }; ENDCASE; }; $Main => { SELECT genericAction FROM $During => duringProc[input, gargoyleData, worldPt]; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted}; $GuardUp => gargoyleData.state _ $GuardUp; $MouseUp => gargoyleData.state _ $MouseUp; ENDCASE; }; $GuardUp => { SELECT genericAction FROM $AllUp => { endProc[input, gargoyleData, gargoyleData.drag.currentPoint]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; }; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted}; ENDCASE; }; $MouseUp => { SELECT genericAction FROM $AllUp => { endProc[input, gargoyleData, gargoyleData.drag.currentPoint]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; }; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted}; $Start => { -- we may be starting another action of this mode or some other mode. endProc[input, gargoyleData, gargoyleData.drag.currentPoint]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; HandleMouse[input, gargoyleData, worldPt]; }; ENDCASE; }; $Aborted => { SELECT genericAction FROM $AllUp => {gargoyleData.state _ $None; gargoyleData.mouseMode _ $None}; ENDCASE; }; ENDCASE => SIGNAL Problem[msg: "Unknown generic state"]; }; HandleUnGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction: ATOM, input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { SELECT gargoyleData.state FROM $None => { SELECT genericAction FROM $Start => {IF startProc[input, gargoyleData, worldPt] THEN gargoyleData.state _ $Main ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted;}; }; ENDCASE; }; $Main => { SELECT genericAction FROM $During => duringProc[input, gargoyleData, worldPt]; <<$MouseUp, $AllUp => { -- CODE REMOVED DUE TO COMPILER BUG>> <> <> <> <<};>> $MouseUp => { endProc[input, gargoyleData, worldPt]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; }; $AllUp => { endProc[input, gargoyleData, worldPt]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; }; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted}; ENDCASE; }; $Aborted => { SELECT genericAction FROM $AllUp => {gargoyleData.state _ $None; gargoyleData.mouseMode _ $None}; $MouseUp => {gargoyleData.state _ $None; gargoyleData.mouseMode _ $None}; ENDCASE; }; ENDCASE => SIGNAL Problem[msg: "Unknown generic state"]; }; <<>> <> StartCaretPos: PUBLIC StartProc = { <> gargoyleData.drag.currentPoint _ worldPt; <> GGAlign.SetBagsForAction[gargoyleData, $CaretPos]; <> StartSelectAux[gargoyleData, worldPt, NIL]; DuringCaretPos[NIL, gargoyleData, worldPt]; }; DuringCaretPos: PUBLIC MouseProc = { <> resultPoint: Point; feature: FeatureData; activeObjects: ObjectBag; sensitiveObjects: TriggerBag; activeObjects _ NARROW[gargoyleData.hitTest.activeObjectBag]; sensitiveObjects _ NARROW[gargoyleData.hitTest.sensitiveTriggerBag]; gargoyleData.drag.currentPoint _ worldPt; [resultPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, activeObjects, sensitiveObjects, gargoyleData]; GGWindow.Painter[$BitcopyBackground, gargoyleData]; -- show all but caret SetCaretAttractor[gargoyleData, resultPoint, feature]; -- move caret to feature point GGWindow.Painter[$PaintDragOverlay, gargoyleData]; -- show caret in new position }; -- end of DuringCaretPos EndCaretPos: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; activeObjects: ObjectBag; sensitiveObjects: TriggerBag; <> <> activeObjects _ NARROW[gargoyleData.hitTest.activeObjectBag]; sensitiveObjects _ NARROW[gargoyleData.hitTest.sensitiveTriggerBag]; [resultPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, activeObjects, sensitiveObjects, gargoyleData]; <> SetCaretAttractor[gargoyleData, resultPoint, feature]; -- move caret to feature point GGCaret.DoNotSit[gargoyleData.caret]; <> GGError.PutF[oneLiner, "Final caret on %g.", [rope[DescribeFeature[feature, gargoyleData]]]]; GGRefresh.MoveOverlayToBackground[gargoyleData]; -- caret is back in background GGWindow.Painter[$MergeCaret, gargoyleData]; -- caret in proper place. }; SitOnFeature: PROC [caret: Caret, feature: FeatureData] = { IF feature = NIL THEN { GGCaret.DoNotSit[caret]; RETURN; }; SELECT feature.resultType FROM joint => GGCaret.SitOnJoint[caret, feature.tseq.traj, feature.jointNum]; segment => GGCaret.SitOnSegment[caret, feature.tseq.traj, feature.segNum]; ENDCASE => GGCaret.DoNotSit[caret]; }; SetCaretAttractorEndpoint: PROC [gargoyleData: GargoyleData, mapPoint: Point, feature: FeatureData] = { IF feature = NIL THEN GGCaret.SetAttractor[gargoyleData, mapPoint, NIL] ELSE SELECT feature.resultType FROM joint => GGCaret.SetAttractor[gargoyleData, mapPoint, feature.tseq.traj, TRUE, feature.jointNum]; segment => { jointNum: NAT; jointPos: Point; jointNum _ NearestJoint[feature, mapPoint]; jointPos _ GGObjects.FetchJointPos[feature.tseq.traj, jointNum]; GGCaret.SetAttractor[gargoyleData, jointPos, feature.tseq.traj, TRUE, jointNum]; }; ENDCASE => GGCaret.SetAttractor[gargoyleData, mapPoint, NIL]; GGError.PutF[oneLiner, "Caret on %g.", [rope[DescribeFeature[feature, gargoyleData]]]]; }; SetCaretAttractor: PROC [gargoyleData: GargoyleData, mapPoint: Point, feature: FeatureData] = { <> IF feature = NIL THEN GGCaret.SetAttractor[gargoyleData, mapPoint, NIL] ELSE SELECT feature.resultType FROM joint => GGCaret.SetAttractor[gargoyleData, mapPoint, feature.tseq.traj, TRUE, feature.jointNum]; segment => GGCaret.SetAttractor[gargoyleData, mapPoint, feature.tseq.traj, FALSE,,feature.segNum]; ENDCASE => GGCaret.SetAttractor[gargoyleData, mapPoint, NIL]; GGError.PutF[oneLiner, "Caret on %g.", [rope[DescribeFeature[feature, gargoyleData]]]]; }; <<>> <> CopySelected: PUBLIC StartProc = { <> <> outSeqGen: GGSelect.OutlineSequenceGenerator; newTraj: Traj; newOutline, outline: Outline; <> <> <> <> IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; GGWindow.SaveCaretPos[gargoyleData]; SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur outSeqGen _ GGSelect.SelectedOutlineSequences[gargoyleData, normal]; FOR outSeq: GGSelect.OutlineSequence _ GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen] UNTIL outSeq = NIL DO IF outSeq.fenceSeq # NIL THEN { IF GGSequence.NextSegment[GGSequence.SegmentsInSequence[outSeq.fenceSeq]]=NIL THEN { <<-- no segments, only joints selected>> GGError.Append[". . . Cannot Copy Joints", oneLiner]; GGError.Blink[]; RETURN[FALSE]; }; outline _ GGObjects.OutlineOfTraj[outSeq.fenceSeq.traj]; IF GGSelect.IsSelectedInFull[outline, gargoyleData, normal] THEN { newOutline _ GGObjects.CopyOutline[outline]; GGObjects.AddOutline[gargoyleData.scene, newOutline, -1]; LOOP; } ELSE { newTraj _ GGObjects.CopyTrajFromSequence[outSeq.fenceSeq]; newOutline _ GGObjects.CreateOutline[traj: newTraj, lineEnds: outline.lineEnds, fillColor: outline.fillColor]; GGObjects.AddOutline[gargoyleData.scene, newOutline, -1]; }; }; FOR holeSeq: Sequence _ GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO outline _ GGObjects.OutlineOfTraj[holeSeq.traj]; newTraj _ GGObjects.CopyTrajFromSequence[holeSeq]; newOutline _ GGObjects.CreateOutline[traj: newTraj, lineEnds: outline.lineEnds, fillColor: outline.fillColor]; GGObjects.AddOutline[gargoyleData.scene, newOutline, -1]; ENDLOOP; ENDLOOP; [] _ StartDrag[NIL, gargoyleData, worldPt]; }; StartSelectJoint: PUBLIC StartProc = { <> <> gargoyleData.drag.selectState _ joint; <> <> GGAlign.SetBagsForAction[gargoyleData, $Select]; <> StartSelectAux[gargoyleData, worldPt, NIL]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectSegment: PUBLIC StartProc = { <> <> gargoyleData.drag.selectState _ segment; <> <> GGAlign.SetBagsForAction[gargoyleData, $Select]; <> StartSelectAux[gargoyleData, worldPt, NIL]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectTrajectory: PUBLIC StartProc = { <> <> gargoyleData.drag.selectState _ traj; <> <> GGAlign.SetBagsForAction[gargoyleData, $Select]; <> StartSelectAux[gargoyleData, worldPt, NIL]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectTopLevel: PUBLIC StartProc = { <> <> gargoyleData.drag.selectState _ topLevel; <> <> GGAlign.SetBagsForAction[gargoyleData, $SelectTopLevel]; <> StartSelectAux[gargoyleData, worldPt, NIL]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectAux: PROC [gargoyleData: GargoyleData, worldPt: Point, startBox: BoundBox] = { <> IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; <> GGWindow.SaveCaretPos[gargoyleData]; SaveSavedState[gargoyleData]; <> GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay <> GGRefresh.StoreBackground[gargoyleData, GGBoundBox.emptyBoundBox]; --all but caret is bkgnd }; DuringSelect: PUBLIC MouseProc = { <> resultPoint: Point; feature: FeatureData; activeObjects: ObjectBag; sensitiveObjects: TriggerBag; <> GGWindow.Painter[$BitcopyBackground, gargoyleData]; -- show all but caret <> activeObjects _ NARROW[gargoyleData.hitTest.activeObjectBag]; sensitiveObjects _ NARROW[gargoyleData.hitTest.sensitiveTriggerBag]; gargoyleData.drag.currentPoint _ worldPt; IF gargoyleData.drag.selectState = joint THEN [resultPoint, feature] _ GGGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, activeObjects, sensitiveObjects] ELSE [resultPoint, feature] _ GGGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, activeObjects, sensitiveObjects]; <> SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature]; <> <> GGSelect.DeselectAll[gargoyleData, normal]; IF feature = NIL THEN { -- no near trajectories, caret in free space } ELSE { SELECT gargoyleData.drag.selectState FROM joint => DuringSelectJointFeedback[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; segment => DuringSelectSegmentFeedback[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; traj => DuringSelectTrajFeedback[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; topLevel => DuringSelectTopLevelFeedback[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; ENDCASE => ERROR; }; <> GGWindow.Painter[$PaintSelectionFeedback, gargoyleData]; -- show caret in new position }; DuringSelectJointFeedback: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { <> SELECT feature.resultType FROM joint => { jointSeq: Sequence _ GGSequence.CreateJointToJoint[feature.tseq.traj, feature.jointNum, feature.jointNum]; GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; <> }; controlPoint => { controlPointSeq: Sequence _ GGSequence.CreateFromControlPoint[feature.tseq.traj, feature.segNum, feature.controlPointNum]; GGSelect.SelectSequence[controlPointSeq, gargoyleData, normal]; <> }; segment => { -- we are in the middle of a segment jointSeq: Sequence; jointNum: NAT; jointNum _ NearestJoint[feature, caretPt]; jointSeq _ GGSequence.CreateJointToJoint[feature.tseq.traj, jointNum, jointNum]; GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; <> }; cluster => { parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, joint]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; }; slopeLine, distanceLine, intersectionPoint, midpoint, radiiCircle => ERROR; <> ENDCASE => ERROR NotYetImplemented; }; -- end of DuringSelectJointFeedback DuringSelectSegmentFeedback: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { <> SELECT feature.resultType FROM joint => { -- joints are ambiguous if they are not end joints. Do nothing. }; segment, controlPoint => { -- we are in the middle of a segment. Select it. seq: Sequence _ GGSequence.CreateJointToJoint[feature.tseq.traj, feature.segNum, GGObjects.FollowingJoint[feature.tseq.traj, feature.segNum]]; GGSelect.SelectSequence[seq, gargoyleData, normal]; <> }; cluster => { parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, segment]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; }; slopeLine, distanceLine, intersectionPoint, midpoint, radiiCircle => ERROR; <> ENDCASE => ERROR NotYetImplemented; }; -- end of DuringSelectSegmentFeedback DuringSelectTrajFeedback: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { <> SELECT feature.resultType FROM joint, controlPoint, segment => { -- we are in the middle of a segment. Select its traj. seq: Sequence _ GGSequence.CreateComplete[feature.tseq.traj]; GGSelect.SelectSequence[seq, gargoyleData, normal]; <> }; cluster => { parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, traj]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; }; slopeLine, distanceLine, intersectionPoint, midpoint, radiiCircle => ERROR; <> ENDCASE => ERROR NotYetImplemented; }; DuringSelectTopLevelFeedback: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { <> SELECT feature.resultType FROM joint, controlPoint, segment => { GGSelect.SelectOutline[feature.tseq.traj.parent, gargoyleData, normal]; <> }; cluster => { parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, topLevel]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; }; slopeLine, distanceLine, intersectionPoint, midpoint, radiiCircle => ERROR; <> ENDCASE => ERROR NotYetImplemented; }; EndSelect: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; activeObjects: ObjectBag; sensitiveObjects: TriggerBag; <> activeObjects _ NARROW[gargoyleData.hitTest.activeObjectBag]; sensitiveObjects _ NARROW[gargoyleData.hitTest.sensitiveTriggerBag]; IF gargoyleData.drag.selectState = joint THEN [resultPoint, feature] _ GGGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, activeObjects, sensitiveObjects] ELSE [resultPoint, feature] _ GGGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, activeObjects, sensitiveObjects]; <> GGRefresh.MoveOverlayToBackground[gargoyleData]; GGSelect.DeselectAll[gargoyleData, normal]; <> SELECT gargoyleData.drag.selectState FROM joint => EndSelectJoint[gargoyleData, resultPoint, feature]; segment => EndSelectSegment[gargoyleData, resultPoint, feature]; traj => EndSelectTrajectory[gargoyleData, resultPoint, feature]; topLevel => EndSelectTopLevel[gargoyleData, resultPoint, feature]; ENDCASE => ERROR; }; EndSelectJoint: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { jointSeq, controlPointSeq: Sequence; jointNum: NAT; <> IF feature # NIL AND (feature.resultType = joint OR feature.resultType = segment) THEN { jointNum _ NearestJoint[feature, resultPoint]; GGCaret.SitOnJoint[gargoyleData.caret, feature.tseq.traj, jointNum]; } ELSE GGCaret.DoNotSit[gargoyleData.caret]; <> IF feature = NIL THEN { -- no preparation needed GGError.Append["No near joint found.", oneLiner]; } ELSE { SELECT feature.resultType FROM joint, segment => { jointSeq _ GGSequence.CreateJointToJoint[feature.tseq.traj, jointNum, jointNum]; GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; IF GGObjects.IsEndJoint[feature.tseq.traj, feature.jointNum] THEN GGError.Append["End ", begin]; -- an end joint GGError.PutF[end, "Joint %g selected", [integer[feature.jointNum]] ]; <> gargoyleData.drag.extendMode _ joint; gargoyleData.drag.trajToExtend _ feature.tseq.traj; }; controlPoint => { controlPointSeq _ GGSequence.CreateFromControlPoint[feature.tseq.traj, feature.segNum, feature.controlPointNum]; GGSelect.SelectSequence[controlPointSeq, gargoyleData, normal]; GGError.PutF[oneLiner, "Control point %g selected", [integer[feature.controlPointNum]] ]; gargoyleData.drag.extendMode _ controlPoint; gargoyleData.drag.trajToExtend _ feature.tseq.traj; }; cluster => { <> parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, joint]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; GGError.Append["Cluster selected", oneLiner]; gargoyleData.drag.extendMode _ joint; }; ENDCASE => ERROR UnexpectedType; }; GGWindow.Painter[$MergeBkgndAndSelected, gargoyleData]; }; EndSelectSegment: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { jointNum: NAT; seq: Sequence; <> IF feature # NIL AND (feature.resultType = joint OR feature.resultType = segment) THEN { jointNum _ NearestJoint[feature, resultPoint]; GGCaret.SitOnJoint[gargoyleData.caret, feature.tseq.traj, jointNum]; } ELSE GGCaret.DoNotSit[gargoyleData.caret]; <> IF feature = NIL THEN { -- no preparation needed GGError.Append["No near segment found.", oneLiner]; } ELSE { SELECT feature.resultType FROM joint => {}; -- actually if it is the last joint of a trajectory, this isn't ambiguous, but for now do nothing. segment, controlPoint => { seq _ GGSequence.CreateFromSegment[feature.tseq.traj, feature.segNum]; GGSelect.SelectSequence[seq, gargoyleData, normal]; GGError.PutF[oneLiner, "Segment %g selected", [integer[feature.segNum]] ]; gargoyleData.drag.extendMode _ segment; gargoyleData.drag.trajToExtend _ feature.tseq.traj; gargoyleData.drag.segToExtendNum _ feature.segNum; }; cluster => { parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, segment]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; GGError.Append["Cluster selected", oneLiner]; gargoyleData.drag.extendMode _ segment; }; ENDCASE => ERROR UnexpectedType; -- nothing else should be in the object bag }; GGWindow.Painter[$MergeBkgndAndSelected, gargoyleData]; }; EndSelectTrajectory: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { GGCaret.DoNotSit[gargoyleData.caret]; IF feature = NIL THEN { GGError.Append["No near trajectory found.", oneLiner]; } ELSE { SELECT feature.type FROM sequence => { jointNum: NAT _ NearestJoint[feature, resultPoint]; <> GGCaret.SitOnJoint[gargoyleData.caret, feature.tseq.traj, jointNum]; GGSelect.SelectTraj[feature.tseq.traj, gargoyleData, normal]; GGError.Append["Trajectory Selected", oneLiner]; gargoyleData.drag.extendMode _ traj; }; cluster => { parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, traj]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; GGError.Append["Cluster selected", oneLiner]; gargoyleData.drag.extendMode _ traj; }; ENDCASE => ERROR UnexpectedType; }; GGWindow.Painter[$MergeBkgndAndSelected, gargoyleData]; }; -- end SelectTrajectory EndSelectTopLevel: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { GGCaret.DoNotSit[gargoyleData.caret]; -- Don't prepare for a subsequent Add operation. <> IF feature = NIL THEN { -- no selection GGError.Append["No near object found.", oneLiner]; } ELSE { SELECT feature.resultType FROM joint, segment, controlPoint => { GGSelect.SelectOutline[feature.tseq.traj.parent, gargoyleData, normal]; GGError.Append["Top level trajectory selected.", oneLiner]; gargoyleData.drag.extendMode _ topLevel; }; cluster => { parts: ClusterParts _ feature.cluster.class.endSelect[feature.cluster, topLevel]; GGSelect.SelectCluster[feature.cluster, parts, gargoyleData, normal]; GGError.Append["Cluster selected", oneLiner]; gargoyleData.drag.extendMode _ topLevel; }; ENDCASE => ERROR; -- nothing else should be in the object bag }; GGWindow.Painter[$MergeBkgndAndSelected, gargoyleData]; }; -- end SelectTopLevel UpdateSelectedAfterMove: PROC [gargoyleData: GargoyleData] = { entityGen: EntityGenerator; GGTouch.InitializeTouching[gargoyleData]; entityGen _ GGSelect.SelectedEntities[gargoyleData, normal]; FOR entity: REF ANY _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM selSeq: Sequence => { GGObjects.TransformSequence[selSeq, gargoyleData.drag.transform]; GGTouch.SequenceMoved[selSeq, gargoyleData]; }; outline: Outline => { FOR trajs: LIST OF Traj _ outline.children, trajs.rest UNTIL trajs = NIL DO GGObjects.TransformTraj[trajs.first, gargoyleData.drag.transform]; GGTouch.TrajMoved[trajs.first, gargoyleData]; ENDLOOP; }; clusD: ClusterDescriptor => { clusD.cluster.class.transform[clusD.cluster, gargoyleData.drag.transform]; }; ENDCASE => ERROR NotYetImplemented; ENDLOOP; }; <> StartDrag: PUBLIC StartProc = { <> <> <> restoreBox: BoundBox; mapPoint: Point; feature: FeatureData; activeObjects: ObjectBag; sensitiveObjects: TriggerBag; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; GGWindow.SaveCaretPos[gargoyleData]; SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur IF GGSelect.NoSelections[gargoyleData, normal] THEN { GGError.Append["Select some objects to drag", oneLiner]; GGError.Blink[]; RETURN[FALSE]; }; restoreBox _ GGCaret.BoundBoxOfCaret[caret: gargoyleData.caret, gargoyleData: gargoyleData]; -- must remember OLD caret position to give to StoreBackground below <> gargoyleData.drag.currentPoint _ worldPt; GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay GGRefresh.MoveAllSelectedToOverlay[gargoyleData, normal]; -- selected objects on overlay <<>> <> GGAlign.SetBagsForAction[gargoyleData, $DragStartUp]; <