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