<> <> <> <> <> <<>> 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; <> StartProc: TYPE = GGMouseEvent.StartProc; <> Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; EasyAbort: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { <> 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]; <> { 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] = { <> [] _ 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]; }; <<>> <> HandleMouseless: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { <> 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] = { <> 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]; < priority1 _ GGScene.SlicePriority[scene, outline];>> ENDCASE => ERROR; WITH ref2 SELECT FROM slice: Slice => priority2 _ GGScene.SlicePriority[scene, slice]; < 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] = { <> <> 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"]; }; <<>> <> StartCaretPos: PUBLIC StartProc = { <> CodeTimer.StartInt[$StartCaretPos, $Gargoyle]; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; SaveSavedState[ggData]; DuringCaretPos[NIL, ggData, worldPt]; CodeTimer.StopInt[$StartCaretPos, $Gargoyle]; }; DuringCaretPos: PUBLIC MouseProc = { <> 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]; <> 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 _ ""] = { <> <> <> IF feature = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL] ELSE { SELECT feature.type FROM < {>> <> <> <> <> <<};>> 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 => { <> 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 < 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 < 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 < 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]] ]; }; <<>> <> 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; <> 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 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; } <> 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; <> newSlices _ SortNewEntities[scene, newSlices]; <> 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]; <> RETURN[NIL, FALSE]; }; END; }; StartCopyAndDrag: PUBLIC StartProc = { <> 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]; <> IF NOT success THEN RETURN[FALSE]; DuringDrag[NIL, ggData, worldPt]; }; <<>> <> <<>> 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] = { <> movingBox: BoundBox; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay GGRefresh.MoveAllSelectedToOverlay[ggData, normal]; ggData.drag.startPoint _ GGCaret.GetPoint[ggData.caret]; <> GGCaret.SetAttractor[ggData.caret, ggData.drag.startPoint, NIL]; <> ggData.drag.transform _ ImagerTransformation.Scale[1.0]; <> movingBox _ GGBoundBox.BoundBoxOfMoving[ggData.scene]; GGBoundBox.EnlargeByBox[bBox: movingBox, by: startBox]; ggData.refresh.startBoundBox^ _ movingBox^; [] _ GGAlign.StaticToDynamicBags[ggData]; <> <> 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]; <> GGCaret.NoAttractor[ggData.caret]; -- is this really needed? Bier, March 26, 1987 <> ggData.drag.transform _ ImagerTransformation.Scale[1.0]; <> movingBox _ GGBoundBox.BoundBoxOfMoving[ggData.scene]; ggData.refresh.startBoundBox^ _ movingBox^; IF NOT backgroundOK THEN GGRefresh.SplitBackgroundAndOverlay[ggData, movingBox]; repaintNeeded _ GGAlign.StaticToDynamicBags[ggData]; <> 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 = { <> CodeTimer.StartInt[$ContinueAdd, $Gargoyle]; TransformObjectsAfterMove[ggData.scene, ggData.drag.transform]; GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; [] _ GGAlign.DynamicToStaticBags[ggData]; <> CodeTimer.StopInt[$ContinueAdd, $Gargoyle]; success _ StartAdd[LIST[$ContinueAdd], ggData, worldPt]; }; EndMotion: PUBLIC MouseProc = { <> 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]; }; <> <> <> <> <> <> <> <<[success, partType, traj, ----, jointNum] _ GGOutline.UnpackSimpleDescriptorOld[outlineD];>> <> <<};>> <<>> 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]; <> 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; } <> 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 { <> 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 = { <> 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]; <> 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]; <> }; 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]; <> }; 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]; <> GGRefresh.MoveToOverlay[sliceD, ggData]; -- slice on overlay to be rubberbanded <> 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 = { <> 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]; <> GGRefresh.MoveToOverlay[sliceD, ggData]; -- slice on overlay to be rubberbanded <> 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 = { <> 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 = { <> 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] = { <> 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.