GGMouseEventImplB.mesa
Last edited by Pier on March 31, 1987 4:58:13 pm PST
Contents: Once a mouse event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Bier, April 16, 1987 11:23:36 am PDT
Kurlander July 17, 1986 2:40:45 pm PDT
DIRECTORY
Basics, Feedback, GGAlign, GGBasicTypes, GGCaret, GGDescribe, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGRefresh, GGScene, GGSegmentTypes, GGSelect, GGSequence, GGState, GGUtility, GGWindow, GList, ImagerTransformation, InputFocus, IO, Menus, Rope;
GGMouseEventImplB: CEDAR PROGRAM
IMPORTS Feedback, GGAlign, GGCaret, GGDescribe, GGMouseEvent, GGMultiGravity, GGScene, GGOutline, GGRefresh, GGSelect, GGSequence, GGState, GGWindow, ImagerTransformation, InputFocus, IO, Rope
EXPORTS GGMouseEvent = BEGIN
AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint;
BoundBox: TYPE = GGModelTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
GGData: TYPE = GGInterfaceTypes.GGData;
Joint: TYPE = GGModelTypes.Joint;
MouseButton: TYPE = Menus.MouseButton;
MouseProc: TYPE = GGMouseEvent.MouseProc;
AlignBag: TYPE = GGInterfaceTypes.AlignBag;
Outline: TYPE = GGModelTypes.Outline;
OutlineDescriptor: TYPE = REF OutlineDescriptorObj;
OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj;
Point: TYPE = GGBasicTypes.Point;
ResultFeatureType: TYPE = GGModelTypes.ResultFeatureType;
Scene: TYPE = GGModelTypes.Scene;
SelectMode: TYPE = GGModelTypes.SelectMode;
Segment: TYPE = GGSegmentTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceClass: TYPE = GGModelTypes.SliceClass;
SliceParts: TYPE = GGModelTypes.SliceParts;
StartProc: TYPE = GGMouseEvent.StartProc;
TouchGroup: TYPE = GGSegmentTypes.TouchGroup;
Traj: TYPE = GGModelTypes.Traj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = GGModelTypes.TrajGenerator;
Vector: TYPE = GGBasicTypes.Vector;
Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
SaveSavedState: PROC [ggData: GGData] = {
GGScene.SaveSelections[ggData.scene];
GGWindow.SaveCaretPos[ggData];
GGCaret.Copy[from: ggData.caret, to: ggData.drag.savedCaret];
};
DescribeSelectionAction: PUBLIC PROC [ggData: GGData, feature: FeatureData, hitData: REF ANY, selectMode: SelectMode, action: Rope.ROPE] = {
Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [rope[action]], [rope[GGDescribe.DescribeFeature[feature, hitData, ggData]]]];
};
SetCaretAttractorEndpoint: PUBLIC PROC [ggData: GGData, mapPoint: Point, feature: FeatureData, hitData: REF ANY] = {
IF feature=NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL] ELSE {
shape: REF ANY ← feature.shape;
SELECT feature.type FROM
outline => {
pos: Point;
sliceD: OutlineDescriptor ← NARROW[shape];
jointD: OutlineDescriptor;
[jointD, pos] ← sliceD.slice.class.closestJointToHitData[sliceD, mapPoint, hitData];
GGCaret.SetAttractor[ggData.caret, pos, jointD];
};
slice => {
pos: Point;
sliceD: SliceDescriptor ← NARROW[shape];
jointD: SliceDescriptor;
[jointD, pos] ← sliceD.slice.class.closestJointToHitData[sliceD, mapPoint, hitData];
GGCaret.SetAttractor[ggData.caret, pos, jointD];
};
ENDCASE => GGCaret.SetAttractor[ggData.caret, mapPoint, NIL];
};
};
Selection Procs
StartSelectJoint: PUBLIC StartProc = {
ggData.drag.selectState ← joint;
StartSelectAux[ggData, worldPt];
DuringSelect[NIL, ggData, worldPt];
};
StartSelectSegment: PUBLIC StartProc = {
ggData.drag.selectState ← segment;
StartSelectAux[ggData, worldPt];
DuringSelect[NIL, ggData, worldPt];
};
StartSelectTrajectory: PUBLIC StartProc = {
ggData.drag.selectState ← traj;
StartSelectAux[ggData, worldPt];
DuringSelect[NIL, ggData, worldPt];
};
StartSelectTopLevel: PUBLIC StartProc = {
ggData.drag.selectState ← topLevel;
StartSelectAux[ggData, worldPt];
DuringSelect[NIL, ggData, worldPt];
};
StartSelectAux: PROC [ggData: GGData, worldPt: Point] = {
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR;
SaveSavedState[ggData];
};
DuringSelect: PUBLIC MouseProc = {
While a joint, segment, traj, or top level object is being selected, gravity is forced to be StrictDistance. 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.
opRope: Rope.ROPE = "Selecting ";
resultPoint: Point;
feature: FeatureData;
hitData: REF ANY;
IF GGState.GetSelectMode[ggData] = joint THEN
[resultPoint, feature, hitData] ← GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.criticalR, ggData.hitTest.innerR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]
ELSE [resultPoint, feature, hitData] ← GGMultiGravity.StrictDistance[worldPt, ggData.hitTest.criticalR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, feature, hitData];
GGSelect.DeselectAll[ggData.scene, normal];
IF feature#NIL THEN SelectAndDescribeSlicePart[feature, hitData, ggData, ggData.drag.selectState, opRope] ELSE DescribeOperationOnNothing[ggData, opRope];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end DuringSelect
EndSelect: PUBLIC MouseProc = {
resultPoint: Point;
feature: FeatureData;
hitData: REF ANY;
Use StrictDistance gravity except for SelectJoint.
IF ggData.drag.selectState = joint THEN
[resultPoint, feature, hitData] ← GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.criticalR, ggData.hitTest.innerR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]
ELSE [resultPoint, feature, hitData] ← GGMultiGravity.StrictDistance[worldPt, ggData.hitTest.criticalR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
Put Caret on a joint (if any).
GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, feature, hitData];
Use the commented code for more explicit messages.
GGMouseEvent.DescribeSelectionAction[ggData, feature, ggData.drag.selectState, "Selected"];
GGWindow.NewCaretPos[ggData];
Deselect all.
GGSelect.DeselectAll[ggData.scene, normal];
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;
ggData.drag.selectState ← none; -- added to help DescribeFeature work. KAP.
}; -- end EndSelect
EndSelectAux: PROC [ggData: GGData, resultPoint: Point, feature: FeatureData, hitData: REF ANY, mode: SelectMode, opName: Rope.ROPE] = {
Prepare for a subsequent Add operation and for Extend.
IF feature = NIL THEN {
GGCaret.SitOn[ggData.caret, NIL];
Feedback.PutFHerald[ggData.feedback, oneLiner, "No near %g found.", [rope[opName]]];
}
ELSE {
Make Selection and Prepare for Extend.
SELECT mode FROM
joint => ggData.drag.extendMode ← joint;
segment => ggData.drag.extendMode ← segmentRange;
traj => ggData.drag.extendMode ← traj;
topLevel => ggData.drag.extendMode ← topLevel;
ENDCASE => ERROR;
SELECT feature.type FROM
outline => {
outlineD: OutlineDescriptor ← NARROW[SelectSlicePart[feature, hitData, ggData, mode]];
SitTheCaret[ggData.caret, outlineD, hitData, mode];
IF mode = topLevel THEN Feedback.AppendHerald[ggData.feedback, "Top level outline selected.", oneLiner]
ELSE Feedback.AppendHerald[ggData.feedback, Rope.Concat[outlineD.slice.class.describe[outlineD], " selected"], oneLiner];
ggData.drag.outlineToExtend ← outlineD;
ggData.drag.sliceToExtend ← NIL;
};
slice => {
sliceD: SliceDescriptor ← NARROW[SelectSlicePart[feature, hitData, ggData, mode]];
IF sliceD.slice.class.type = $Outline THEN {
ERROR Problem[msg: "Outlines must be Slices now."];
}
ELSE {
IF mode = traj OR mode = topLevel THEN GGCaret.SitOn[ggData.caret, NIL]
ELSE GGCaret.SitOn[ggData.caret, sliceD];
};
Feedback.AppendHerald[ggData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD], " selected"], oneLiner];
ggData.drag.outlineToExtend ← NIL;
ggData.drag.sliceToExtend ← sliceD;
};
ENDCASE => ERROR Problem[msg: "Unexpected feature type"];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndSelectAux
SelectAndDescribeSlicePart: PUBLIC PROC [feature: FeatureData, hitData: REF ANY, ggData: GGData, mode: SelectMode, opRope: Rope.ROPE] = {
selectedD: REF ANY ← SelectSlicePart[feature, hitData, ggData, mode];
description: Rope.ROPEWITH selectedD SELECT FROM
outlineD: OutlineDescriptor => outlineD.slice.class.describe[outlineD],
sliceD: SliceDescriptor => sliceD.slice.class.describe[sliceD],
ENDCASE => GGDescribe.DescribeFeature[feature, hitData, ggData];
Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [rope[opRope]], [rope[description]] ];
};
SelectSlicePart: PUBLIC PROC [feature: FeatureData, hitData: REF ANY, ggData: GGData, mode: SelectMode] RETURNS [selectedD: REF ANY] = {
Find out which point is being selected and highlight it.
SELECT feature.type FROM
outline => {
outline: Outline ← NARROW[feature.shape, OutlineDescriptor].slice;
selectedOutlineD: OutlineDescriptor;
selectedOutlineD ← outline.class.newParts[outline, hitData, mode];
GGSelect.SelectOutline[selectedOutlineD, ggData.scene, normal];
selectedD ← selectedOutlineD;
};
slice => {
slice: Slice ← NARROW[feature.shape, SliceDescriptor].slice;
selectedSliceD: SliceDescriptor;
selectedSliceD ← slice.class.newParts[slice, hitData, mode];
GGSelect.SelectSlice[selectedSliceD, ggData.scene, normal];
selectedD ← selectedSliceD;
};
slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR;
ENDCASE => ERROR Problem[msg: "unimplemented result type"];
};
DescribeOperationOnNothing: PUBLIC PROC [ggData: GGData, opRope: Rope.ROPE] = {
Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [rope[opRope]], [rope["nothing"]] ];
};
SitTheCaret: PROC [caret: Caret, outlineD: OutlineDescriptor, hitData: REF ANY, mode: SelectMode] = {
SELECT mode FROM
joint => GGCaret.SitOn[caret, outlineD];
segment, traj, topLevel => {
jointParts: SliceParts;
jointSeq: Sequence;
jointD: OutlineDescriptor;
jointNum: NAT;
traj: Traj;
IF outlineD.slice.class.type = $Outline THEN {
[jointNum, traj] ← GGOutline.NearestJointToHitData[hitData];
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
jointParts ← GGOutline.PartsFromSequence[outlineD.slice, jointSeq];
jointD ← GGOutline.DescriptorFromParts[outlineD.slice, jointParts];
GGCaret.SitOn[caret, jointD];
}
ELSE GGCaret.SitOn[caret, NIL];
};
ENDCASE => ERROR;
};
Deselection Procs
StartDeselectJoint: PUBLIC StartProc = {
ggData.drag.selectState ← joint;
StartDeselectAux[ggData, worldPt, NIL];
DuringDeselect[NIL, ggData, worldPt];
};
StartDeselectSegment: PUBLIC StartProc = {
ggData.drag.selectState ← segment;
StartDeselectAux[ggData, worldPt, NIL];
DuringDeselect[NIL, ggData, worldPt];
};
StartDeselectTrajectory: PUBLIC StartProc = {
ggData.drag.selectState ← traj;
StartDeselectAux[ggData, worldPt, NIL];
DuringDeselect[NIL, ggData, worldPt];
};
StartDeselectTopLevel: PUBLIC StartProc = {
ggData.drag.selectState ← topLevel;
StartDeselectAux[ggData, worldPt, NIL];
DuringDeselect[NIL, ggData, worldPt];
};
StartDeselectAux: PROC [ggData: GGData, worldPt: Point, startBox: BoundBox] = {
Check overlay invariant and grab input focus.
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay
[] ← InputFocus.SetInputFocus[ggData.actionArea];
Measure caret coordinates.
SaveSavedState[ggData];
ggData.drag.currentPoint ← worldPt; -- this line moved from individual Start code
ggData.drag.transform ← ImagerTransformation.Scale[1.0]; -- needed for DuringDeselect to work properly
};
DuringDeselect: PUBLIC MouseProc = {
While a joint, cp, segment, traj, or top level object is being deselected, gravity is forced to be StrictDistance. 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.
opRope: Rope.ROPE = "Deselecting ";
resultPoint: Point;
feature: FeatureData;
hitData: REF ANY;
Use StrictDistance gravity except for DeselectJoint.
ggData.drag.currentPoint ← worldPt;
IF ggData.drag.selectState = joint THEN
[resultPoint, feature, hitData] ← GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.criticalR, ggData.hitTest.innerR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]
ELSE [resultPoint, feature, hitData] ← GGMultiGravity.StrictDistance[worldPt, ggData.hitTest.criticalR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
Reselect all.
GGScene.RestoreSelections[ggData.scene];
IF feature#NIL THEN DuringDeselectAux[feature, hitData, resultPoint, ggData, ggData.drag.selectState] ELSE DescribeOperationOnNothing[ggData, opRope];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end DuringDeselect
DuringDeselectAux: PROC [feature: FeatureData, hitData: REF ANY, caretPt: Point, ggData: GGData, mode: GGModelTypes.SelectMode] = {
description: Rope.ROPE;
opRope: Rope.ROPE = "Deselecting ";
SELECT feature.type FROM
outline => {
deselectedD: OutlineDescriptor ← OutlineDeselectAux[feature, hitData, mode, ggData.scene];
description ← IF deselectedD=NIL THEN "" ELSE deselectedD.slice.class.describe[deselectedD];
Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [rope[opRope]], [rope[description]] ];
};
slice => {
deselectedD: SliceDescriptor ← SliceDeselectAux[feature, hitData, mode, ggData.scene];
description ← IF deselectedD=NIL THEN "" ELSE deselectedD.slice.class.describe[deselectedD];
Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [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] = {
sliceD: SliceDescriptor ← GGSelect.FindSelectedSlice[NARROW[feature.shape, SliceDescriptor].slice, scene, normal];
IF sliceD#NIL THEN {
class: SliceClass ← sliceD.slice.class;
newD: SliceDescriptor ← class.newParts[sliceD.slice, hitData, mode];
diffD: SliceDescriptor ← class.differenceParts[sliceD, newD];
GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal];
IF NOT class.emptyParts[diffD] THEN GGSelect.SelectSlice[diffD, scene, normal];
deselectedD ← newD;
};
};
OutlineDeselectAux: PROC [feature: FeatureData, hitData: REF ANY, mode: GGModelTypes.SelectMode, scene: Scene] RETURNS [deselectedD: OutlineDescriptor] = {
sliceD: OutlineDescriptor ← GGSelect.FindSelectedOutline[NARROW[feature.shape, OutlineDescriptor].slice, scene, normal];
IF sliceD#NIL THEN {
class: GGModelTypes.OutlineClass ← sliceD.slice.class;
newD: OutlineDescriptor ← class.newParts[sliceD.slice, hitData, mode];
diffD: OutlineDescriptor ← class.differenceParts[sliceD, newD];
GGSelect.DeselectOutline[sliceD.slice, sliceD.parts, scene, normal];
IF NOT class.emptyParts[diffD] THEN GGSelect.SelectOutline[diffD, scene, normal];
deselectedD ← newD;
};
};
EndDeselect: PUBLIC MouseProc = {
resultPoint: Point;
feature: FeatureData;
hitData: REF ANY;
IF ggData.drag.selectState = joint THEN
[resultPoint, feature, hitData] ← GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.criticalR, ggData.hitTest.innerR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]
ELSE [resultPoint, feature, hitData] ← GGMultiGravity.StrictDistance[worldPt, ggData.hitTest.criticalR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
Put Caret on a joint (if any).
SetCaretAttractorEndpoint[ggData, resultPoint, feature];
GGWindow.NewCaretPos[ggData]; -- why not leave the caret alone for deselect? Bier, March 31, 1987
Clear the overlay plane.
GGRefresh.MoveOverlayToBackground[ggData];
And Dispatch to the proper EndDeselect handler.
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;
ggData.drag.selectState ← none; -- added to help DescribeFeature work. KAP.
};
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.PutFHerald[ggData.feedback, oneLiner, "No near %g found.", [rope[selectRope]]];
}
ELSE {
SELECT feature.type FROM
outline => {
gone: OutlineDescriptor ← OutlineDeselectAux[feature, hitData, mode, ggData.scene];
sliceD: OutlineDescriptor ← NARROW[feature.shape];
IF gone#NIL AND gone.parts#NIL THEN Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[gone]]], [rope[" deselected"]]];
};
slice => {
gone: SliceDescriptor ← SliceDeselectAux[feature, hitData, mode, ggData.scene];
sliceD: SliceDescriptor ← NARROW[feature.shape];
IF gone#NIL AND gone.parts#NIL THEN Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[gone]]], [rope[" deselected"]]];
};
ENDCASE => SIGNAL Problem [msg: IO.PutFR["Unexpected feature type for deselect %g.", [rope[selectRope]]]];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndDeselectJoint
Extend selection procs.
StartExtendSelectJoint: PUBLIC StartProc = {
ggData.drag.extendMode ← joint;
RETURN[StartExtendSelection[input, ggData, worldPt]];
};
StartExtendSelectSegment: PUBLIC StartProc = {
ggData.drag.extendMode ← segment;
RETURN[StartExtendSelection[input, ggData, worldPt]];
};
StartExtendSelectTraj: PUBLIC StartProc = {
ggData.drag.extendMode ← traj;
RETURN[StartExtendSelection[input, ggData, worldPt]];
};
StartExtendSelectTopLevel: PUBLIC StartProc = {
ggData.drag.extendMode ← topLevel;
RETURN[StartExtendSelection[input, ggData, worldPt]];
};
StartExtendSelection: PUBLIC StartProc = {
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR;
SaveSavedState[ggData]; -- must do this before any possible aborts occur
ggData.drag.currentPoint ← worldPt;
ggData.drag.transform ← ImagerTransformation.Scale[1.0];
DuringExtendSelection[NIL, ggData, worldPt];
};
DuringExtendSelection: PUBLIC MouseProc= {
opRope: Rope.ROPE = "Extending to ";
resultPoint: Point;
feature: FeatureData;
hitData: REF ANY;
scene: Scene ← ggData.scene;
IF GGState.GetExtendMode[ggData] = joint THEN
[resultPoint, feature, hitData] ← GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.criticalR, ggData.hitTest.innerR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]
ELSE [resultPoint, feature, hitData] ← GGMultiGravity.StrictDistance[worldPt, ggData.hitTest.criticalR, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
Put Caret on a joint (if any).
SetCaretAttractorEndpoint[ggData, resultPoint, feature, hitData];
Reselect all.
GGSelect.DeselectAll[scene, normal];
GGScene.RestoreSelections[scene];
IF feature#NIL THEN {
SELECT ggData.drag.extendMode FROM
joint, segment, segmentRange, traj, topLevel => DuringExtendSelectionFeedback[feature, hitData, resultPoint, ggData];
slice, none => NULL; -- NoOp
ENDCASE => ERROR;
}
ELSE DescribeOperationOnNothing[ggData, opRope];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end DuringExtendSelection
DuringExtendSelectionFeedback: PROC [feature: FeatureData, hitData: REF ANY, caretPt: Point, ggData: GGData] = {
depending on extend mode, check if feature is in that mode, then select the new feature.
opRope: Rope.ROPE ← "Extending to ";
SELECT ggData.drag.extendMode FROM
joint, segment, traj, topLevel => [] ← SelectAndDescribeSlicePart[feature, hitData, ggData, ggData.drag.extendMode, opRope];
segmentRange => {
IF feature.type = outline THEN {
traj, trajToExtend: Traj;
hitType: GGModelTypes.TrajPartType;
segNum, cpNum, jointNum, segToExtendNum: INT;
hitPoint: Point;
[traj, hitType, segNum, cpNum, jointNum, hitPoint] ← GGOutline.UnpackHitData[hitData];
IF (hitType = segment OR hitType = controlPoint) THEN {
[trajToExtend, segToExtendNum] ← GGOutline.UnpackOneSegmentDescriptorOld[ggData.drag.outlineToExtend];
IF trajToExtend = traj THEN { -- we're extending segments in the same trajectory
seq: Sequence ← GGSequence.CreateFromSegments[traj, segToExtendNum, segNum];
[] ← GGSelect.SelectSequence[seq, ggData.scene, normal]; -- select current hit
Feedback.PutFHerald[ggData.feedback, oneLiner, "%g %g", [rope[opRope]], [rope[GGDescribe.DescribeSegment[traj, segNum] ]] ];
}
ELSE GOTO RegularSelectMechanism;
}
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 = {
GGCaret.DoNotSit[ggData.caret]; -- simple but not always correct thing to do
simply paint new selections directly
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndExtendSelection
END.