GGMouseEventImplA.mesa
Last edited by Bier on May 13, 1987 5:07:17 pm PDT
Contents: Once a mouse event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Pier, May 14, 1987 3:51:28 pm PDT
Kurlander August 7, 1986 10:54:44 am PDT
DIRECTORY
Atom, Basics, CodeTimer, Feedback, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGDescribe, GGEvent, GGGravity, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGScene, GGOutline, GGRefresh, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GGTransform, GGWindow, GList, Imager, ImagerTransformation, InputFocus, Menus, RealFns, Rope, Vectors2d;
GGMouseEventImplA: CEDAR PROGRAM
IMPORTS Atom, Basics, CodeTimer, Feedback, GGAlign, GGBoundBox, GGCaret, GGDescribe, GGEvent, GGMouseEvent, GGMultiGravity, GGScene, GGOutline, GGRefresh, GGSegment, GGSelect, GGSequence, GGSlice, GGTraj, GGTransform, GGWindow, GList, ImagerTransformation, InputFocus, RealFns, Rope, Vectors2d
EXPORTS GGMouseEvent = BEGIN
AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint;
BoundBox: TYPE = GGModelTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
DefaultData: TYPE = GGModelTypes.DefaultData;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
FeatureData: TYPE = GGModelTypes.FeatureData;
FeedbackData: TYPE = Feedback.FeedbackData;
GGData: TYPE = GGInterfaceTypes.GGData;
Joint: TYPE = GGModelTypes.Joint;
MouseButton: TYPE = Menus.MouseButton;
AlignBag: TYPE = GGGravity.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;
Segment: TYPE = GGSegmentTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Slice: TYPE = GGModelTypes.Slice;
SliceParts: TYPE = GGModelTypes.SliceParts;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
Traj: TYPE = GGModelTypes.Traj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = GGScene.TrajGenerator;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TriggerBag: TYPE = GGAlign.TriggerBag;
Vector: TYPE = GGBasicTypes.Vector;
MouseProc: TYPE = GGMouseEvent.MouseProc;
PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point];
StartProc: TYPE = GGMouseEvent.StartProc;
PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] RETURNS [success: BOOL ← TRUE];
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
EasyAbort: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
Many actions don't change the scene at all until the End proc is called. For these cases, we just restore the selections and caret position.
scene: Scene ← ggData.scene;
GGScene.RestoreSelections[scene];
GGCaret.Copy[from: ggData.drag.savedCaret, to: ggData.caret]; --restore original caret
FinishAbort[ggData];
};
AbortAdd: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
GGEvent.DeleteCaretSegment[ggData, NIL];
GGScene.RestoreSelections[ggData.scene]; -- can't do this. Obsolete saved selection
{
sliceD: OutlineDescriptor ← NARROW[GGCaret.GetChair[ggData.caret]];
IF sliceD#NIL THEN GGSelect.SelectSlice[sliceD: sliceD, scene: ggData.scene, selectClass: normal];
FinishAbort[ggData];
};
};
AbortCopyAndDrag: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
GGEvent.Delete[NIL, ggData];
[] ← GGScene.DeleteAllSelected[ggData.scene];
GGScene.RestoreSelections[ggData.scene];
GGCaret.Copy[from: ggData.drag.savedCaret, to: ggData.caret];
FinishAbort[ggData];
};
AbortBox: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
FixupAbortedBox[ggData];
FinishAbort[ggData];
};
FinishAbort: PROC [ggData: GGData] = {
GGRefresh.MoveOverlayToBackground[ggData];
Feedback.AppendHerald[ggData.feedback, ". . . Aborted.", end];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
InitializeFSM: PUBLIC PROC [ggData: GGData] = {
ggData.state ← $None;
ggData.mouseMode ← $None;
};
ResetMouseMachinery: PUBLIC PROC [ggData: GGData] = {
ggData.mouseMode ← $None;
ggData.state ← $None;
GGRefresh.MoveOverlayToBackground[ggData];
};
SaveSavedState: PROC [ggData: GGData] = {
GGScene.SaveSelections[ggData.scene];
GGWindow.SaveCaretPos[ggData];
GGCaret.Copy[from: ggData.caret, to: ggData.drag.savedCaret];
};
The FSM
HandleMouseless: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = {
An easy way to handle the Abort action.
event ← LIST[event.first, NEW[Point ← [0.0, 0.0]]];
HandleMouse[clientData, event];
};
SortNewEntities: PROC [scene: Scene, entityList: LIST OF Slice] RETURNS [sorted: LIST OF Slice] = {
Sort back to front (in increasing order).
CompareProc: GList.CompareProc = {
[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]
priority1, priority2: INT;
WITH ref1 SELECT FROM
slice: Slice => priority1 ← GGScene.SlicePriority[scene, slice];
outline: Outline => priority1 ← GGScene.SlicePriority[scene, outline];
ENDCASE => ERROR;
WITH ref2 SELECT FROM
slice: Slice => priority2 ← GGScene.SlicePriority[scene, slice];
outline: Outline => priority2 ← GGScene.SlicePriority[scene, outline];
ENDCASE => ERROR;
RETURN[Basics.CompareInt[priority1, priority2]];
};
sorted ← NARROW[GList.Sort[entityList, CompareProc]];
};
HandleMouse: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = {
event will be in the format
LIST[ATOM, REF Point];
ggData: GGData ← NARROW[clientData];
atom: ATOMNARROW[event.first];
point: Point ← NARROW[event.rest.first, REF Point]^;
SELECT ggData.mouseMode FROM
$CaretPos => HandleGuarded[StartCaretPos, DuringCaretPos, EndCaretPos, EasyAbort, NIL, event, ggData, point];
$Box => HandleGuarded[StartBox, DuringDrag, EndBox, AbortBox, NIL, event, ggData, point];
$Add => HandleGuarded[StartAdd, DuringDrag, EndMotion, AbortAdd, ContinueAdd, event, ggData, point];
$Drag => HandleGuarded[StartDrag, DuringDrag, EndMotion, EasyAbort, NIL, event, ggData, point];
$CopyAndDrag => HandleGuarded[StartCopyAndDrag, DuringDrag, EndMotion, AbortCopyAndDrag, NIL, event, ggData, point];
$Rotate => HandleGuarded[StartRotate, DuringRotate, EndMotion, EasyAbort, NIL, event, ggData, point];
$Scale => HandleGuarded[StartScale, DuringScale, EndMotion, EasyAbort, NIL, event, ggData, point];
$SixPoint => HandleGuarded[StartSixPoint, DuringSixPoint, EndMotion, EasyAbort, NIL, event, ggData, point];
$SelectWithBox => HandleUnGuarded[StartSelectWithBox, DuringDrag, EndSelectWithBox, AbortBox, event, ggData, point];
$SelectJoint => HandleUnGuarded[GGMouseEvent.StartSelectJoint, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point];
$ExtSelectJoint => HandleGuarded[GGMouseEvent.StartExtendSelectJoint, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point];
$SelectSegment => HandleUnGuarded[GGMouseEvent.StartSelectSegment, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point];
$ExtSelectSegment => HandleGuarded[GGMouseEvent.StartExtendSelectSegment, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point];
$SelectTrajectory => HandleUnGuarded[GGMouseEvent.StartSelectTrajectory, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point];
$ExtSelectTrajectory => HandleGuarded[GGMouseEvent.StartExtendSelectTraj, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point];
$SelectTopLevel => HandleUnGuarded[GGMouseEvent.StartSelectTopLevel, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point];
$ExtSelectTopLevel => HandleGuarded[GGMouseEvent.StartExtendSelectTopLevel, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point];
$ExtendSelection => HandleUnGuarded[GGMouseEvent.StartExtendSelection, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, event, ggData, point];
$DeselectJoint => HandleGuarded[GGMouseEvent.StartDeselectJoint, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point];
$DeselectSegment => HandleGuarded[GGMouseEvent.StartDeselectSegment, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point];
$DeselectTrajectory => HandleGuarded[GGMouseEvent.StartDeselectTrajectory, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point];
$DeselectTopLevel => HandleGuarded[GGMouseEvent.StartDeselectTopLevel, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point];
$None => {
SELECT atom FROM
$StartCaretPos => {
ggData.mouseMode ← $CaretPos; HandleMouse[ggData, event];
};
$StartAdd => {
ggData.mouseMode ← $Add; HandleMouse[ggData, event];
};
$StartBox => {
ggData.mouseMode ← $Box; HandleMouse[ggData, event];
};
$StartSelectWithBox => {
ggData.mouseMode ← $SelectWithBox; HandleMouse[ggData, event];
};
$StartDrag => {
ggData.mouseMode ← $Drag; HandleMouse[ggData, event];
};
$StartCopyAndDrag => {
ggData.mouseMode ← $CopyAndDrag; HandleMouse[ggData, event];
};
$StartRotate => {
ggData.mouseMode ← $Rotate; HandleMouse[ggData, event];
};
$StartScale => {
ggData.mouseMode ← $Scale; HandleMouse[ggData, event];
};
$StartSixPoint => {
ggData.mouseMode ← $SixPoint; HandleMouse[ggData, event];
};
$StartSelectJoint => {
ggData.mouseMode ← $SelectJoint; HandleMouse[ggData, event];
};
$StartExtSelectJoint => {
ggData.mouseMode ← $ExtSelectJoint; HandleMouse[ggData, event];
};
$StartSelectSegment => {
ggData.mouseMode ← $SelectSegment; HandleMouse[ggData, event];
};
$StartExtSelectSegment => {
ggData.mouseMode ← $ExtSelectSegment; HandleMouse[ggData, event];
};
$StartSelectTrajectory => {
ggData.mouseMode ← $SelectTrajectory; HandleMouse[ggData, event];
};
$StartExtSelectTrajectory => {
ggData.mouseMode ← $ExtSelectTrajectory; HandleMouse[ggData, event];
};
$StartSelectTopLevel => {
ggData.mouseMode ← $SelectTopLevel; HandleMouse[ggData, event];
};
$StartExtSelectTopLevel => {
ggData.mouseMode ← $ExtSelectTopLevel; HandleMouse[ggData, event];
};
$StartExtendSelection => {
ggData.mouseMode ← $ExtendSelection; HandleMouse[ggData, event];
};
$StartDeselectJoint => {
ggData.mouseMode ← $DeselectJoint; HandleMouse[ggData, event];
};
$StartDeselectSegment => {
ggData.mouseMode ← $DeselectSegment; HandleMouse[ggData, event];
};
$StartDeselectTrajectory => {
ggData.mouseMode ← $DeselectTrajectory; HandleMouse[ggData, event];
};
$StartDeselectTopLevel => {
ggData.mouseMode ← $DeselectTopLevel; HandleMouse[ggData, event];
};
ENDCASE; -- ignore other actions
};
ENDCASE => SIGNAL Problem[msg: "Unimplemented Mode"];
};
HandleGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
genericAction, atom: ATOM;
atomName: Rope.ROPE;
CodeTimer.StartInt[$HandleGuarded, $Gargoyle];
WITH input.first SELECT FROM
refChar: REF CHAR => RETURN; -- ignore characters during mouse actions
ENDCASE;
atom ← NARROW[input.first];
atomName ← Atom.GetPName[atom];
genericAction ← IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom;
HandleGuardedAux[startProc, duringProc, endProc, abortProc, continueProc, genericAction, input, ggData, worldPt];
CodeTimer.StopInt[$HandleGuarded, $Gargoyle];
};
Restart: PROC [input: LIST OF REF ANY, ggData: GGData] RETURNS [BOOL] = {
mouseMode: ATOM ← ggData.mouseMode;
state: ATOMNARROW[input.first];
RETURN[
(mouseMode = $CaretPos AND state = $StartCaretPos) OR
(mouseMode = $Add AND state = $StartAdd) OR
(mouseMode = $Box AND state = $StartBox) OR
(mouseMode = $Circle AND state = $StartCircle) OR
(mouseMode = $Drag AND state = $StartDrag) OR
(mouseMode = $CopyAndDrag AND state = $StartCopyAndDrag) OR
(mouseMode = $Rotate AND state = $StartRotate) OR
(mouseMode = $Scale AND state = $StartScale) OR
(mouseMode = $SelectJoint AND state = $StartSelectJoint) OR
(mouseMode = $SelectSegment AND state = $StartSelectSegment) OR
(mouseMode = $SelectTrajectory AND state = $StartSelectTrajectory) OR
(mouseMode = $SelectTopLevel AND state = $StartSelectTopLevel) OR
(mouseMode = $ExtendSelection AND state = $StartExtendSelection) OR
(mouseMode = $DeselectJoint AND state = $StartDeselectJoint) OR
(mouseMode = $DeselectSegment AND state = $StartDeselectSegment) OR
(mouseMode = $DeselectTrajectory AND state = $StartDeselectTrajectory) OR
(mouseMode = $DeselectTopLevel AND state = $StartDeselectTopLevel)];
};
HandleGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, genericAction: ATOM, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
SELECT ggData.state FROM
$None => {
SELECT genericAction FROM
$Start => {
ggData.drag.currentPoint ← worldPt;
[] ← InputFocus.SetInputFocus[ggData.actionArea];
IF startProc[input, ggData, worldPt] THEN ggData.state ← $Main
ELSE {abortProc[input, ggData, worldPt]; ggData.state ← $Aborted;}; };
ENDCASE;
};
$Main => {
SELECT genericAction FROM
$During => {
duringProc[input, ggData, worldPt];
ggData.drag.currentPoint ← worldPt;
};
$SawMouseFinish, $Abort => {abortProc[input, ggData, worldPt]; ggData.state ← $Aborted};
$GuardUp => ggData.state ← $GuardUp;
$MouseUp => ggData.state ← $MouseUp;
$Start => { -- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one.
abortProc[input, ggData, worldPt]; ggData.state ← $None;
ggData.mouseMode ← $None;
HandleMouse[ggData, input];
};
ENDCASE;
};
$GuardUp => {
SELECT genericAction FROM
$AllUp => {
endProc[input, ggData, ggData.drag.currentPoint];
ggData.mouseMode ← $None;
ggData.state ← $None;
};
$SawMouseFinish, $Abort => {abortProc[input, ggData, worldPt]; ggData.state ← $Aborted};
ENDCASE;
};
$MouseUp => {
SELECT genericAction FROM
$SawMouseFinish, $AllUp => {
endProc[input, ggData, ggData.drag.currentPoint];
ggData.mouseMode ← $None;
ggData.state ← $None;
};
$Abort => {abortProc[input, ggData, worldPt]; ggData.state ← $Aborted};
$Start => { -- we may be starting another action of this mode or some other mode.
IF Restart[input, ggData] AND continueProc # NIL THEN {
IF continueProc[input, ggData, worldPt] THEN {
ggData.state ← $Main;
ggData.drag.currentPoint ← worldPt;
}
ELSE {abortProc[input, ggData, worldPt]; ggData.state ← $Aborted;};
}
ELSE {
ggData.mouseMode ← $None;
endProc[input, ggData, ggData.drag.currentPoint];
ggData.state ← $None;
HandleMouse[ggData, input];
};
};
ENDCASE;
};
$Aborted => {
SELECT genericAction FROM
$AllUp => {ggData.state ← $None; ggData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
HandleUnGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
genericAction, atom: ATOM;
atomName: Rope.ROPE;
WITH input.first SELECT FROM
refChar: REF CHAR => RETURN; -- ignore characters during mouse actions
ENDCASE;
atom ← NARROW[input.first];
atomName ← Atom.GetPName[atom];
genericAction ← IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom;
HandleUnGuardedAux[startProc, duringProc, endProc, abortProc, genericAction, input, ggData, worldPt];
};
HandleUnGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction: ATOM, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = {
SELECT ggData.state FROM
$None => {
SELECT genericAction FROM
$Start => {
[] ← InputFocus.SetInputFocus[ggData.actionArea];
IF startProc[input, ggData, worldPt] THEN ggData.state ← $Main
ELSE {abortProc[input, ggData, worldPt]; ggData.state ← $Aborted}
};
ENDCASE;
};
$Main => {
SELECT genericAction FROM
$During => duringProc[input, ggData, worldPt];
$MouseUp, $AllUp => {
endProc[input, ggData, worldPt];
ggData.state ← $None;
ggData.mouseMode ← $None;
};
$Abort => {abortProc[input, ggData, worldPt]; ggData.state ← $Aborted};
$Start => { -- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one.
abortProc[input, ggData, worldPt]; ggData.state ← $None;
ggData.mouseMode ← $None;
HandleMouse[ggData, input];
};
ENDCASE;
};
$Aborted => {
SELECT genericAction FROM
$AllUp => {ggData.state ← $None; ggData.mouseMode ← $None};
$MouseUp => {ggData.state ← $None; ggData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
Caret Procs
StartCaretPos: PUBLIC StartProc = {
The user wishes to place the caret without changing the current selection. Only hot objects trigger alignment lines.
CodeTimer.StartInt[$StartCaretPos, $Gargoyle];
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR;
SaveSavedState[ggData];
DuringCaretPos[NIL, ggData, worldPt];
CodeTimer.StopInt[$StartCaretPos, $Gargoyle];
};
DuringCaretPos: PUBLIC MouseProc = {
While the caret is being placed, the normal gravity scheme applies. The only feedback is the appearance of text strings and the position of the caret itself.
resultPoint: Point;
feature: FeatureData;
hitData: REF ANY;
CodeTimer.StartInt[$DuringCaretPos, $Gargoyle];
[resultPoint, feature, hitData] ← GGMultiGravity.Map[worldPt, ggData.hitTest.criticalR, ggData.hitTest.alignBag, ggData.hitTest.sceneBag, ggData, TRUE];
SetCaretAttractor[ggData, resultPoint, feature, hitData]; -- move caret to feature point
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringCaretPos, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; -- show caret in new position
CodeTimer.StopInt[$DuringCaretPos, $Gargoyle];
}; -- end of DuringCaretPos
EndCaretPos: PUBLIC MouseProc = {
resultPoint: Point;
feature: FeatureData;
hitData: REF ANY;
currentObjects: AlignBag ← NARROW[ggData.hitTest.alignBag];
sceneObjects: TriggerBag ← NARROW[ggData.hitTest.sceneBag];
CodeTimer.StartInt[$EndCaretPos, $Gargoyle];
[resultPoint, feature, hitData] ← GGMultiGravity.Map[worldPt, ggData.hitTest.criticalR, currentObjects, sceneObjects, ggData, TRUE];
Move caret to new position.
SetCaretAttractor[ggData, resultPoint, feature, hitData, "Final"]; -- move caret to feature point
GGWindow.NewCaretPos[ggData];
GGCaret.SitOn[ggData.caret, NIL]; -- subsequent Add operations will start a NEW trajectory.
GGWindow.RestoreScreenAndInvariants[paintAction: $CaretMoved, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$EndCaretPos, $Gargoyle];
}; -- end EndCaretPos
SitOnFeature: PROC [caret: Caret, feature: FeatureData] = {
IF feature = NIL THEN GGCaret.SitOn[caret, NIL]
ELSE { -- feature.shape type is OutlineD or SliceD
WITH feature.shape SELECT FROM
outlineD: OutlineDescriptor => GGCaret.SitOn[caret, outlineD];
sliceD: SliceDescriptor => GGCaret.SitOn[caret, sliceD];
ENDCASE => GGCaret.SitOn[caret, NIL];
};
};
SetCaretAttractor: PROC [ggData: GGData, mapPoint: Point, feature: FeatureData, hitData: REF ANY, action: Rope.ROPE ← ""] = {
Moves the caret and records the attractor which is a simple descriptor.
This code very similar to SetCaretAttractorEndpoint in GGMouseEventImplB.
Called by EndCaretPos and all the "During" procs.
IF feature = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL]
ELSE {
SELECT feature.type FROM
outline => {
partsD: OutlineDescriptor;
outlineD: OutlineDescriptor ← NARROW[feature.shape];
partsD ← outlineD.slice.class.newParts[outlineD.slice, hitData, literal];
GGCaret.SetAttractor[ggData.caret, mapPoint, partsD];
};
outline, slice => {
partsD: SliceDescriptor;
slideD: SliceDescriptor ← NARROW[feature.shape];
partsD ← slideD.slice.class.newParts[slideD.slice, hitData, literal];
GGCaret.SetAttractor[ggData.caret, mapPoint, partsD];
};
intersectionPoint => {
See GGMultiGravityImpl.FindIntersections to see where this was made.
alignPoint: AlignmentPoint ← NARROW[feature.shape];
curve1, curve2: FeatureData;
curve1 ← alignPoint.curve1;
curve2 ← alignPoint.curve2;
IF hitData = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL]
ELSE {
attractorD: SliceDescriptor;
IF curve1.type = outline OR curve1.type = slice THEN {
WITH curve1.shape SELECT FROM
outlineD: OutlineDescriptor => attractorD ← outlineD.slice.class.newParts[outlineD.slice, hitData, literal];
sliceD: SliceDescriptor => attractorD ← sliceD.slice.class.newParts[sliceD.slice, hitData, literal];
ENDCASE => ERROR;
}
ELSE IF curve2.type = outline OR curve2.type = slice THEN {
WITH curve2.shape SELECT FROM
outlineD: OutlineDescriptor => attractorD ← outlineD.slice.class.newParts[outlineD.slice, hitData, literal];
sliceD: SliceDescriptor => attractorD ← sliceD.slice.class.newParts[sliceD.slice, hitData, literal];
ENDCASE => ERROR;
}
ELSE ERROR;
GGCaret.SetAttractor[ggData.caret, mapPoint, attractorD]
};
};
midpoint => {
IF hitData = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL]
ELSE {
attractorD: SliceDescriptor;
alignPoint: AlignmentPoint ← NARROW[feature.shape];
curveFeature: FeatureData ← alignPoint.curve1;
WITH curveFeature.shape SELECT FROM
outlineD: OutlineDescriptor => attractorD ← outlineD.slice.class.newParts[outlineD.slice, hitData, literal];
sliceD: SliceDescriptor => attractorD ← sliceD.slice.class.newParts[sliceD.slice, hitData, literal];
ENDCASE => ERROR;
GGCaret.SetAttractor[ggData.caret, mapPoint, attractorD];
};
};
ENDCASE => GGCaret.SetAttractor[ggData.caret, mapPoint, NIL];
};
Feedback.PutFHerald[ggData.feedback, oneLiner, "%g Caret on %g at [%g, %g]",
[rope[action]], [rope[GGDescribe.DescribeFeature[feature, hitData, ggData]]],
[real[mapPoint.x]], [real[mapPoint.y]]
];
};
Copy and Drag
UpdateSceneForCopy: PROC [scene: Scene, feedback: FeedbackData] RETURNS [newSlices: LIST OF Slice, success: BOOLTRUE] = {
sliceDGen: SliceDescriptorGenerator;
outSeqGen: GGSelect.OutlineSequenceGenerator;
newOutline, outline: Outline;
newTraj: Traj;
newSlice: Slice;
Copy selected slices.
sliceDGen ← GGSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDGen], GGSelect.NextSliceDescriptor[sliceDGen] UNTIL sliceD = NIL DO
newSlice ← GGSlice.CopySlice[sliceD.slice]; -- make the new one
GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal];
newSlices ← CONS[newSlice, newSlices];
newSlice.priority ← GGScene.SlicePriority[scene, sliceD.slice];
ENDLOOP;
BEGIN
outSeqGen ← GGSelect.SelectedOutlineSequences[scene, normal];
FOR outSeq: GGSelect.OutlineSequence ← GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen] UNTIL outSeq = NIL DO
outline ← outSeq.outline;
IF outSeq.fenceSeq # NIL THEN {
IF NOT GGSequence.ContainsSomeSegment[outSeq.fenceSeq] THEN GOTO NoSegmentsSelected;
If the whole outline is selected, copy it as a whole.
IF GGSelect.IsSelectedInFull[outline, scene, normal] THEN {
newOutline ← outline.class.copy[outline];
GGSelect.DeselectEntireSlice[outline, scene, normal]; -- deselect the old one
newSlices ← CONS[newOutline, newSlices];
newOutline.priority ← outline.priority;
LOOP;
}
Otherwise, each piece becomes a new slice.
ELSE {
newTraj ← GGTraj.CopyTrajFromRun[outSeq.fenceSeq];
newOutline ← GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline]];
GGSelect.DeselectSequence[outSeq.fenceSeq, scene, normal];
newSlices ← CONS[newOutline, newSlices];
newOutline.priority ← outline.priority;
};
};
FOR holeSeq: Sequence ← GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO
IF NOT GGSequence.ContainsSomeSegment[holeSeq] THEN GOTO NoSegmentsSelected;
newTraj ← GGTraj.CopyTrajFromRun[holeSeq];
newOutline ← GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline]];
GGSelect.DeselectSequence[holeSeq, scene, normal];
newSlices ← CONS[newOutline, newSlices];
newOutline.priority ← outline.priority;
ENDLOOP;
ENDLOOP;
Sort the new slices
newSlices ← SortNewEntities[scene, newSlices];
Add all the new slices to the scene.
FOR sliceList: LIST OF Slice ← newSlices, sliceList.rest UNTIL sliceList = NIL DO
allParts: SliceDescriptor;
GGScene.AddSlice[scene, sliceList.first, -1];
allParts ← sliceList.first.class.newParts[sliceList.first, NIL, slice];
GGSelect.SelectSlice[allParts, scene, normal];
ENDLOOP;
EXITS
NoSegmentsSelected => {
Feedback.AppendHerald[feedback, ". . . Cannot Copy Joints or CPs", begin];
Feedback.Blink[feedback];
GGSelect.DeselectAll[scene, normal];
We are about to self-abort. We deselect all so that we don't delete any of the original geometry (see AbortCopyAndDrag above).
RETURN[NIL, FALSE];
};
END;
};
StartCopyAndDrag: PUBLIC StartProc = {
Create copies of all selected objects. The selected objects can be part of trajectories, trajectories, outlines, or whole slices. Sort the resulting slices by the priority ordering of the objects they came from. Add the new objects on top. This can be done in two ways: Sort the selections by priority order to begin with, or sort the new objects after they are created.
sliceList: LIST OF Slice;
scene: Scene ← ggData.scene;
SaveSavedState[ggData]; -- must do this before any possible aborts occur
[sliceList, success] ← UpdateSceneForCopy[scene, ggData.feedback]; -- adds new shapes and selects them
IF NOT success THEN RETURN;
GGAlign.UpdateBagsForNewSlices[sliceList, ggData];
success ← StartMotion[ggData: ggData, opName: "copy", bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE];
backgroundOK is TRUE because we are strictly adding.
IF NOT success THEN RETURN[FALSE];
DuringDrag[NIL, ggData, worldPt];
};
Motion Procs
TransformObjectsAfterMove: PROC [scene: Scene, transform: ImagerTransformation.Transformation] = {
sliceDescGen: SliceDescriptorGenerator ← GGSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO
sliceD.slice.class.transform[sliceD, transform];
ENDLOOP;
};
ContinueMotion: PROC [ggData: GGData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, startBox: BoundBox ← NIL] RETURNS [success: BOOLTRUE] = {
We begin with correct static bags. Add is done. Begin the drag.
movingBox: BoundBox;
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay
GGRefresh.MoveAllSelectedToOverlay[ggData, normal];
ggData.drag.startPoint ← GGCaret.GetPoint[ggData.caret];
Set Caret
GGCaret.SetAttractor[ggData.caret, ggData.drag.startPoint, NIL];
Set Transform
ggData.drag.transform ← ImagerTransformation.Scale[1.0];
Prepare Refresh
movingBox ← GGBoundBox.BoundBoxOfMoving[ggData.scene];
GGBoundBox.EnlargeByBox[bBox: movingBox, by: startBox];
ggData.refresh.startBoundBox^ ← movingBox^;
[] ← GGAlign.StaticToDynamicBags[ggData];
Bags are now correct and dynamic.
Prepare Foreground Plane
GGRefresh.UpdateForegroundForMotion[ggData];
};
StartMotion: PROC [ggData: GGData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, saveState: BOOLTRUE, needAnchor: BOOLFALSE, backgroundOK: BOOLFALSE] RETURNS [success: BOOLTRUE] = {
movingBox: BoundBox;
repaintNeeded: BOOL;
CodeTimer.StartInt[$StartMotion, $Gargoyle];
IF saveState THEN SaveSavedState[ggData]; -- must do this before any possible aborts occur
BEGIN
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay
IF GGSelect.NoSelections[ggData.scene, normal] THEN GOTO NoSelections;
IF needAnchor AND NOT GGCaret.Exists[ggData.anchor] THEN GOTO NoAnchor;
GGRefresh.MoveAllSelectedToOverlay[ggData, normal];
ggData.drag.startPoint ← GGCaret.GetPoint[ggData.caret];
Set Caret
GGCaret.NoAttractor[ggData.caret]; -- is this really needed? Bier, March 26, 1987
Set Transform
ggData.drag.transform ← ImagerTransformation.Scale[1.0];
Prepare Refresh
movingBox ← GGBoundBox.BoundBoxOfMoving[ggData.scene];
ggData.refresh.startBoundBox^ ← movingBox^;
IF NOT backgroundOK THEN GGRefresh.SplitBackgroundAndOverlay[ggData, movingBox];
repaintNeeded ← GGAlign.StaticToDynamicBags[ggData];
Prepare Bags
IF repaintNeeded THEN GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]
ELSE GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$StartMotion, $Gargoyle];
EXITS
NoSelections => {
Feedback.PutFHerald[ggData.feedback, oneLiner, "Select some objects to %g", [rope[opName]]];
Feedback.Blink[ggData.feedback];
success ← FALSE;
CodeTimer.StopInt[$StartMotion, $Gargoyle];
};
NoAnchor => {
Feedback.PutFHerald[ggData.feedback, oneLiner, "Anchor needed to %g", [rope[opName]]];
Feedback.Blink[ggData.feedback];
success ← FALSE;
CodeTimer.StopInt[$StartMotion, $Gargoyle];
};
END;
};
StartDrag: PUBLIC StartProc = {
startSuccess: BOOL ← StartMotion[ggData, "drag", $Drag, worldPt];
IF NOT startSuccess THEN RETURN[FALSE];
DuringDrag[NIL, ggData, worldPt];
};
StartRotate: PUBLIC StartProc = {
startSuccess: BOOL ← StartMotion[ggData, "rotate", $Drag, worldPt, TRUE, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
DuringRotate[NIL, ggData, worldPt];
};
StartScale: PUBLIC StartProc = {
anchorPoint: Point;
originalVector: Vector;
startSuccess: BOOL ← StartMotion[ggData, "scale", $Drag, worldPt, TRUE, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
anchorPoint ← GGCaret.GetPoint[ggData.anchor];
originalVector ← Vectors2d.Sub[ggData.drag.startPoint, anchorPoint];
IF originalVector = [0.0, 0.0] THEN {
Feedback.AppendHerald[ggData.feedback, "Move caret away from anchor before scaling.", oneLiner];
Feedback.Blink[ggData.feedback];
RETURN[FALSE];
};
DuringScale[NIL, ggData, worldPt];
};
StartSixPoint: PUBLIC StartProc = {
OPEN Vectors2d;
epsilon: REAL = 1.0e-3;
p0, p1, p2: Point;
crossProduct: REAL;
startSuccess: BOOL ← StartMotion[ggData, "six point", $Drag, worldPt, TRUE, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
p0 ← GGCaret.GetPoint[ggData.anchor];
p2 ← ggData.drag.startPoint;
p1 ← Add[p0, VectorPlusAngle[Sub[p2, p0], 90.0]];
crossProduct ← CrossProductScalar[Sub[p1,p0], Sub[p2, p0]];
IF ABS[crossProduct] < epsilon THEN {
Feedback.AppendHerald[ggData.feedback, "Move caret away from anchor before six point.", oneLiner];
Feedback.Blink[ggData.feedback];
RETURN[FALSE];
};
DuringSixPoint[NIL, ggData, worldPt];
};
DragTheCaret: PROC [worldPt: Point, ggData: GGData, opName: Rope.ROPE] RETURNS [mapPoint: Point] = {
feature: FeatureData;
hitData: REF ANY;
[mapPoint, feature, hitData] ← GGMultiGravity.Map[worldPt, ggData.hitTest.criticalR, ggData.hitTest.alignBag, ggData.hitTest.sceneBag, ggData, TRUE];
SetCaretAttractor[ggData, mapPoint, feature, hitData, opName];
};
DuringDrag: PUBLIC MouseProc = {
totalDragVector: Vector;
mapPoint: Point;
CodeTimer.StartInt[$DuringDrag, $Gargoyle];
mapPoint ← DragTheCaret[worldPt, ggData, "Dragging:"];
totalDragVector ← Vectors2d.Sub[mapPoint, ggData.drag.startPoint];
ggData.drag.transform ← ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$DuringDrag, $Gargoyle];
}; -- end DuringDrag
DuringRotate: PUBLIC MouseProc = {
originalVector, newVector: Vector;
mapPoint: Point;
degrees: REAL;
anchorPoint: Point ← GGCaret.GetPoint[ggData.anchor];
mapPoint ← DragTheCaret[worldPt, ggData, "Rotating:"];
originalVector ← Vectors2d.Sub[ggData.drag.startPoint, anchorPoint];
newVector ← Vectors2d.Sub[mapPoint, anchorPoint];
degrees ← Vectors2d.AngleCCWBetweenVectors[originalVector, newVector];
ggData.drag.transform ← GGTransform.RotateAboutPoint[anchorPoint, degrees];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringScale: PUBLIC MouseProc = {
epsilon: REAL = 1.0e-3;
originalVector, newVector: Vector;
mapPoint: Point;
ratio: REAL;
anchorPoint: Point ← GGCaret.GetPoint[ggData.anchor];
mapPoint ← DragTheCaret[worldPt, ggData, "Scaling:"];
originalVector ← Vectors2d.Sub[ggData.drag.startPoint, anchorPoint];
newVector ← Vectors2d.Sub[mapPoint, anchorPoint];
IF RealFns.AlmostZero[newVector.x, -10] AND RealFns.AlmostZero[newVector.y, -10] THEN RETURN; -- can't scale to zero
ratio ← Vectors2d.Magnitude[newVector]/Vectors2d.Magnitude[originalVector];
ggData.drag.transform ← GGTransform.ScaleAboutPoint[anchorPoint, ratio];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringSixPoint: PUBLIC MouseProc = {
OPEN Vectors2d;
epsilon: REAL = 1.0e-3;
pts: ARRAY [0..5] OF Point;
crossProduct: REAL;
mapPoint: Point;
pts[0] ← pts[3] ← GGCaret.GetPoint[ggData.anchor];
pts[2] ← ggData.drag.startPoint;
pts[1] ← pts[4] ← Add[pts[0], VectorPlusAngle[Sub[pts[2], pts[0]], 90.0]];
mapPoint ← DragTheCaret[worldPt, ggData, "Six Point Transform:"];
pts[5] ← mapPoint;
crossProduct ← CrossProductScalar[Sub[pts[4],pts[3]], Sub[pts[5],pts[3]]];
IF ABS[crossProduct] < epsilon THEN RETURN; -- illegal six point transform
ggData.drag.transform ← GGTransform.SixPoints[pts];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
ContinueAdd: PUBLIC StartProc = {
The dragging is done. Update the endpoint with the current transform. Then begin a new add operation.
CodeTimer.StartInt[$ContinueAdd, $Gargoyle];
TransformObjectsAfterMove[ggData.scene, ggData.drag.transform];
GGRefresh.MoveOverlayToBackground[ggData];
GGWindow.NewCaretPos[ggData];
[] ← GGAlign.DynamicToStaticBags[ggData];
We are done with the last add and the bags are correct and static.
CodeTimer.StopInt[$ContinueAdd, $Gargoyle];
success ← StartAdd[LIST[$ContinueAdd], ggData, worldPt];
};
EndMotion: PUBLIC MouseProc = {
The dragging is done. Update all of the drag entities with the total drag vector and repaint the entire scene.
CodeTimer.StartInt[$EndMotion, $Gargoyle];
TransformObjectsAfterMove[ggData.scene, ggData.drag.transform];
GGRefresh.MoveOverlayToBackground[ggData];
GGWindow.NewCaretPos[ggData];
[] ← GGAlign.DynamicToStaticBags[ggData];
GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedDragging, ggData: ggData, remake: bitMap, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$EndMotion, $Gargoyle];
};
Addition and Extension Procs
SafelyGetCaretTrajOLDD: PROC [caret: Caret] RETURNS [outlineD: OutlineDescriptor, partType: TrajPartType, traj: Traj, jointNum: NAT] = {
chair: REF ANY;
success: BOOL;
chair ← GGCaret.GetChair[caret];
IF ISTYPE[chair, SliceDescriptor] THEN ERROR Problem[msg: "Slices are Outlines now. Update this code."];
outlineD ← NARROW[chair];
[success, partType, traj, ----, jointNum] ← GGOutline.UnpackSimpleDescriptorOld[outlineD];
IF NOT success OR partType # joint THEN ERROR Problem[msg: "Attempt to extend a trajectory without the caret on its end"];
};
SafelyGetCaretTraj: PROC [caret: Caret] RETURNS [chair: SliceDescriptor, partType: TrajPartType, traj: Traj, jointNum: NAT] = {
success: BOOL ← FALSE;
chair ← GGCaret.GetChair[caret];
[success, partType, traj, ----, jointNum] ← GGOutline.UnpackSimpleDescriptor[chair];
IF NOT success OR partType # joint THEN ERROR Problem[msg: "Attempt to extend a trajectory without the caret on its end"];
};
UpdateSceneForAdd: PROC [scene: Scene, worldPt: Point, caret: Caret, defaults: DefaultData] RETURNS [oldTraj, newTraj: Traj, trajEnd: TrajEnd, newOutline: Outline] = {
caretPoint: Point;
jointNum: NAT;
newSeg, extendSeg: Segment;
chair: SliceDescriptor;
partType: TrajPartType;
success: BOOL;
caretPoint ← GGCaret.GetPoint[caret];
newSeg ← GGSegment.MakeLine[worldPt, caretPoint, NIL];
Extend the existing trajectory
IF GGCaret.SittingOnEnd[caret] THEN {
[chair, partType, newTraj, jointNum] ← SafelyGetCaretTraj[caret];
oldTraj ← newTraj;
trajEnd ← SELECT jointNum FROM
0 => lo,
GGTraj.HiJoint[newTraj] => hi,
ENDCASE => ERROR;
extendSeg ← GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]];
GGSegment.CopyLooks[extendSeg, newSeg];
success ← GGTraj.AddSegment[newTraj, trajEnd, newSeg, hi];
IF NOT success THEN RETURN;
newOutline ← chair.slice;
}
Create a new trajectory starting at the caret.
ELSE {
oldTraj ← NIL;
trajEnd ← hi;
newTraj ← GGTraj.CreateTraj[caretPoint];
GGTraj.SetTrajStrokeJoint[newTraj, defaults.strokeJoint];
GGSegment.SetDefaults[newSeg, defaults];
success ← GGTraj.AddSegment[newTraj, trajEnd, newSeg, hi];
IF NOT success THEN RETURN;
newOutline ← GGOutline.CreateOutline[newTraj, defaults.fillColor];
GGScene.AddOutline[scene, newOutline, -1];
};
};
UpdateSelectionsForAdd: PROC [scene: Scene, oldTraj, newTraj: Traj, trajEnd: TrajEnd] RETURNS [newNormal, newHot: Sequence] = {
jointNum: NAT;
jointNum ← SELECT trajEnd FROM
lo => 0,
hi => GGTraj.HiJoint[newTraj],
ENDCASE => ERROR;
IF oldTraj # NIL THEN {
The new outline should be hot in the same places as the old.
newHot ← GGSelect.ReselectTraj[newTraj, trajEnd, scene, TRUE];
newNormal ← GGSequence.CreateFromJoint[newTraj, jointNum];
}
ELSE {
newHot ← GGSequence.CreateEmpty[newTraj];
newNormal ← GGSequence.CreateFromJoint[newTraj, 1];
};
GGSelect.DeselectAll[scene, normal];
GGSelect.SelectSequence[newNormal, scene, normal];
};
UpdateCaretForAdd: PROC [caret: Caret, newOutline: Outline, newNormal: Sequence, worldPt: Point] = {
jointD: OutlineDescriptor;
GGCaret.SetAttractor[caret, worldPt, NIL];
jointD ← GGOutline.DescriptorFromSequence[newOutline, newNormal];
GGCaret.SitOn[caret, jointD];
};
StartAdd: PUBLIC StartProc = {
StartAdd must update the caret, the scene, the selections, and the bags. Then, it transfers control to StartDrag.
continue: BOOL ← FALSE;
caret: Caret ← ggData.caret;
scene: Scene ← ggData.scene;
oldTraj, newTraj: Traj;
newOutline, oldOutline: Outline;
trajEnd: TrajEnd;
newNormal, newHot: Sequence;
startBox: BoundBox;
CodeTimer.StartInt[$StartAdd, $Gargoyle];
continue ← NARROW[input.first, ATOM] = $ContinueAdd;
SaveSavedState[ggData];
[oldTraj, newTraj, trajEnd, newOutline] ← UpdateSceneForAdd[scene, worldPt, caret, ggData.defaults];
oldOutline ← IF oldTraj = NIL THEN NIL ELSE GGOutline.OutlineOfTraj[oldTraj];
[newNormal, newHot] ← UpdateSelectionsForAdd[scene, oldTraj, newTraj, trajEnd];
UpdateCaretForAdd[caret, newOutline, newNormal, worldPt];
[] ← GGAlign.UpdateBagsForAdd[oldOutline, newOutline, trajEnd, ggData];
We have added the NEW segment and the bags are correct and static.
IF continue THEN {
startBox ← ggData.refresh.startBoundBox;
success ← ContinueMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, startBox: startBox];
}
ELSE {
success ← StartMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE];
backgroundOK is TRUE even though this will leave some residue on the background, because we are adding, so the traj is open (not filled).
};
IF NOT success THEN RETURN[FALSE];
DuringDrag[NIL, ggData, worldPt];
CodeTimer.StopInt[$StartAdd, $Gargoyle];
}; -- end StartAdd
AddNewBoxSlice: PROC [from, to: Point, ggData: GGData] RETURNS [sliceD: SliceDescriptor] = {
completeSliceD: SliceDescriptor;
box: BoundBox;
corner: GGSlice.Corner ← none;
loX: REALMIN[from.x, to.x ];
loY: REALMIN[from.y, to.y ];
hiX: REALMAX[from.x, to.x ];
hiY: REALMAX[from.y, to.y ];
IF to.x=loX THEN IF to.y=loY THEN corner ← ll ELSE corner ← ul;
IF to.x=hiX THEN IF to.y=loY THEN corner ← lr ELSE corner ← ur;
box ← GGBoundBox.CreateBoundBox[loX, loY, hiX, hiY];
ggData.drag.boxInProgress ← sliceD ← GGSlice.MakeBoxSlice[box, corner, GGTransform.Identity[]];
completeSliceD ← sliceD.slice.class.newParts[sliceD.slice, NIL, slice];
GGSlice.SetDefaults[completeSliceD.slice, completeSliceD.parts, ggData.defaults];
GGScene.AddSlice[ggData.scene, sliceD.slice, -1];
do no painting yet
};
StartBox: PUBLIC StartProc = {
sliceD: SliceDescriptor;
caretPos: Point;
CodeTimer.StartInt[$StartBox, $Gargoyle];
caretPos ← GGCaret.GetPoint[ggData.caret];
SaveSavedState[ggData]; -- must do this before any possible aborts occur
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay
sliceD ← AddNewBoxSlice[caretPos, worldPt, ggData];
GGSelect.DeselectAll[ggData.scene, normal];
GGSelect.SelectSlice[sliceD: sliceD, scene: ggData.scene, selectClass: normal];
Move new box to overlay
GGRefresh.MoveToOverlay[sliceD, ggData]; -- slice on overlay to be rubberbanded
Initialize the transformation and remember the starting position.
ggData.drag.startPoint ← worldPt;
ggData.drag.transform ← ImagerTransformation.Scale[1.0];
GGRefresh.SplitBackgroundAndOverlay[ggData, GGBoundBox.emptyBoundBox];
GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
DuringDrag[NIL, ggData, worldPt];
CodeTimer.StopInt[$StartBox, $Gargoyle];
};
StartSelectWithBox: PUBLIC StartProc = {
THIS IS A TEMPORARY PROC TO GET SELECTWITHBOX WITHOUT DEFAULT FILL COLOR GETTING IN THE WAY
sliceD: SliceDescriptor;
caretPos: Point;
CodeTimer.StartInt[$StartBox, $Gargoyle];
caretPos ← GGCaret.GetPoint[ggData.caret];
SaveSavedState[ggData]; -- must do this before any possible aborts occur
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay
sliceD ← AddNewBoxSlice[caretPos, worldPt, ggData];
sliceD.slice.class.setFillColor[sliceD.slice, NIL];
GGSelect.DeselectAll[ggData.scene, normal];
GGSelect.SelectSlice[sliceD: sliceD, scene: ggData.scene, selectClass: normal];
Move new box to overlay
GGRefresh.MoveToOverlay[sliceD, ggData]; -- slice on overlay to be rubberbanded
Initialize the transformation and remember the starting position.
ggData.drag.startPoint ← worldPt;
ggData.drag.transform ← ImagerTransformation.Scale[1.0];
GGRefresh.SplitBackgroundAndOverlay[ggData, GGBoundBox.emptyBoundBox];
GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
DuringDrag[NIL, ggData, worldPt];
CodeTimer.StopInt[$StartBox, $Gargoyle];
};
EndBox: PUBLIC MouseProc = {
The dragging is done. Update the box slice with the current transform. The box is cached in the ggData.
sliceD: SliceDescriptor ← NARROW[ggData.drag.boxInProgress];
slice: Slice ← sliceD.slice;
slice.class.transform[sliceD, ggData.drag.transform]; -- update the slice
GGRefresh.MoveOverlayToBackground[ggData];
ggData.refresh.startBoundBox^ ← slice.boundBox^; --newly updated by slice.class.transform
ggData.refresh.addedObject ← slice;
GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
EndSelectWithBox: PUBLIC MouseProc = {
The dragging is done. Update the box slice with the current transform. The box is cached in the ggData. Then, select the entire box and call AreaSelectNewAndDelete.
sliceD: SliceDescriptor ← NARROW[ggData.drag.boxInProgress];
slice: Slice ← sliceD.slice;
slice.class.transform[sliceD, ggData.drag.transform]; -- update the slice
GGRefresh.MoveOverlayToBackground[ggData];
GGSelect.SelectSlice[sliceD: slice.class.newParts[slice, NIL, topLevel], scene: ggData.scene, selectClass: normal];
GGEvent.AreaSelectNewAndDelete[ggData, NIL];
ggData.refresh.startBoundBox^ ← slice.boundBox^; --newly updated by slice.class.transform
GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
FixupAbortedBox: PROC [ggData: GGData] = {
This routine is called instead of EndBox. Find the box that is being added and delete it.
sliceD: SliceDescriptor ← NARROW[ggData.drag.boxInProgress];
slice: Slice ← sliceD.slice;
repaintBox: BoundBox ← GGCaret.BoundBoxOfCaret[ggData.caret, ggData]; -- start with caret
GGBoundBox.EnlargeByBox[repaintBox, slice.boundBox]; -- repaint deleted box slice
GGSelect.DeselectEntityAllClasses[slice, ggData.scene];
GGScene.DeleteSlice[ggData.scene, slice];
ggData.refresh.startBoundBox^ ← repaintBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
InitStats: PROC [] = {
interval: CodeTimer.Interval;
interval ← CodeTimer.CreateInterval[$StartAdd];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$DuringDrag];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$StartCaretPos];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$DuringCaretPos];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$EndCaretPos];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$ContinueAdd];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$StartMotion];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$EndMotion];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$StartBox];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$HandleGuarded];
CodeTimer.AddInt[interval, $Gargoyle];
};
InitStats[];
END.