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: ATOM ← NARROW[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: ATOM ← NARROW[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:
BOOL ←
TRUE] = {
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:
BOOL ←
TRUE] = {
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:
BOOL ←
TRUE, needAnchor:
BOOL ←
FALSE, backgroundOK:
BOOL ←
FALSE]
RETURNS [success:
BOOL ←
TRUE] = {
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: REAL ← MIN[from.x, to.x ];
loY: REAL ← MIN[from.y, to.y ];
hiX: REAL ← MAX[from.x, to.x ];
hiY: REAL ← MAX[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.