GGMouseEventImplB.mesa
Last edited by Pier on November 21, 1986 3:10:19 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, January 27, 1987 1:07:13 am PST
Kurlander July 17, 1986 2:40:45 pm PDT
DIRECTORY
GGBasicTypes, GGCaret, GGDescribe, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGObjects, GGOutline, GGRefresh, GGSegmentTypes, GGSelect, GGSequence, GGTraj, GGUtility, GGVector, GGWindow, ImagerTransformation, InputFocus, Menus, Rope;
GGMouseEventImplB: CEDAR PROGRAM
IMPORTS GGCaret, GGDescribe, GGError, GGGravity, GGMouseEvent, GGMultiGravity, GGObjects, GGOutline, GGRefresh, GGSelect, GGSequence, GGTraj, GGVector, GGWindow, ImagerTransformation, InputFocus
EXPORTS GGMouseEvent = BEGIN
AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint;
BoundBox: TYPE = GGModelTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
MouseButton: TYPE = Menus.MouseButton;
MouseProc: TYPE = GGMouseEvent.MouseProc;
ObjectBag: TYPE = GGInterfaceTypes.ObjectBag;
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;
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] = GGError.Problem;
SaveSavedState: PROC [gargoyleData: GargoyleData] = {
GGObjects.SaveSelections[gargoyleData.scene];
GGWindow.SaveCaretPos[gargoyleData];
GGCaret.Copy[from: gargoyleData.caret, to: gargoyleData.drag.savedCaret];
};
DescribeSelectionAction: PUBLIC PROC [gargoyleData: GargoyleData, feature: FeatureData, selectMode: SelectMode, action: Rope.ROPE] = {
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[action]], [rope[GGDescribe.DescribeFeature[feature, gargoyleData]]]];
};
SetCaretAttractorEndpoint: PUBLIC PROC [gargoyleData: GargoyleData, mapPoint: Point, feature: FeatureData] = {
IF feature=NIL THEN GGCaret.SetAttractor[gargoyleData.caret, mapPoint, NIL] ELSE {
resultType: ResultFeatureType ← feature.resultType;
shape: REF ANY ← feature.shape;
SELECT resultType FROM
outline => {
traj: Traj;
hitType: GGModelTypes.TrajPartType;
segNum, cpNum, jointNum: INT;
hitPoint, thisPoint: Point;
jointSeq: Sequence;
jointParts: SliceParts;
jointD: OutlineDescriptor;
outlineD: OutlineDescriptor ← NARROW[shape];
[traj, hitType, segNum, cpNum, jointNum, hitPoint] ← GGOutline.UnpackHitData[feature.hitPart];
SELECT hitType FROM
joint => {
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
thisPoint ← mapPoint;
};
controlPoint => {
jointSeq ← GGSequence.CreateFromControlPoint[traj, segNum, cpNum];
thisPoint ← mapPoint;
};
segment => {
success: BOOL;
jointPoint, cpPoint: Point;
seg: Segment ← GGTraj.FetchSegment[traj, segNum];
[jointNum, ----] ← GGOutline.NearestJointToHitData[feature.hitPart];
jointPoint ← GGTraj.FetchJointPos[traj, jointNum];
[cpPoint, cpNum, success] ← seg.class.closestControlPoint[seg, mapPoint, GGUtility.plusInfinity];
IF NOT success THEN { -- its a joint for sure
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
thisPoint ← jointPoint;
}
ELSE { -- could be a cp instead of a joint
cpDist: REAL ← GGVector.DistanceSquared[cpPoint, mapPoint];
jointDist: REAL ← GGVector.DistanceSquared[jointPoint, mapPoint];
tisAJoint: BOOL ← jointDist <= cpDist;
IF tisAJoint
THEN {
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
thisPoint ← jointPoint;
}
ELSE {
jointSeq ← GGSequence.CreateFromControlPoint[traj, segNum, cpNum];
thisPoint ← cpPoint;
};
};
};
ENDCASE => ERROR;
jointParts ← GGOutline.PartsFromSequence[outlineD.slice, jointSeq];
jointD ← NEW[OutlineDescriptorObj ← [outlineD.slice, jointParts]];
GGCaret.SetAttractor[gargoyleData.caret, thisPoint, jointD];
};
slice => {
pos: Point;
sliceD: SliceDescriptor ← NARROW[shape];
IF sliceD.slice.class.type = $Outline THEN ERROR Problem[msg: "Outlines are Slices"];
[pos, ----, ----, ----] ← sliceD.slice.class.closestPoint[sliceD, mapPoint, GGUtility.plusInfinity];
GGCaret.SetAttractor[gargoyleData.caret, pos, sliceD];
};
ENDCASE => GGCaret.SetAttractor[gargoyleData.caret, mapPoint, NIL];
};
};
Deselection Procs
StartDeselectJoint: PUBLIC StartProc = {
Use the StrictDistance gravity function. Find the nearest segment to the cursor. Snap the caret to the nearest endpoint of that segment. Feedback during the operation will be to deselect hightlight the joint which the caret is snapped to. The final result is to deselect a single joint selection. If the final position of the cursor is too far from any line, do nothing.
Refresh strategy: the selections are moved to the overlay, then DuringDeselect the hit joints are deselected. Since the selection is on the overlay, it's joints will be highlighted, preserving selection feedback, except for the transiently deselected joint.
gargoyleData.drag.selectState ← joint;
GGAlign.SetBagsForAction[gargoyleData, $Select];
Common Deselect Operations.
StartDeselectAux[gargoyleData, worldPt, NIL];
DuringDeselect[NIL, gargoyleData, worldPt];
};
StartDeselectSegment: PUBLIC StartProc = {
Use the StrictDistance gravity function. Find the nearest segment to the cursor. Snap the caret to the nearest endpoint of that segment. Feedback during the operation will be to select hightlight the endjoints of the segment which the caret is snapped to. The final result is to deselect a single segment selection. If the final position of the cursor is too far from any line, do nothing.
Refresh strategy: the selections are moved to the overlay, then DuringDeselect the hit segments are deselected. Since the selection is on the overlay, it's segments will be highlighted, preserving selection feedback, except for the transiently deselected segments.
gargoyleData.drag.selectState ← segment;
Common Deselect Operations.
StartDeselectAux[gargoyleData, worldPt, NIL];
DuringDeselect[NIL, gargoyleData, worldPt];
};
StartDeselectTrajectory: PUBLIC StartProc = {
Use the StrictDistance gravity function. Find the nearest trajectory to the cursor. Snap the caret to the nearest endjoint of that trajectory. Feedback during the operation will be to select hightlight the endjoints of the trajectory which the caret is snapped to. The final result is to deselect a single trajectory selection. If the final position of the cursor is too far from any line, do nothing.
Refresh strategy: the selections are moved to the overlay, then DuringDeselect the hit trajectories are deselected. Since the selection is on the overlay, it's trajectories will be highlighted, preserving selection feedback, except for the transiently deselected trajectories.
gargoyleData.drag.selectState ← traj;
GGAlign.SetBagsForAction[gargoyleData, $Select];
Common Deselect Operations.
StartDeselectAux[gargoyleData, worldPt, NIL];
DuringDeselect[NIL, gargoyleData, worldPt];
};
StartDeselectTopLevel: PUBLIC StartProc = {
gargoyleData.drag.selectState ← topLevel;
GGAlign.SetBagsForAction[gargoyleData, $SelectTopLevel];
Common Deselect Operations.
StartDeselectAux[gargoyleData, worldPt, NIL];
DuringDeselect[NIL, gargoyleData, worldPt];
};
StartDeselectAux: PROC [gargoyleData: GargoyleData, worldPt: Point, startBox: BoundBox] = {
Check overlay invariant and grab input focus.
IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay
[] ← InputFocus.SetInputFocus[gargoyleData.actionArea];
Measure caret coordinates.
SaveSavedState[gargoyleData];
gargoyleData.drag.currentPoint ← worldPt; -- this line moved from individual Start code
gargoyleData.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.
resultPoint: Point;
feature: FeatureData;
Use StrictDistance gravity except for DeselectJoint.
gargoyleData.drag.currentPoint ← worldPt;
IF gargoyleData.drag.selectState = joint THEN
[resultPoint, feature] ← GGMultiGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE]
ELSE [resultPoint, feature] ← GGMultiGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData];
Put Caret on a joint (if any).
SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature];
DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.selectState, "Deselecting"];
Reselect all.
GGObjects.RestoreSelections[gargoyleData.scene];
IF feature = NIL THEN { -- no near trajectories, do nothing
}
ELSE {
DuringDeselectAux[feature, resultPoint, gargoyleData.scene, gargoyleData.drag.selectState];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end DuringDeselect
DuringDeselectAux: PROC [feature: FeatureData, caretPt: Point, scene: Scene, mode: GGModelTypes.SelectMode] = {
SELECT feature.resultType FROM
outline => {
[] ← OutlineDeselectAux[feature, mode, scene];
};
slice => {
[] ← SliceDeselectAux[feature, mode, scene];
};
midpoint => NULL; -- temporarily ignore midpoints in bags. KAP. June 26, 1986
slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR;
ENDCASE => ERROR;
};
SliceDeselectAux: PROC [feature: FeatureData, mode: GGModelTypes.SelectMode, scene: Scene] RETURNS [deselectedParts: SliceParts] = {
sliceD: SliceDescriptor ← GGSelect.FindSelectedSlice[NARROW[feature.shape, SliceDescriptor].slice, scene, normal];
IF sliceD#NIL THEN {
class: SliceClass ← sliceD.slice.class;
newP: SliceParts ← class.newParts[sliceD.slice, feature.hitPart, mode];
diffP: SliceParts ← class.differenceParts[sliceD.slice, sliceD.parts, newP];
GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal];
IF NOT class.emptyParts[sliceD.slice, diffP] THEN GGSelect.SelectSlice[sliceD.slice, diffP, scene, normal];
deselectedParts ← newP;
};
};
OutlineDeselectAux: PROC [feature: FeatureData, mode: GGModelTypes.SelectMode, scene: Scene] RETURNS [deselectedParts: SliceParts] = {
sliceD: OutlineDescriptor ← GGSelect.FindSelectedOutline[NARROW[feature.shape, OutlineDescriptor].slice, scene, normal];
IF sliceD#NIL THEN {
class: GGModelTypes.OutlineClass ← sliceD.slice.class;
newP: SliceParts ← class.newParts[sliceD.slice, feature.hitPart, mode];
diffP: SliceParts ← class.differenceParts[sliceD.slice, sliceD.parts, newP];
GGSelect.DeselectOutline[sliceD.slice, sliceD.parts, scene, normal];
IF NOT class.emptyParts[sliceD.slice, diffP] THEN GGSelect.SelectOutline[sliceD.slice, diffP, scene, normal];
deselectedParts ← newP;
};
};
EndDeselect: PUBLIC MouseProc = {
resultPoint: Point;
feature: FeatureData;
IF gargoyleData.drag.selectState = joint THEN
[resultPoint, feature] ← GGMultiGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE]
ELSE [resultPoint, feature] ← GGMultiGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData];
Put Caret on a joint (if any).
SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature];
GGWindow.NewCaretPos[gargoyleData];
Clear the overlay plane.
GGRefresh.MoveOverlayToBackground[gargoyleData];
And Dispatch to the proper EndDeselect handler.
SELECT gargoyleData.drag.selectState FROM
joint => EndDeselectJoint[gargoyleData, resultPoint, feature];
segment => EndDeselectSegment[gargoyleData, resultPoint, feature];
traj => EndDeselectTrajectory[gargoyleData, resultPoint, feature];
topLevel => EndDeselectTopLevel[gargoyleData, resultPoint, feature];
ENDCASE => ERROR;
gargoyleData.drag.selectState ← none; -- added to help DescribeFeature work. KAP.
};
EndDeselectJoint: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
GGCaret.SitOn[gargoyleData.caret, NIL];
IF feature = NIL THEN {
GGError.AppendHerald[gargoyleData.feedback, "No near joint found.", oneLiner];
}
ELSE {
SELECT feature.resultType FROM
outline => {
gone: SliceParts ← OutlineDeselectAux[feature, joint, gargoyleData.scene];
sliceD: OutlineDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
slice => {
gone: SliceParts ← SliceDeselectAux[feature, joint, gargoyleData.scene];
sliceD: SliceDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
ENDCASE => SIGNAL Problem [msg: "Unexpected feature type for deselect joint."];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndDeselectJoint
EndDeselectSegment: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
GGCaret.SitOn[gargoyleData.caret, NIL];
IF feature = NIL THEN { -- do nothing
GGError.AppendHerald[gargoyleData.feedback, "No near segment found.", oneLiner];
}
ELSE {
SELECT feature.resultType FROM
outline => {
gone: SliceParts ← OutlineDeselectAux[feature, segment, gargoyleData.scene];
sliceD: OutlineDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
slice => {
gone: SliceParts ← SliceDeselectAux[feature, segment, gargoyleData.scene];
sliceD: SliceDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
ENDCASE => SIGNAL Problem [msg: "Unexpected feature type for deselect segment."];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndDeselectSegment
EndDeselectTrajectory: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
GGCaret.SitOn[gargoyleData.caret, NIL];
IF feature = NIL THEN {
GGError.AppendHerald[gargoyleData.feedback, "No near trajectory found.", oneLiner];
}
ELSE {
SELECT feature.type FROM
outline => {
gone: SliceParts ← OutlineDeselectAux[feature, traj, gargoyleData.scene];
sliceD: OutlineDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
slice => {
gone: SliceParts ← SliceDeselectAux[feature, traj, gargoyleData.scene];
sliceD: SliceDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
ENDCASE => SIGNAL Problem [msg: "Unexpected feature type for deselect trajectory."];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndDeselectTrajectory
EndDeselectTopLevel: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
GGCaret.SitOn[gargoyleData.caret, NIL];
IF feature = NIL THEN {
GGError.AppendHerald[gargoyleData.feedback, "No near object found.", oneLiner];
}
ELSE {
SELECT feature.resultType FROM
outline => {
gone: SliceParts ← OutlineDeselectAux[feature, topLevel, gargoyleData.scene];
sliceD: OutlineDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
slice => {
gone: SliceParts ← SliceDeselectAux[feature, topLevel, gargoyleData.scene];
sliceD: SliceDescriptor ← NARROW[feature.shape];
IF gone#NIL THEN GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g %g", [rope[sliceD.slice.class.describe[sliceD.slice, gone]]], [rope[" deselected"]]];
};
ENDCASE => SIGNAL Problem [msg: "Unexpected feature type for deselect top level."];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
Extend selection procs.
StartExtendSelectJoint: PUBLIC StartProc = {
gargoyleData.drag.extendMode ← joint;
RETURN[StartExtendSelection[input, gargoyleData, worldPt]];
};
StartExtendSelectSegment: PUBLIC StartProc = {
gargoyleData.drag.extendMode ← segment;
RETURN[StartExtendSelection[input, gargoyleData, worldPt]];
};
StartExtendSelectTraj: PUBLIC StartProc = {
gargoyleData.drag.extendMode ← traj;
RETURN[StartExtendSelection[input, gargoyleData, worldPt]];
};
StartExtendSelectTopLevel: PUBLIC StartProc = {
gargoyleData.drag.extendMode ← topLevel;
RETURN[StartExtendSelection[input, gargoyleData, worldPt]];
};
StartExtendSelection: PUBLIC StartProc = {
IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR;
[] ← InputFocus.SetInputFocus[gargoyleData.actionArea];
SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur
gargoyleData.drag.currentPoint ← worldPt;
gargoyleData.drag.transform ← ImagerTransformation.Scale[1.0];
DuringExtendSelection[NIL, gargoyleData, worldPt];
};
DuringExtendSelection: PUBLIC MouseProc= {
resultPoint: Point;
feature: FeatureData;
IF gargoyleData.drag.extendMode = joint THEN
[resultPoint, feature] ← GGMultiGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE]
ELSE [resultPoint, feature] ← GGMultiGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData];
Put Caret on a joint (if any).
SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature];
DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.extendMode, "Extending to"];
Reselect all.
GGSelect.DeselectAll[gargoyleData.scene, normal]; -- MUST CLEAR OUT TRANSIENT SELECTIONS!
GGObjects.RestoreSelections[gargoyleData.scene];
IF feature = NIL THEN { -- no near trajectories, do nothing
}
ELSE {
SELECT gargoyleData.drag.extendMode FROM
joint, segment, segmentRange, traj, topLevel => DuringExtendSelectionFeedback[feature, resultPoint, gargoyleData];
outline, slice, none => NULL; -- NoOp
ENDCASE => ERROR;
};
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end DuringExtendSelection
DuringExtendSelectionFeedback: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = {
depending on extend mode, check if feature is in that mode, then select the new feature.
SELECT gargoyleData.drag.extendMode FROM
joint => GGMouseEvent.SelectJointOrCP[feature, caretPt, gargoyleData];
segment => GGMouseEvent.SelectSegment[feature, caretPt, gargoyleData];
segmentRange => {
IF feature.resultType = outline THEN {
traj, trajToExtend: Traj;
hitType: GGModelTypes.TrajPartType;
segNum, cpNum, jointNum, segToExtendNum: INT;
hitPoint: Point;
[traj, hitType, segNum, cpNum, jointNum, hitPoint] ← GGOutline.UnpackHitData[feature.hitPart];
IF (hitType = segment OR hitType = controlPoint) THEN {
[trajToExtend, segToExtendNum] ← GGOutline.UnpackOneSegmentDescriptorOld[gargoyleData.drag.outlineToExtend];
IF trajToExtend = traj THEN { -- we're extending segments in the same trajectory
seq: Sequence ← GGSequence.CreateFromSegments[traj, segToExtendNum, segNum];
[] ← GGSelect.SelectSequence[seq, gargoyleData.scene, normal]; -- select current hit
}
ELSE GOTO RegularSelectMechanism;
}
ELSE GOTO RegularSelectMechanism;
}
ELSE GOTO RegularSelectMechanism;
EXITS
RegularSelectMechanism => GGMouseEvent.SelectSegment[feature, caretPt, gargoyleData];
};
traj => GGMouseEvent.SelectTraj[feature: feature, caretPt: caretPt, gargoyleData: gargoyleData];
topLevel => GGMouseEvent.SelectTopLevel[feature: feature, caretPt: caretPt, gargoyleData: gargoyleData];
ENDCASE => ERROR; -- should have been weeded about before calling this Proc
};
EndExtendSelection: PUBLIC MouseProc = {
GGCaret.DoNotSit[gargoyleData.caret]; -- simple but not always correct thing to do
simply paint new selections directly
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndExtendSelection
END.