GGMouseEventImplA.mesa
Last edited by Bier on January 28, 1987 3:03:29 pm PST
Contents: Once a mouse event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Pier, January 15, 1987 1:27:43 pm PST
Kurlander August 7, 1986 10:54:44 am PDT
DIRECTORY
Atom, Basics, GGAlign, GGBasicTypes, GGBoundBox, AtomButtons, GGSlice, GGCaret, GGDescribe, GGMouseEvent, GGMultiGravity, GGEvent, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGObjects, GGOutline, GGRefresh, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGStatistics, GGTouch, GGTraj, GGTransform, GGVector, GGWindow, GList, Imager, ImagerTransformation, InputFocus, Menus, RealFns, Rope;
GGMouseEventImplA: CEDAR PROGRAM
IMPORTS Atom, Basics, GGAlign, GGBoundBox, AtomButtons, GGSlice, GGCaret, GGDescribe, GGError, GGEvent, GGGravity, GGMouseEvent, GGMultiGravity, GGObjects, GGOutline, GGRefresh, GGSegment, GGSelect, GGSequence, GGStatistics, GGTouch, GGTraj, GGTransform, GGVector, GGWindow, GList, Imager, ImagerTransformation, InputFocus, RealFns, Rope
EXPORTS GGMouseEvent = BEGIN
AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint;
BoundBox: TYPE = GGModelTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
FeatureData: TYPE = GGModelTypes.FeatureData;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
MouseButton: TYPE = Menus.MouseButton;
ObjectBag: TYPE = GGGravity.ObjectBag;
Outline: TYPE = GGModelTypes.Outline;
OutlineDescriptor: TYPE = REF OutlineDescriptorObj;
OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj;
Point: TYPE = GGBasicTypes.Point;
ResultFeatureType: TYPE = GGModelTypes.ResultFeatureType;
Scene: TYPE = GGModelTypes.Scene;
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;
TouchGroup: TYPE = GGSegmentTypes.TouchGroup;
TouchItem: TYPE = GGSegmentTypes.TouchItem;
TouchItemGenerator: TYPE = GGTouch.TouchItemGenerator;
Traj: TYPE = GGModelTypes.Traj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = GGObjects.TrajGenerator;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TriggerBag: TYPE = GGAlign.TriggerBag;
Vector: TYPE = GGBasicTypes.Vector;
MouseProc: TYPE = GGMouseEvent.MouseProc;
PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point];
StartProc: TYPE = GGMouseEvent.StartProc;
PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] RETURNS [success: BOOL ← TRUE];
NotYetImplemented: PUBLIC SIGNAL = CODE;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = GGError.Problem;
EasyAbort: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, 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 ← gargoyleData.scene;
GGObjects.RestoreSelections[scene];
GGCaret.Copy[from: gargoyleData.drag.savedCaret, to: gargoyleData.caret]; --restore original caret
FinishAbort[gargoyleData];
};
AbortAdd: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = {
GGEvent.DeleteCaretSegment[NIL, gargoyleData];
FinishAbort[gargoyleData];
};
AbortBox: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = {
FixupAbortedBox[gargoyleData];
FinishAbort[gargoyleData];
};
FinishAbort: PROC [gargoyleData: GargoyleData] = {
GGRefresh.MoveOverlayToBackground[gargoyleData];
GGError.AppendHerald[gargoyleData.feedback, ". . . Aborted.", end];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
ResetMouseMachinery: PUBLIC PROC [gargoyleData: GargoyleData] = {
gargoyleData.mouseMode ← $None;
gargoyleData.state ← $None;
GGRefresh.MoveOverlayToBackground[gargoyleData];
};
SaveSavedState: PROC [gargoyleData: GargoyleData] = {
GGObjects.SaveSelections[gargoyleData.scene];
GGWindow.SaveCaretPos[gargoyleData];
GGCaret.Copy[from: gargoyleData.caret, to: gargoyleData.drag.savedCaret];
};
The FSM
HandleMouseless: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
An easy way to handle the Abort action.
HandleMouse[event, [0.0, 0.0], clientData];
};
HandleMouse: PUBLIC PROC [event: LIST OF REF ANY, point: Point, clientData: REF ANY] = {
atom: ATOM;
gargoyleData: GargoyleData ← NARROW[clientData];
SELECT gargoyleData.mouseMode FROM
$CaretPos => HandleGuarded[StartCaretPos, DuringCaretPos, EndCaretPos, EasyAbort, NIL, event, gargoyleData, point];
$Add => HandleGuarded[StartAdd, DuringAdd, EndAdd, AbortAdd, ContinueAdd, event, gargoyleData, point];
$Box => HandleGuarded[StartBox, DuringAdd, EndBox, AbortBox, NIL, event, gargoyleData, point];
$Circle => HandleGuarded[StartCircle, DuringAdd, EndCircle, AbortCircle, NIL, event, gargoyleData, point];
$Drag => HandleGuarded[StartDrag, DuringDrag, EndMotion, EasyAbort, NIL, event, gargoyleData, point];
$CopyAndDrag => HandleGuarded[CopySelected, DuringDrag, EndMotion, EasyAbort, NIL, event, gargoyleData, point];
$Rotate => HandleGuarded[StartRotate, DuringRotate, EndMotion, EasyAbort, NIL, event, gargoyleData, point];
$Scale => HandleGuarded[StartScale, DuringScale, EndMotion, EasyAbort, NIL, event, gargoyleData, point];
$SixPoint => HandleGuarded[StartSixPoint, DuringSixPoint, EndMotion, EasyAbort, NIL, event, gargoyleData, point];
$SelectWithBox => HandleUnGuarded[StartBox, DuringAdd, EndSelectWithBox, AbortBox, event, gargoyleData, point];
$SelectJoint => HandleUnGuarded[StartSelectJoint, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point];
$ExtSelectJoint => HandleGuarded[GGMouseEvent.StartExtendSelectJoint, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point];
$SelectSegment => HandleUnGuarded[StartSelectSegment, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point];
$ExtSelectSegment => HandleGuarded[GGMouseEvent.StartExtendSelectSegment, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point];
$SelectTrajectory => HandleUnGuarded[StartSelectTrajectory, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point];
$ExtSelectTrajectory => HandleGuarded[GGMouseEvent.StartExtendSelectTraj, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point];
$SelectTopLevel => HandleUnGuarded[StartSelectTopLevel, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point];
$ExtSelectTopLevel => HandleGuarded[GGMouseEvent.StartExtendSelectTopLevel, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point];
$Extend => HandleUnGuarded[GGMouseEvent.StartExtendSelection, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, event, gargoyleData, point];
$DeselectJoint => HandleGuarded[GGMouseEvent.StartDeselectJoint, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point];
$DeselectSegment => HandleGuarded[GGMouseEvent.StartDeselectSegment, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point];
$DeselectTrajectory => HandleGuarded[GGMouseEvent.StartDeselectTrajectory, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point];
$DeselectTopLevel => HandleGuarded[GGMouseEvent.StartDeselectTopLevel, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point];
$None => {
atom ← NARROW[event.first];
SELECT atom FROM
$StartCaretPos => {
gargoyleData.mouseMode ← $CaretPos; HandleMouse[event, point, gargoyleData];
};
$StartAdd => {
gargoyleData.mouseMode ← $Add; HandleMouse[event, point, gargoyleData];
};
$StartBox => {
gargoyleData.mouseMode ← $Box; HandleMouse[event, point, gargoyleData];
};
$StartSelectWithBox => {
gargoyleData.mouseMode ← $SelectWithBox; HandleMouse[event, point, gargoyleData];
};
$StartDrag => {
gargoyleData.mouseMode ← $Drag; HandleMouse[event, point, gargoyleData];
};
$StartCopyAndDrag => {
gargoyleData.mouseMode ← $CopyAndDrag; HandleMouse[event, point, gargoyleData];
};
$StartRotate => {
gargoyleData.mouseMode ← $Rotate; HandleMouse[event, point, gargoyleData];
};
$StartScale => {
gargoyleData.mouseMode ← $Scale; HandleMouse[event, point, gargoyleData];
};
$StartSixPoint => {
gargoyleData.mouseMode ← $SixPoint; HandleMouse[event, point, gargoyleData];
};
$StartSelectJoint => {
gargoyleData.mouseMode ← $SelectJoint; HandleMouse[event, point, gargoyleData];
};
$StartExtSelectJoint => {
gargoyleData.mouseMode ← $ExtSelectJoint; HandleMouse[event, point, gargoyleData];
};
$StartSelectSegment => {
gargoyleData.mouseMode ← $SelectSegment; HandleMouse[event, point, gargoyleData];
};
$StartExtSelectSegment => {
gargoyleData.mouseMode ← $ExtSelectSegment; HandleMouse[event, point, gargoyleData];
};
$StartSelectTrajectory => {
gargoyleData.mouseMode ← $SelectTrajectory; HandleMouse[event, point, gargoyleData];
};
$StartExtSelectTrajectory => {
gargoyleData.mouseMode ← $ExtSelectTrajectory; HandleMouse[event, point, gargoyleData];
};
$StartSelectTopLevel => {
gargoyleData.mouseMode ← $SelectTopLevel; HandleMouse[event, point, gargoyleData];
};
$StartExtSelectTopLevel => {
gargoyleData.mouseMode ← $ExtSelectTopLevel; HandleMouse[event, point, gargoyleData];
};
$StartExtendSelection => {
gargoyleData.mouseMode ← $Extend; HandleMouse[event, point, gargoyleData];
};
$StartDeselectJoint => {
gargoyleData.mouseMode ← $DeselectJoint; HandleMouse[event, point, gargoyleData];
};
$StartDeselectSegment => {
gargoyleData.mouseMode ← $DeselectSegment; HandleMouse[event, point, gargoyleData];
};
$StartDeselectTrajectory => {
gargoyleData.mouseMode ← $DeselectTrajectory; HandleMouse[event, point, gargoyleData];
};
$StartDeselectTopLevel => {
gargoyleData.mouseMode ← $DeselectTopLevel; HandleMouse[event, point, gargoyleData];
};
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, gargoyleData: GargoyleData, worldPt: Point] = {
genericAction, atom: ATOM;
atomName: Rope.ROPE;
GGStatistics.StartInterval[$HandleGuarded, GGStatistics.GlobalTable[]];
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, gargoyleData, worldPt];
GGStatistics.StopInterval[$HandleGuarded, GGStatistics.GlobalTable[]];
};
Restart: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData] RETURNS [BOOL] = {
mouseMode: ATOM ← gargoyleData.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, gargoyleData: GargoyleData, worldPt: Point] = {
SELECT gargoyleData.state FROM
$None => {
SELECT genericAction FROM
$Start => {
gargoyleData.drag.currentPoint ← worldPt;
[] ← InputFocus.SetInputFocus[gargoyleData.actionArea];
IF startProc[input, gargoyleData, worldPt] THEN gargoyleData.state ← $Main
ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state ← $Aborted;}; };
ENDCASE;
};
$Main => {
SELECT genericAction FROM
$During => {
duringProc[input, gargoyleData, worldPt];
gargoyleData.drag.currentPoint ← worldPt;
};
$Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state ← $Aborted};
$GuardUp => gargoyleData.state ← $GuardUp;
$MouseUp => gargoyleData.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, gargoyleData, worldPt]; gargoyleData.state ← $None;
gargoyleData.mouseMode ← $None;
HandleMouse[input, worldPt, gargoyleData];
};
ENDCASE;
};
$GuardUp => {
SELECT genericAction FROM
$AllUp => {
endProc[input, gargoyleData, gargoyleData.drag.currentPoint];
gargoyleData.mouseMode ← $None;
gargoyleData.state ← $None;
};
$Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state ← $Aborted};
ENDCASE;
};
$MouseUp => {
SELECT genericAction FROM
$AllUp => {
endProc[input, gargoyleData, gargoyleData.drag.currentPoint];
gargoyleData.mouseMode ← $None;
gargoyleData.state ← $None;
};
$Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state ← $Aborted};
$Start => { -- we may be starting another action of this mode or some other mode.
IF Restart[input, gargoyleData] AND continueProc # NIL THEN {
IF continueProc[input, gargoyleData, worldPt] THEN {
gargoyleData.state ← $Main;
gargoyleData.drag.currentPoint ← worldPt;
}
ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state ← $Aborted;};
}
ELSE {
gargoyleData.mouseMode ← $None;
endProc[input, gargoyleData, gargoyleData.drag.currentPoint];
gargoyleData.state ← $None;
HandleMouse[input, worldPt, gargoyleData];
};
};
ENDCASE;
};
$Aborted => {
SELECT genericAction FROM
$AllUp => {gargoyleData.state ← $None; gargoyleData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
HandleUnGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, input: LIST OF REF ANY, gargoyleData: GargoyleData, 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, gargoyleData, worldPt];
};
HandleUnGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction: ATOM, input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = {
SELECT gargoyleData.state FROM
$None => {
SELECT genericAction FROM
$Start => {
[] ← InputFocus.SetInputFocus[gargoyleData.actionArea];
IF startProc[input, gargoyleData, worldPt] THEN gargoyleData.state ← $Main
ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state ← $Aborted}
};
ENDCASE;
};
$Main => {
SELECT genericAction FROM
$During => duringProc[input, gargoyleData, worldPt];
$MouseUp, $AllUp => {
endProc[input, gargoyleData, worldPt];
gargoyleData.state ← $None;
gargoyleData.mouseMode ← $None;
};
$Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.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, gargoyleData, worldPt]; gargoyleData.state ← $None;
gargoyleData.mouseMode ← $None;
HandleMouse[input, worldPt, gargoyleData];
};
ENDCASE;
};
$Aborted => {
SELECT genericAction FROM
$AllUp => {gargoyleData.state ← $None; gargoyleData.mouseMode ← $None};
$MouseUp => {gargoyleData.state ← $None; gargoyleData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
Selection Procs
SortNewEntities: PROC [entityList: LIST OF REF ANY] RETURNS [sorted: LIST OF REF ANY] = {
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 ← slice.priority;
outline: Outline => priority1 ← outline.priority;
ENDCASE;
WITH ref2 SELECT FROM
slice: Slice => priority2 ← slice.priority;
outline: Outline => priority2 ← outline.priority;
ENDCASE;
RETURN[Basics.CompareINT[priority1, priority2]];
};
sorted ← NARROW[GList.Sort[entityList, CompareProc]];
};
ComputePriorities: PROC [scene: Scene] = {
count: INT ← 0;
entityGen: EntityGenerator ← GGObjects.TopLevelEntitiesInScene[scene];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
slice: Slice => slice.priority ← count;
outline: Outline => outline.priority ← count;
ENDCASE => ERROR;
count ← count + 1;
ENDLOOP;
};
CopySelected: PUBLIC StartProc = {
Make 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.
newTraj: Traj;
newSlice: Slice;
entityList: LIST OF REF ANY;
allParts: SliceParts;
newOutline, outline: Outline;
outSeqGen: GGSelect.OutlineSequenceGenerator;
sliceDGen: SliceDescriptorGenerator;
scene: Scene ← gargoyleData.scene;
SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur
ComputePriorities[gargoyleData.scene];
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];
entityList ← CONS[newSlice, entityList];
newSlice.priority ← sliceD.slice.priority;
ENDLOOP;
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 GGSequence.NextSegment[GGSequence.SegmentsInSequence[outSeq.fenceSeq]]=NIL THEN {
-- no segments, only joints selected
GGError.AppendHerald[gargoyleData.feedback, ". . . Cannot Copy Joints or CPs", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN[FALSE];
};
If the whole outline is selected, copy it as a whole.
IF GGSelect.IsSelectedInFull[outline, scene, normal] THEN {
newOutline ← outline.class.copy[outline];
GGSelect.DeselectEntireOutline[outline, scene, normal]; -- deselect the old one
entityList ← CONS[newOutline, entityList];
newOutline.priority ← outline.priority;
LOOP;
}
Otherwise, each piece becomes a new slice.
ELSE {
newTraj ← GGTraj.CopyTrajFromRun[outSeq.fenceSeq];
newOutline ← GGOutline.CreateOutline[newTraj, outline.lineEnds, outline.fillColor];
GGSelect.DeselectSequence[outSeq.fenceSeq, scene, normal];
entityList ← CONS[newOutline, entityList];
newOutline.priority ← outline.priority;
};
};
FOR holeSeq: Sequence ← GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO
IF GGSequence.NextSegment[GGSequence.SegmentsInSequence[holeSeq]]=NIL THEN {
-- no segments, only joints selected
GGError.AppendHerald[gargoyleData.feedback, ". . . Cannot Copy Joints or CPs", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN[FALSE];
};
newTraj ← GGTraj.CopyTrajFromRun[holeSeq];
newOutline ← GGOutline.CreateOutline[newTraj, outline.lineEnds, outline.fillColor];
GGSelect.DeselectSequence[holeSeq, scene, normal];
entityList ← CONS[newOutline, entityList];
newOutline.priority ← outline.priority;
ENDLOOP;
ENDLOOP;
Sort the new slices
entityList ← SortNewEntities[entityList];
Add all the new slices to the scene.
FOR sliceList: LIST OF REF ANY ← entityList, sliceList.rest UNTIL sliceList = NIL DO
WITH sliceList.first SELECT FROM
newSlice: Slice => {
GGObjects.AddSlice[gargoyleData.scene, newSlice, -1];
allParts ← newSlice.class.newParts[newSlice, NIL, slice];
GGSelect.SelectSlice[newSlice, allParts, scene, normal];
};
newOutline: Outline => {
GGObjects.AddOutline[gargoyleData.scene, newOutline, -1];
allParts ← newOutline.class.newParts[newOutline, NIL, slice];
GGSelect.SelectOutline[newOutline, allParts, scene, normal];
};
ENDCASE => ERROR;
ENDLOOP;
[] ← StartDrag[NIL, gargoyleData, worldPt];
};
StartSelectJoint: PUBLIC StartProc = {
gargoyleData.drag.selectState ← joint;
StartSelectAux[gargoyleData, worldPt];
DuringSelect[NIL, gargoyleData, worldPt];
};
StartSelectSegment: PUBLIC StartProc = {
gargoyleData.drag.selectState ← segment;
StartSelectAux[gargoyleData, worldPt];
DuringSelect[NIL, gargoyleData, worldPt];
};
StartSelectTrajectory: PUBLIC StartProc = {
gargoyleData.drag.selectState ← traj;
StartSelectAux[gargoyleData, worldPt];
DuringSelect[NIL, gargoyleData, worldPt];
};
StartSelectTopLevel: PUBLIC StartProc = {
gargoyleData.drag.selectState ← topLevel;
StartSelectAux[gargoyleData, worldPt];
DuringSelect[NIL, gargoyleData, worldPt];
};
StartSelectAux: PROC [gargoyleData: GargoyleData, worldPt: Point] = {
IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR;
SaveSavedState[gargoyleData];
};
DuringSelect: PUBLIC MouseProc = {
While a joint, segment, traj, or top level object is being selected, gravity is forced to be StrictDistance. The object bag should consist only of trajectories and slices. The caret is moved to the segment endpoint of the nearest segment or traj as appropriate or tracks the cursor if none are nearby. Feedback is in the form of highlighted joints.
resultPoint: Point;
feature: FeatureData;
Use StrictDistance gravity except for SelectJoint.
IF gargoyleData.drag.selectState = joint THEN
[resultPoint, feature] ← GGMultiGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE]
ELSE [resultPoint, feature] ← GGMultiGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData];
Put Caret on a joint (if any).
GGMouseEvent.SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature];
GGMouseEvent.DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.selectState, "Selecting"];
Deselect all.
GGSelect.DeselectAll[gargoyleData.scene, normal];
IF feature = NIL THEN { -- no near trajectories, caret in free space
}
ELSE {
Do the Selections
SELECT gargoyleData.drag.selectState FROM
joint => SelectJointOrCP[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData];
segment => SelectSegment[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData];
traj => SelectTraj[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData];
topLevel => SelectTopLevel[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData];
ENDCASE => ERROR;
};
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end DuringSelect
SelectJointOrCP: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = {
[----] ← SelectJointOrCPInternal[feature, caretPt, gargoyleData];
};
SelectJointOrCPInternal: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts ← NIL]= {
Find out which point is being selected and highlight it.
SELECT feature.resultType FROM
slice => {
sliceD: SliceDescriptor ← NARROW[feature.shape];
parts ← sliceD.slice.class.newParts[sliceD.slice, feature.hitPart, joint];
GGSelect.SelectSlice[sliceD.slice, parts, gargoyleData.scene, normal];
};
outline => {
outlineD: OutlineDescriptor ← NARROW[feature.shape];
parts ← outlineD.slice.class.newParts[outlineD.slice, feature.hitPart, joint];
GGSelect.SelectOutline[outlineD.slice, parts, gargoyleData.scene, normal];
};
midpoint => NULL; -- temporarily ignore midpoints in bags. KAP. June 26, 1986
slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR;
There shouldn't be any alignment lines in the object bag.
ENDCASE => ERROR NotYetImplemented;
}; -- end SelectJointOrCPInternal
SelectSegment: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = {
[----] ← SelectSegmentInternal[feature, caretPt, gargoyleData];
};
SelectSegmentInternal: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts ← NIL] = {
Find out which segment is being selected and highlight it.
SELECT feature.resultType FROM
outline => {
outline: Outline ← NARROW[feature.shape, OutlineDescriptor].slice;
parts ← outline.class.newParts[outline, feature.hitPart, segment];
GGSelect.SelectOutline[outline, parts, gargoyleData.scene, normal];
};
slice => {
slice: Slice ← NARROW[feature.shape, SliceDescriptor].slice;
parts ← slice.class.newParts[slice, feature.hitPart, segment];
GGSelect.SelectSlice[slice, parts, gargoyleData.scene, normal];
};
midpoint => NULL; -- temporarily ignore midpoints in bags. KAP. June 26, 1986
slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR;
There shouldn't be any alignment lines in the object bag.
ENDCASE => ERROR NotYetImplemented;
}; -- end of SelectSegment
SelectTraj: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = {
[] ← SelectTrajInternal[feature, caretPt, gargoyleData];
};
SelectTrajInternal: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts ← NIL] = {
SelectFeedbackProc
Find out which traj is being selected and highlight it.
SELECT feature.type FROM
outline => {
outline: Outline ← NARROW[feature.shape, OutlineDescriptor].slice;
parts ← outline.class.newParts[outline, feature.hitPart, traj];
GGSelect.SelectOutline[outline, parts, gargoyleData.scene, normal];
};
slice => {
slice: Slice ← NARROW[feature.shape, SliceDescriptor].slice;
parts ← slice.class.newParts[slice, feature.hitPart, traj];
GGSelect.SelectSlice[slice, parts, gargoyleData.scene, normal];
};
ENDCASE => SIGNAL Problem[msg: "Unexpected feature type"];
}; -- end SelectTraj
SelectTopLevel: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = {
[] ← SelectTopLevelInternal[feature, caretPt, gargoyleData];
};
SelectTopLevelInternal: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts ← NIL] = {
SelectFeedbackProc
Find out which traj is being selected and highlight it.
SELECT feature.resultType FROM
outline => {
outline: Outline ← NARROW[feature.shape, OutlineDescriptor].slice;
parts ← outline.class.newParts[outline, feature.hitPart, topLevel];
GGSelect.SelectOutline[outline, parts, gargoyleData.scene, normal];
};
slice => {
slice: Slice ← NARROW[feature.shape, SliceDescriptor].slice;
parts ← slice.class.newParts[slice, feature.hitPart, topLevel];
GGSelect.SelectSlice[slice, parts, gargoyleData.scene, normal];
};
midpoint => NULL; -- temporarily ignore midpoints in bags. KAP. June 26, 1986
slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR;
There shouldn't be any alignment lines in the object bag.
ENDCASE => ERROR NotYetImplemented;
};
EndSelect: PUBLIC MouseProc = {
resultPoint: Point;
feature: FeatureData;
Use StrictDistance gravity except for SelectJoint.
IF gargoyleData.drag.selectState = joint THEN
[resultPoint, feature] ← GGMultiGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE]
ELSE [resultPoint, feature] ← GGMultiGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData];
Put Caret on a joint (if any).
GGMouseEvent.SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature];
Use the commented code for more explicit messages.
GGMouseEvent.DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.selectState, "Selected"];
GGWindow.NewCaretPos[gargoyleData];
Deselect all.
GGSelect.DeselectAll[gargoyleData.scene, normal];
Dispatch to the proper EndSelect handler for final selection.
SELECT gargoyleData.drag.selectState FROM
joint => EndSelectJoint[gargoyleData, resultPoint, feature];
segment => EndSelectSegment[gargoyleData, resultPoint, feature];
traj => EndSelectTrajectory[gargoyleData, resultPoint, feature];
topLevel => EndSelectTopLevel[gargoyleData, resultPoint, feature];
ENDCASE => ERROR;
gargoyleData.drag.selectState ← none; -- added to help DescribeFeature work. KAP.
}; -- end EndSelect
EndSelectJoint: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
Prepare for a subsequent Add operation.
IF feature = NIL THEN { -- no preparation needed
GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, "No near joint found.", oneLiner];
}
ELSE {
Make Selection and Prepare for Extend.
gargoyleData.drag.extendMode ← joint;
SELECT feature.resultType FROM
outline => {
outlineD: OutlineDescriptor ← NEW[OutlineDescriptorObj ← [slice: NARROW[feature.shape, OutlineDescriptor].slice] ];
outlineD.parts ← SelectJointOrCPInternal[feature, resultPoint, gargoyleData];
GGCaret.SitOn[gargoyleData.caret, outlineD];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[outlineD.slice.class.describe[outlineD.slice, outlineD.parts], " selected"], oneLiner];
gargoyleData.drag.outlineToExtend ← outlineD;
};
slice => {
sliceD: SliceDescriptor ← NEW[SliceDescriptorObj ← [slice: NARROW[feature.shape, SliceDescriptor].slice] ];
sliceD.parts ← SelectJointOrCPInternal[feature, resultPoint, gargoyleData];
GGCaret.SitOn[gargoyleData.caret, sliceD];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner];
gargoyleData.drag.sliceToExtend ← sliceD;
};
ENDCASE => ERROR Problem[msg: "Unexpected feature type"];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndSelectJoint
EndSelectSegment: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
Prepare for a subsequent Add operation and for Extend.
IF feature = NIL THEN {
GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, "No near segment found.", oneLiner];
}
ELSE {
gargoyleData.drag.extendMode ← segmentRange;
SELECT feature.resultType FROM
outline => {
jointParts: SliceParts;
jointSeq: Sequence;
jointD: OutlineDescriptor;
traj: Traj;
jointNum: NAT;
outlineD: OutlineDescriptor ← NEW[OutlineDescriptorObj ← [slice: NARROW[feature.shape, OutlineDescriptor].slice]];
outlineD.parts ← SelectSegmentInternal[feature, resultPoint, gargoyleData];
IF outlineD.slice.class.type = $Outline THEN {
[jointNum, traj] ← GGOutline.NearestJointToHitData[feature.hitPart];
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
jointParts ← GGOutline.PartsFromSequence[outlineD.slice, jointSeq];
jointD ← NEW[OutlineDescriptorObj ← [outlineD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[outlineD.slice.class.describe[outlineD.slice, outlineD.parts], " selected"], oneLiner];
gargoyleData.drag.outlineToExtend ← outlineD;
};
slice => {
sliceD: SliceDescriptor ← NEW[SliceDescriptorObj ← [slice: NARROW[feature.shape, SliceDescriptor].slice]];
sliceD.parts ← SelectSegmentInternal[feature, resultPoint, gargoyleData];
IF sliceD.slice.class.type = $Outline THEN {
ERROR Problem[msg: "Outlines must be Slices now."];
[jointNum, traj] ← GGOutline.NearestJointToHitData[feature.hitPart];
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
jointParts ← GGOutline.PartsFromSequence[sliceD.slice, jointSeq];
jointD ← NEW[OutlineDescriptorObj ← [sliceD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner];
gargoyleData.drag.sliceToExtend ← sliceD;
};
ENDCASE => SIGNAL Problem["Unexpected feature type"];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end EndSelectSegment
EndSelectTrajectory: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
IF feature = NIL THEN {
GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, "No near trajectory found.", oneLiner];
}
ELSE {
Select the Trajectory or Slice. Prepare for Add and for Extend.
parts: SliceParts ← SelectTrajInternal[feature, resultPoint, gargoyleData];
gargoyleData.drag.extendMode ← traj;
SELECT feature.type FROM
outline => {
jointParts: SliceParts;
jointSeq: Sequence;
jointD: OutlineDescriptor;
traj: Traj;
jointNum: NAT;
outlineD: OutlineDescriptor ← NEW[OutlineDescriptorObj ← [slice: NARROW[feature.shape, OutlineDescriptor].slice, parts: parts]];
IF outlineD.slice.class.type = $Outline THEN {
[jointNum, traj] ← GGOutline.NearestJointToHitData[feature.hitPart];
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
jointParts ← GGOutline.PartsFromSequence[outlineD.slice, jointSeq];
jointD ← NEW[OutlineDescriptorObj ← [outlineD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[outlineD.slice.class.describe[outlineD.slice, outlineD.parts], " selected"], oneLiner];
gargoyleData.drag.outlineToExtend ← outlineD;
};
slice => {
sliceD: SliceDescriptor ← NEW[SliceDescriptorObj ← [slice: NARROW[feature.shape, SliceDescriptor].slice, parts: parts] ];
IF sliceD.slice.class.type = $Outline THEN {
ERROR Problem[msg: "Outlines must be Slices now."];
[jointNum, traj] ← GGOutline.NearestJointToHitData[feature.hitPart];
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
jointParts ← GGOutline.PartsFromSequence[sliceD.slice, jointSeq];
jointD ← NEW[OutlineDescriptorObj ← [sliceD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner];
gargoyleData.drag.sliceToExtend ← sliceD;
};
ENDCASE => SIGNAL Problem[msg: "Unexpected feature type"];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end SelectTrajectory
EndSelectTopLevel: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = {
IF feature = NIL THEN { -- no selection
GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, "No near object found.", oneLiner];
}
ELSE {
Perform the selection. Prepare for a subsequent Add or Extend.
parts: SliceParts ← SelectTopLevelInternal[feature, resultPoint, gargoyleData];
gargoyleData.drag.extendMode ← topLevel;
SELECT feature.resultType FROM
outline => {
jointParts: SliceParts;
jointSeq: Sequence;
jointD: OutlineDescriptor;
traj: Traj;
jointNum: NAT;
outlineD: OutlineDescriptor ← NEW[OutlineDescriptorObj ← [slice: NARROW[feature.shape, OutlineDescriptor].slice, parts: parts]];
IF outlineD.slice.class.type = $Outline THEN {
[jointNum, traj] ← GGOutline.NearestJointToHitData[feature.hitPart];
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
jointParts ← GGOutline.PartsFromSequence[outlineD.slice, jointSeq];
jointD ← NEW[OutlineDescriptorObj ← [outlineD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, "Top level outline selected.", oneLiner];
gargoyleData.drag.outlineToExtend ← outlineD;
};
slice => {
sliceD: SliceDescriptor ← NEW[SliceDescriptorObj ← [slice: NARROW[feature.shape, SliceDescriptor].slice, parts: parts]];
IF sliceD.slice.class.type = $Outline THEN {
ERROR Problem[msg: "Outlines must be Slices now."];
[jointNum, traj] ← GGOutline.NearestJointToHitData[feature.hitPart];
jointSeq ← GGSequence.CreateFromJoint[traj, jointNum];
jointParts ← GGOutline.PartsFromSequence[sliceD.slice, jointSeq];
jointD ← NEW[OutlineDescriptorObj ← [sliceD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE GGCaret.SitOn[gargoyleData.caret, NIL];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner];
gargoyleData.drag.sliceToExtend ← sliceD;
};
ENDCASE => ERROR; -- nothing else should be in the object bag
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end SelectTopLevel
TransformObjectsAfterMove: PROC [gargoyleData: GargoyleData] = {
entityGen: EntityGenerator;
GGTouch.InitializeTouching[gargoyleData];
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
outlineD: OutlineDescriptor => {
outlineD.slice.class.transform[outlineD.slice, outlineD.parts, gargoyleData.drag.transform];
};
sliceD: SliceDescriptor => {
sliceD.slice.class.transform[sliceD.slice, sliceD.parts, gargoyleData.drag.transform];
};
ENDCASE => ERROR NotYetImplemented;
ENDLOOP;
};
Caret Procs
StartCaretPos: PUBLIC StartProc = {
The user wishes to place the caret without changing the current selection. Only hot objects trigger alignment lines.
GGStatistics.StartInterval[$StartCaretPos, GGStatistics.GlobalTable[]];
StartSelectAux[gargoyleData, worldPt];
DuringCaretPos[NIL, gargoyleData, worldPt];
GGStatistics.StopInterval[$StartCaretPos, GGStatistics.GlobalTable[]];
};
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;
GGStatistics.StartInterval[$DuringCaretPos, GGStatistics.GlobalTable[]];
[resultPoint, feature] ← GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE];
SetCaretAttractor[gargoyleData, resultPoint, feature]; -- move caret to feature point
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringCaretPos, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; -- show caret in new position
GGStatistics.StopInterval[$DuringCaretPos, GGStatistics.GlobalTable[]];
}; -- end of DuringCaretPos
EndCaretPos: PUBLIC MouseProc = {
resultPoint: Point;
feature: FeatureData;
currentObjects: ObjectBag ← NARROW[gargoyleData.hitTest.currentObjectBag];
sceneObjects: TriggerBag ← NARROW[gargoyleData.hitTest.sceneTriggerBag];
GGStatistics.StartInterval[$EndCaretPos, GGStatistics.GlobalTable[]];
[resultPoint, feature] ← GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, currentObjects, sceneObjects, gargoyleData, TRUE];
Move caret to new position.
SetCaretAttractor[gargoyleData, resultPoint, feature, "Final"]; -- move caret to feature point
GGWindow.NewCaretPos[gargoyleData];
GGCaret.SitOn[gargoyleData.caret, NIL]; -- subsequent Add operations will start a NEW trajectory.
GGWindow.RestoreScreenAndInvariants[paintAction: $CaretMoved, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
GGStatistics.StopInterval[$EndCaretPos, GGStatistics.GlobalTable[]];
}; -- 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 [gargoyleData: GargoyleData, mapPoint: Point, feature: FeatureData, action: Rope.ROPE ← ""] = {
Called by EndCaretPos and all the "During" procs. Moves the caret and records the attractor which is a simple descriptor. This code very similar to SetCaretAttractorEndpoint in GGMouseEventImplB.
IF feature = NIL THEN GGCaret.SetAttractor[gargoyleData.caret, mapPoint, NIL]
ELSE {
SELECT feature.resultType FROM
outline => {
traj: Traj;
hitType: GGModelTypes.TrajPartType;
segNum, cpNum, jointNum: INT;
seq: Sequence;
parts: SliceParts;
partsD: OutlineDescriptor;
outlineD: OutlineDescriptor ← NARROW[feature.shape];
[traj, hitType, segNum, cpNum, jointNum] ← GGOutline.UnpackHitData[feature.hitPart];
SELECT hitType FROM
joint => {
seq ← GGSequence.CreateFromJoint[traj, jointNum];
};
controlPoint => {
seq ← GGSequence.CreateFromControlPoint[traj, segNum, cpNum];
};
segment => {
seq ← GGSequence.CreateSimpleFromSegment[traj, segNum];
};
ENDCASE => ERROR;
parts ← GGOutline.PartsFromSequence[outlineD.slice, seq];
partsD ← NEW[OutlineDescriptorObj ← [outlineD.slice, parts]];
GGCaret.SetAttractor[gargoyleData.caret, mapPoint, partsD];
};
slice => {
GGCaret.SetAttractor[gargoyleData.caret, mapPoint, feature.shape];
};
ENDCASE => GGCaret.SetAttractor[gargoyleData.caret, mapPoint, NIL];
};
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g Caret on %g at [%g, %g]",
[rope[action]], [rope[GGDescribe.DescribeFeature[feature, gargoyleData]]],
[real[mapPoint.x]], [real[mapPoint.y]]
];
};
Motion Procs
StartMotionAux: PROC [gargoyleData: GargoyleData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, needAnchor: BOOLFALSE] RETURNS [BOOL] = {
restoreBox: BoundBox;
repaintNeeded: BOOL;
IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay
SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur
IF GGSelect.NoSelections[gargoyleData.scene, normal] THEN {
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Select some objects to %g", [rope[opName]]];
GGError.Blink[gargoyleData.feedback];
RETURN[FALSE];
};
IF needAnchor AND NOT GGCaret.Exists[gargoyleData.anchor] THEN {
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Anchor needed to %g", [rope[opName]]];
GGError.Blink[gargoyleData.feedback];
RETURN[FALSE];
};
GGRefresh.MoveAllSelectedToOverlay[gargoyleData, normal];
gargoyleData.drag.startPoint ← GGCaret.GetPoint[gargoyleData.caret];
GGCaret.SetAttractor[gargoyleData.caret, gargoyleData.drag.startPoint, NIL]; -- move caret to start point
gargoyleData.drag.transform ← ImagerTransformation.Scale[1.0];
restoreBox ← GGBoundBox.BoundBoxOfMoving[gargoyleData.scene];
gargoyleData.refresh.startBoundBox^ ← restoreBox^;
GGRefresh.SplitBackgroundAndOverlay[gargoyleData, restoreBox];
repaintNeeded ← GGAlign.UpdateBagsForAction[gargoyleData, bagType];
IF repaintNeeded THEN GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]
ELSE GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
RETURN[TRUE];
};
StartDrag: PUBLIC StartProc = {
startSuccess: BOOL ← StartMotionAux[gargoyleData, "drag", $Drag, worldPt];
IF NOT startSuccess THEN RETURN[FALSE];
DuringDrag[NIL, gargoyleData, worldPt];
};
StartRotate: PUBLIC StartProc = {
startSuccess: BOOL ← StartMotionAux[gargoyleData, "rotate", $Drag, worldPt, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
DuringRotate[NIL, gargoyleData, worldPt];
};
StartScale: PUBLIC StartProc = {
anchorPoint: Point;
originalVector: Vector;
startSuccess: BOOL ← StartMotionAux[gargoyleData, "scale", $Drag, worldPt, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
anchorPoint ← GGCaret.GetPoint[gargoyleData.anchor];
originalVector ← GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint];
IF originalVector = [0.0, 0.0] THEN {
GGError.AppendHerald[gargoyleData.feedback, "Move caret away from anchor before scaling.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN[FALSE];
};
DuringScale[NIL, gargoyleData, worldPt];
};
StartSixPoint: PUBLIC StartProc = {
OPEN GGVector;
epsilon: REAL = 1.0e-3;
p0, p1, p2: Point;
crossProduct: REAL;
startSuccess: BOOL ← StartMotionAux[gargoyleData, "six point", $Drag, worldPt, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
p0 ← GGCaret.GetPoint[gargoyleData.anchor];
p2 ← gargoyleData.drag.startPoint;
p1 ← Add[p0, VectorPlusAngle[Sub[p2, p0], 90.0]];
crossProduct ← CrossProductScalar[Sub[p1,p0], Sub[p2, p0]];
IF ABS[crossProduct] < epsilon THEN {
GGError.AppendHerald[gargoyleData.feedback, "Move caret away from anchor before six point.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN[FALSE];
};
DuringSixPoint[NIL, gargoyleData, worldPt];
};
We keep track of the total vector of motion since the drag began (i.e. the difference between the initial mouse position and the current mouse position. We transform all of the dragged objects before redrawing them. This style should be better than changing the actual data since there is less accumulated error, and the undo transformation is easily derived.
DragTheCaret: PROC [worldPt: Point, gargoyleData: GargoyleData, opName: Rope.ROPE] RETURNS [mapPoint: Point] = {
feature: FeatureData;
[mapPoint, feature] ← GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE];
SetCaretAttractor[gargoyleData, mapPoint, feature, opName];
};
DuringDrag: PUBLIC MouseProc = {
totalDragVector: Vector;
mapPoint: Point;
mapPoint ← DragTheCaret[worldPt, gargoyleData, "Dragging:"];
totalDragVector ← GGVector.Sub[mapPoint, gargoyleData.drag.startPoint];
gargoyleData.drag.transform ← ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}; -- end DuringDrag
DuringRotate: PUBLIC MouseProc = {
originalVector, newVector: Vector;
mapPoint: Point;
degrees: REAL;
anchorPoint: Point ← GGCaret.GetPoint[gargoyleData.anchor];
mapPoint ← DragTheCaret[worldPt, gargoyleData, "Rotating:"];
originalVector ← GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint];
newVector ← GGVector.Sub[mapPoint, anchorPoint];
degrees ← GGVector.AngleCCWBetweenVectors[originalVector, newVector];
gargoyleData.drag.transform ← GGTransform.RotateAboutPoint[anchorPoint, degrees];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, 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[gargoyleData.anchor];
mapPoint ← DragTheCaret[worldPt, gargoyleData, "Scaling:"];
originalVector ← GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint];
newVector ← GGVector.Sub[mapPoint, anchorPoint];
IF RealFns.AlmostZero[newVector.x, -10] AND RealFns.AlmostZero[newVector.y, -10] THEN RETURN; -- can't scale to zero
ratio ← GGVector.Magnitude[newVector]/GGVector.Magnitude[originalVector];
gargoyleData.drag.transform ← GGTransform.ScaleAboutPoint[anchorPoint, ratio];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringSixPoint: PUBLIC MouseProc = {
OPEN GGVector;
epsilon: REAL = 1.0e-3;
pts: ARRAY [0..5] OF Point;
crossProduct: REAL;
mapPoint: Point;
pts[0] ← pts[3] ← GGCaret.GetPoint[gargoyleData.anchor];
pts[2] ← gargoyleData.drag.startPoint;
pts[1] ← pts[4] ← Add[pts[0], VectorPlusAngle[Sub[pts[2], pts[0]], 90.0]];
mapPoint ← DragTheCaret[worldPt, gargoyleData, "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
gargoyleData.drag.transform ← GGTransform.SixPoints[pts];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
EndMotion: PUBLIC MouseProc = {
The dragging is done. Update all of the drag entities with the total drag vector and repaint the entire scene.
GGStatistics.StartInterval[$EndMotion, GGStatistics.GlobalTable[]];
TransformObjectsAfterMove[gargoyleData];
GGRefresh.MoveOverlayToBackground[gargoyleData];
GGWindow.NewCaretPos[gargoyleData];
GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedDragging, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
GGStatistics.StopInterval[$EndMotion, GGStatistics.GlobalTable[]];
};
Addition and Extension Procs
ExtendTrajToMouse: PRIVATE PROC [scene: Scene, worldPt: Point, gargoyleData: GargoyleData] RETURNS [traj: Traj, movingJointSeq: Sequence, success: BOOL] = {
caret: Caret ← gargoyleData.caret;
chair: REF ANY;
caretPoint: Point;
jointNum: NAT;
newLine: Segment;
jointParts: SliceParts;
jointD: OutlineDescriptor;
partType: TrajPartType;
caretPoint ← GGCaret.GetPoint[caret];
IF GGCaret.SittingOnEnd[caret] THEN { -- extend the existing trajectory
outlineD: OutlineDescriptor;
chair ← GGCaret.GetChair[caret]; -- better be a traj !!
IF ISTYPE[chair, SliceDescriptor] THEN ERROR Problem[msg: "Slices are Outlines now."];
outlineD ← NARROW[chair];
[success, partType, traj, ----, jointNum] ← GGOutline.UnpackSimpleDescriptorOld[outlineD];
IF NOT success OR partType # joint THEN ERROR;
IF jointNum = 0 THEN { -- add to the low end of the trajectory
newLine ← GGSegment.MakeLine[worldPt, caretPoint, NIL];
success ← GGTraj.AddSegment[traj, lo, newLine, hi];
IF NOT success THEN RETURN;
The trajectory is about to change size. Any sequences stored on the selection lists are obsolete. Update them.
GGSelect.ReselectTraj[traj, lo, gargoyleData.scene, TRUE];
GGCaret.SetAttractor[gargoyleData.caret, worldPt, NIL];
movingJointSeq ← GGSequence.CreateFromJoint[traj, 0];
jointParts ← GGOutline.PartsFromSequence[outlineD.slice, movingJointSeq];
jointD ← NEW[OutlineDescriptorObj ← [outlineD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE IF jointNum = GGTraj.HiJoint[traj] THEN { -- add to the high end of the trajectory
newLine ← GGSegment.MakeLine[caretPoint, worldPt, NIL];
success ← GGTraj.AddSegment[traj, hi, newLine, lo];
IF NOT success THEN RETURN;
GGSelect.ReselectTraj[traj, hi, gargoyleData.scene, TRUE];
The trajectory is about to change size. Any sequences stored on the selection lists are obsolete. Update them.
GGCaret.SetAttractor[gargoyleData.caret, worldPt, NIL];
movingJointSeq ← GGSequence.CreateFromJoint[traj, GGTraj.HiJoint[traj]];
jointParts ← GGOutline.PartsFromSequence[outlineD.slice, movingJointSeq];
jointD ← NEW[OutlineDescriptorObj ← [outlineD.slice, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
}
ELSE ERROR;
}
ELSE { -- Create a new trajectory starting at the caret (making touching constraints, if any).
newOutline: Outline;
newLine ← GGSegment.MakeLine[caretPoint, worldPt, NIL];
traj ← GGTraj.CreateTraj[caretPoint];
success ← GGTraj.AddSegment[traj, hi, newLine, lo];
IF NOT success THEN RETURN;
newOutline ← GGOutline.CreateOutline[traj];
GGObjects.AddOutline[scene, newOutline, -1];
GGCaret.SetAttractor[gargoyleData.caret, worldPt, NIL];
movingJointSeq ← GGSequence.CreateFromJoint[traj, 1];
jointParts ← GGOutline.PartsFromSequence[newOutline, movingJointSeq];
jointD ← NEW[OutlineDescriptorObj ← [newOutline, jointParts]];
GGCaret.SitOn[gargoyleData.caret, jointD];
};
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGSelect.SelectSequence[movingJointSeq, gargoyleData.scene, normal];
};
ContinueAdd: PUBLIC StartProc = {
The dragging is done. Update the endpoint with the current transform. Then begin a new add operation.
selSeq: Sequence ← gargoyleData.drag.seqInProgress;
GGTraj.TransformSequence[selSeq, gargoyleData.drag.transform];
GGWindow.NewCaretPos[gargoyleData];
GGRefresh.MoveOverlayToBackground[gargoyleData];
success ← StartAdd[LIST[$ContinueAdd], gargoyleData, worldPt];
};
StartAdd: PUBLIC StartProc = {
continue, repaintNeeded: BOOL;
newOutline: Outline;
GGStatistics.StartInterval[$StartAdd, GGStatistics.GlobalTable[]];
continue ← NARROW[input.first, ATOM] = $ContinueAdd;
[newOutline, success] ← StartAddAux[gargoyleData, worldPt];
IF NOT success THEN RETURN;
Update the Drawing data structures.
GGRefresh.SplitBackgroundAndOverlay[gargoyleData, GGBoundBox.emptyBoundBox];
repaintNeeded ← GGAlign.UpdateBagsForAction[gargoyleData, $Drag];
IF NOT repaintNeeded OR
(continue AND AtomButtons.GetButtonState[gargoyleData.hitTest.heuristicsButton] = off) THEN {
GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
}
ELSE {
GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringAdd[NIL, gargoyleData, worldPt];
GGStatistics.StopInterval[$StartAdd, GGStatistics.GlobalTable[]];
}; -- end StartAdd
StartAddAux: PROC [gargoyleData: GargoyleData, worldPt: Point] RETURNS [newOutline: Outline, success: BOOL] = {
jointSeq: Sequence;
trajUnderCaret: Traj;
jointNum: NAT;
chair: REF ANY;
movingJointSeq: Sequence;
oldOutline: Outline;
outlineD: OutlineDescriptor;
outlineParts: SliceParts;
SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur
IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay
Add a new segment.
[trajUnderCaret, movingJointSeq, success] ← ExtendTrajToMouse[gargoyleData.scene, worldPt, gargoyleData];
IF NOT success THEN RETURN[NIL, FALSE];
oldOutline ← GGOutline.OutlineOfTraj[trajUnderCaret];
newOutline ← GGOutline.OutlineOfTraj[movingJointSeq.traj];
GGAlign.ReplaceObsoleteOutlineTrigger[gargoyleData, oldOutline, newOutline];
Move the trajectory's outline to the overlay plane.
chair ← GGCaret.GetChair[gargoyleData.caret];
outlineD ← NARROW[chair];
[jointNum: jointNum] ← GGOutline.UnpackSimpleDescriptorOld[outlineD];
jointSeq ← GGSequence.CreateJointToJoint[trajUnderCaret, jointNum, jointNum];
gargoyleData.drag.seqInProgress ← jointSeq; -- remember this sequence
outlineParts ← GGOutline.PartsFromSequence[newOutline, jointSeq];
outlineD ← NEW[OutlineDescriptorObj ← [newOutline, outlineParts]];
GGRefresh.MoveToOverlay[outlineD, gargoyleData];
Initialize the transformation and remember the starting position.
gargoyleData.drag.startPoint ← worldPt; -- used in DuringAddMotion
gargoyleData.drag.transform ← ImagerTransformation.Scale[1.0];
}; -- end StartAddAux
DuringAdd: PUBLIC MouseProc = {
DuringAddMotion[gargoyleData, worldPt];
GGStatistics.StartInterval[$AddPaint, GGStatistics.GlobalTable[]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
GGStatistics.StopInterval[$AddPaint, GGStatistics.GlobalTable[]];
}; -- end During Add
DuringAddMotion: PROC [gargoyleData: GargoyleData, worldPt: Point] = {
Map the endpoint and the caret and reposition them.
totalDragVector: Vector;
mapPoint: Point;
feature: FeatureData;
currentObjects: ObjectBag ← NARROW[gargoyleData.hitTest.currentObjectBag];
sceneObjects: TriggerBag ← NARROW[gargoyleData.hitTest.sceneTriggerBag];
[mapPoint, feature] ← GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, currentObjects, sceneObjects, gargoyleData, TRUE];
SetCaretAttractor[gargoyleData, mapPoint, feature, "Adding:"];
totalDragVector ← GGVector.Sub[mapPoint, gargoyleData.drag.startPoint];
gargoyleData.drag.transform ← ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]];
}; -- end DuringAddMotion
EndAdd: PUBLIC MouseProc = {
The dragging is done. Update the endpoint with the current transform.
selSeq: Sequence;
GGStatistics.StartInterval[$EndAdd, GGStatistics.GlobalTable[]];
DuringAddMotion[gargoyleData, worldPt];
selSeq ← gargoyleData.drag.seqInProgress;
GGTraj.TransformSequence[selSeq, gargoyleData.drag.transform];
GGWindow.NewCaretPos[gargoyleData];
GGRefresh.MoveOverlayToBackground[gargoyleData];
gargoyleData.refresh.startBoundBox^ ← selSeq.traj.boundBox^;
gargoyleData.refresh.addedObject ← GGOutline.OutlineOfTraj[selSeq.traj];
GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
GGStatistics.StopInterval[$EndAdd, GGStatistics.GlobalTable[]];
};
AddNewBoxSlice: PROC [from, to: Point, gargoyleData: GargoyleData] RETURNS [sliceD: 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];
gargoyleData.drag.boxInProgress ← sliceD ← GGSlice.MakeBoxSlice[box, corner, GGTransform.Identity[]];
GGObjects.AddSlice[gargoyleData.scene, sliceD.slice, -1];
do no painting yet
};
StartBox: PUBLIC StartProc = {
sliceD: SliceDescriptor;
caretPos: Point;
GGStatistics.StartInterval[$StartBox, GGStatistics.GlobalTable[]];
caretPos ← GGCaret.GetPoint[gargoyleData.caret];
SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur
IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay
sliceD ← AddNewBoxSlice[caretPos, worldPt, gargoyleData];
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGSelect.SelectSlice[slice: sliceD.slice, parts: sliceD.parts, scene: gargoyleData.scene, selectClass: normal];
Move new box to overlay
GGRefresh.MoveToOverlay[sliceD, gargoyleData]; -- slice on overlay to be rubberbanded
Initialize the transformation and remember the starting position.
gargoyleData.drag.startPoint ← worldPt;
gargoyleData.drag.transform ← ImagerTransformation.Scale[1.0];
GGRefresh.SplitBackgroundAndOverlay[gargoyleData, GGBoundBox.emptyBoundBox];
GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
DuringAdd[NIL, gargoyleData, worldPt];
GGStatistics.StopInterval[$StartBox, GGStatistics.GlobalTable[]];
};
EndBox: PUBLIC MouseProc = {
The dragging is done. Update the box slice with the current transform. The box is cached in the gargoyleData.
sliceD: SliceDescriptor ← NARROW[gargoyleData.drag.boxInProgress];
slice: Slice ← sliceD.slice;
slice.class.transform[slice, sliceD.parts, gargoyleData.drag.transform]; -- update the slice
GGRefresh.MoveOverlayToBackground[gargoyleData];
gargoyleData.refresh.startBoundBox^ ← slice.boundBox^; --newly updated by slice.class.transform
gargoyleData.refresh.addedObject ← slice;
GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, gargoyleData: gargoyleData, 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 gargoyleData. Then, select the entire box and call AreaSelectNewAndDelete.
sliceD: SliceDescriptor ← NARROW[gargoyleData.drag.boxInProgress];
slice: Slice ← sliceD.slice;
slice.class.transform[slice, sliceD.parts, gargoyleData.drag.transform]; -- update the slice
GGRefresh.MoveOverlayToBackground[gargoyleData];
GGSelect.SelectSlice[slice: slice, parts: slice.class.newParts[slice, NIL, topLevel], scene: gargoyleData.scene, selectClass: normal];
GGEvent.AreaSelectNewAndDelete[NIL, gargoyleData];
gargoyleData.refresh.startBoundBox^ ← slice.boundBox^; --newly updated by slice.class.transform
GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
FixupAbortedBox: PROC [gargoyleData: GargoyleData] = {
This routine is called instead of EndBox. Find the box that is being added and delete it.
sliceD: SliceDescriptor ← NARROW[gargoyleData.drag.boxInProgress];
slice: Slice ← sliceD.slice;
repaintBox: BoundBox ← GGCaret.BoundBoxOfCaret[gargoyleData.caret, gargoyleData]; -- start with caret
GGBoundBox.EnlargeByBox[repaintBox, slice.boundBox]; -- repaint deleted box slice
GGSelect.DeselectEntityAllClasses[slice, gargoyleData.scene];
GGSlice.DeleteSlice[gargoyleData.scene, slice];
gargoyleData.refresh.startBoundBox^ ← repaintBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
InitStats: PROC [] = {
interval: GGStatistics.Interval;
bags, paint, background: GGStatistics.Interval;
bags ← GGStatistics.CreateInterval[$SetBags];
paint ← GGStatistics.CreateInterval[$AddPaint];
background ← GGStatistics.CreateInterval[$StoreBackground];
interval ← GGStatistics.CreateInterval[$StartAdd, LIST[bags, paint, background]];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
interval ← GGStatistics.CreateInterval[$EndAdd];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
interval ← GGStatistics.CreateInterval[$StartCaretPos];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
interval ← GGStatistics.CreateInterval[$DuringCaretPos];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
interval ← GGStatistics.CreateInterval[$EndCaretPos];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
interval ← GGStatistics.CreateInterval[$EndMotion];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
interval ← GGStatistics.CreateInterval[$StartBox];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
interval ← GGStatistics.CreateInterval[$HandleGuarded];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
};
InitStats[];
END.