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.
ROPE ←
WITH 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.
StartExtend
SelectJoint:
PUBLIC StartProc = {
ggData.drag.extendMode ← joint;
RETURN[StartExtendSelection[input, ggData, worldPt]];
};
StartExtend
SelectSegment:
PUBLIC StartProc = {
ggData.drag.extendMode ← segment;
RETURN[StartExtendSelection[input, ggData, worldPt]];
};
StartExtend
SelectTraj:
PUBLIC StartProc = {
ggData.drag.extendMode ← traj;
RETURN[StartExtendSelection[input, ggData, worldPt]];
};
StartExtend
SelectTopLevel:
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
};
End
ExtendSelection:
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.