<> <> <> <> <> <> <> <<>> DIRECTORY Atom, CodeTimer, Feedback, FeedbackTypes, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGDragTypes, GGEmbedTypes, GGEvent, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGRefreshTypes, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUIUtility, GGUserInput, GGUserProfile, GGWindow, Imager, ImagerTransformation, Menus, RealFns, RefTab, Rope, Vectors2d; GGMouseEventImplA: CEDAR PROGRAM IMPORTS Atom, CodeTimer, Feedback, GGAlign, GGBoundBox, GGCaret, GGEvent, GGHistory, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUIUtility, GGUserInput, GGUserProfile, GGWindow, Imager, ImagerTransformation, RealFns, RefTab, Rope, Vectors2d EXPORTS GGMouseEvent, GGInterfaceTypes = BEGIN EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; AlignBag: TYPE = GGInterfaceTypes.AlignBag; AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; DefaultData: TYPE = GGModelTypes.DefaultData; FeatureData: TYPE = GGModelTypes.FeatureData; MsgRouter: TYPE = FeedbackTypes.MsgRouter; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Joint: TYPE = GGModelTypes.Joint; MouseButton: TYPE = Menus.MouseButton; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajParts: TYPE = GGModelTypes.TrajParts; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGAlign.TriggerBag; UserInputProc: TYPE = GGUserInput.UserInputProc; 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] = { <> GGScene.RestoreSelections[ggData.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: SliceDescriptor _ 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: PUBLIC PROC [ggData: GGData] = { GGRefresh.MoveOverlayToBackground[ggData]; Feedback.Append[ggData.router, end, $Feedback, ". . . Aborted."]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE]; }; InitializeFSM: PUBLIC PROC [ggData: GGData] = { ggData.state _ $None; ggData.mouseMode _ $None; ggData.drag.guardedMode _ default; }; ResetMouseMachinery: PUBLIC PROC [ggData: GGData] = { ggData.mouseMode _ $None; ggData.state _ $None; ggData.drag.guardedMode _ default; GGRefresh.MoveOverlayToBackground[ggData]; }; SaveSavedState: PUBLIC PROC [ggData: GGData] = { GGScene.SaveSelections[ggData.scene]; GGWindow.SaveCaretPos[ggData]; GGCaret.Copy[from: ggData.caret, to: ggData.drag.savedCaret]; }; <<>> <> HandleMouseless: PUBLIC UserInputProc = { <> event _ LIST[event.first, NEW[Point _ [0.0, 0.0]]]; HandleMouse[ggData, event]; }; HandleMouse: PUBLIC UserInputProc = { HandleMouseAux[ggData, event]; }; HandleMouseAux: PROC [ggData: GGData, event: LIST OF REF, guardP: GuardPredicate _ default] = { <> mE: ModeEntry; IF ggData.mouseMode # $None THEN { point: Point; IF event.first = $UnGuarded THEN { ggData.drag.nextGuardedMode _ unguarded; event _ event.rest; }; point _ NARROW[event.rest.first, REF Point]^; mE _ FindMode[ggData.mouseMode]; IF mE = NIL THEN SIGNAL Problem[msg: "Unimplemented Mode"] ELSE { IF ggData.drag.guardedMode = guarded THEN HandleGuarded[mE.startProc, mE.duringProc, mE.endProc, mE.abortProc, mE.continueProc, event, ggData, point] ELSE HandleUnGuarded[mE.startProc, mE.duringProc, mE.endProc, mE.abortProc, event, ggData, point]; } } ELSE { SetMouseMode: PROC [mouseMode: ATOM] = { ggData.mouseMode _ mouseMode; IF guardP = default THEN { mE _ FindMode[mouseMode]; IF mE = NIL THEN SIGNAL Problem[msg: "Starting unimplemented mode"] ELSE ggData.drag.guardedMode _ mE.guardP; } ELSE ggData.drag.guardedMode _ guardP; }; atom: ATOM _ NARROW[event.first]; SELECT atom FROM $UnGuarded => { ggData.drag.guardedMode _ unguarded; HandleMouseAux[ggData, event.rest, unguarded]; -- strip UnGuarded off the front. execute the actual command, setting guardP to "unguarded" }; $StartCaretPos => { SetMouseMode[$CaretPos]; HandleMouse[ggData, event]; }; $StartAdd => { SetMouseMode[$Add]; HandleMouse[ggData, event]; }; $StartBezier => { SetMouseMode[$AddBezier]; HandleMouse[ggData, event]; }; $StartBox => { SetMouseMode[$Box]; HandleMouse[ggData, event]; }; $StartSelectWithBox => { SetMouseMode[$SelectWithBox]; HandleMouse[ggData, event]; }; $StartDrag => { SetMouseMode[$Drag]; HandleMouse[ggData, event]; }; $StartCopyAndDrag => { SetMouseMode[$CopyAndDrag]; HandleMouse[ggData, event]; }; $StartRotate => { SetMouseMode[$Rotate]; HandleMouse[ggData, event]; }; $StartScale => { SetMouseMode[$Scale]; HandleMouse[ggData, event]; }; $StartSixPoint => { SetMouseMode[$SixPoint]; HandleMouse[ggData, event]; }; $StartSelectJoint => { SetMouseMode[$SelectJoint]; HandleMouse[ggData, event]; }; $StartExtSelectJoint => { SetMouseMode[$ExtSelectJoint]; HandleMouse[ggData, event]; }; $StartSelectSegment => { SetMouseMode[$SelectSegment]; HandleMouse[ggData, event]; }; $StartExtSelectSegment => { SetMouseMode[$ExtSelectSegment]; HandleMouse[ggData, event]; }; $StartSelectTrajectory => { SetMouseMode[$SelectTrajectory]; HandleMouse[ggData, event]; }; $StartExtSelectTrajectory => { SetMouseMode[$ExtSelectTrajectory]; HandleMouse[ggData, event]; }; $StartSelectTopLevel => { SetMouseMode[$SelectTopLevel]; HandleMouse[ggData, event]; }; $StartExtSelectTopLevel => { SetMouseMode[$ExtSelectTopLevel]; HandleMouse[ggData, event]; }; $StartExtendSelection => { SetMouseMode[$ExtendSelection]; HandleMouse[ggData, event]; }; $StartDeselectJoint => { SetMouseMode[$DeselectJoint]; HandleMouse[ggData, event]; }; $StartDeselectSegment => { SetMouseMode[$DeselectSegment]; HandleMouse[ggData, event]; }; $StartDeselectTrajectory => { SetMouseMode[$DeselectTrajectory]; HandleMouse[ggData, event]; }; $StartDeselectTopLevel => { SetMouseMode[$DeselectTopLevel]; HandleMouse[ggData, event]; }; ENDCASE => RETURN; -- ignore other actions }; }; <<>> 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 = $AddBezier AND state = $StartBezier) 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)]; }; 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]; }; 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; HandleMouseAux[ggData, input, ggData.drag.nextGuardedMode]; }; 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; HandleMouseAux[ggData, input, ggData.drag.nextGuardedMode]; }; }; 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; HandleMouseAux[ggData, input, ggData.drag.nextGuardedMode]; }; 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 = { <> resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; CodeTimer.StartInt[$StartCaretPos, $Gargoyle]; SaveSavedState[ggData]; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; ggData.drag.startPoint _ GGCaret.GetPoint[ggData.caret]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE, selectedCPs: TRUE, hotCPs: TRUE]; [resultPoint, normal, feature, hitData] _ GGMultiGravity.Map[worldPt, ggData.hitTest.t, ggData.hitTest.alignBag, ggData.hitTest.sceneBag, ggData, TRUE]; SetCaretAttractor[ggData, resultPoint, normal, feature, hitData]; -- move caret to feature point GGWindow.RestoreScreenAndInvariants[paintAction: $StartCaretPos, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; -- show caret in new position CodeTimer.StopInt[$StartCaretPos, $Gargoyle]; }; DuringCaretPos: PUBLIC MouseProc = { <> resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; CodeTimer.StartInt[$DuringCaretPos, $Gargoyle]; [resultPoint, normal, feature, hitData] _ GGMultiGravity.Map[worldPt, ggData.hitTest.t, ggData.hitTest.alignBag, ggData.hitTest.sceneBag, ggData, TRUE]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE]; SetCaretAttractor[ggData, resultPoint, normal, feature, hitData]; -- move caret to feature point GGWindow.RestoreScreenAndInvariants[paintAction: $DuringCaretPos, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; -- show caret in new position CodeTimer.StopInt[$DuringCaretPos, $Gargoyle]; }; -- end of DuringCaretPos EndCaretPos: PUBLIC MouseProc = { resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; currentObjects: AlignBag _ NARROW[ggData.hitTest.alignBag]; sceneObjects: TriggerBag _ NARROW[ggData.hitTest.sceneBag]; CodeTimer.StartInt[$EndCaretPos, $Gargoyle]; [resultPoint, normal, feature, hitData] _ GGMultiGravity.Map[worldPt, ggData.hitTest.t, currentObjects, sceneObjects, ggData, TRUE]; <> SetCaretAttractor[ggData, resultPoint, normal, feature, hitData, "Final"]; -- move caret to feature point GGWindow.NewCaretPos[ggData]; GGCaret.SitOn[ggData.caret, NIL]; -- subsequent Add operations will start a NEW trajectory. GGRefresh.NullStartBox[ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $CaretMoved, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; CodeTimer.StopInt[$EndCaretPos, $Gargoyle]; }; -- end EndCaretPos SetCaretAttractor: PROC [ggData: GGData, mapPoint: Point, normal: Vector, feature: FeatureData, hitData: REF ANY, action: Rope.ROPE _ ""] = { <> <> <> scale: REAL _ 1.0; IF feature = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, normal, NIL] ELSE { SELECT feature.type FROM slice => { partsD: SliceDescriptor; slideD: SliceDescriptor _ NARROW[feature.shape]; partsD _ GGSliceOps.NewParts[slideD.slice, hitData, literal]; GGCaret.SetAttractor[ggData.caret, mapPoint, normal, 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, normal, alignPoint] ELSE { attractorD: SliceDescriptor; IF curve1.type = slice THEN { WITH curve1.shape SELECT FROM sliceD: SliceDescriptor => attractorD _ GGSliceOps.NewParts[sliceD.slice, hitData, literal]; ENDCASE => ERROR; } ELSE IF curve2.type = slice THEN { WITH curve2.shape SELECT FROM sliceD: SliceDescriptor => attractorD _ GGSliceOps.NewParts[sliceD.slice, hitData, literal]; ENDCASE => ERROR; } ELSE ERROR; GGCaret.SetAttractor[ggData.caret, mapPoint, normal, attractorD] }; }; midpoint => { IF hitData = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, normal, NIL] ELSE { attractorD: SliceDescriptor; alignPoint: AlignmentPoint _ NARROW[feature.shape]; curveFeature: FeatureData _ alignPoint.curve1; WITH curveFeature.shape SELECT FROM sliceD: SliceDescriptor => attractorD _ GGSliceOps.NewParts[sliceD.slice, hitData, literal]; ENDCASE => ERROR; GGCaret.SetAttractor[ggData.caret, mapPoint, normal, attractorD]; }; }; ENDCASE => GGCaret.SetAttractor[ggData.caret, mapPoint, normal, feature.shape]; }; scale _ GGState.GetScaleUnit[ggData]; IF action = NIL THEN Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "Caret on %g at [%g, %g]. Delta [%g, %g].", [rope[GGUIUtility.DescribeFeature[feature, hitData, ggData]]], [real[mapPoint.x/scale]], [real[mapPoint.y/scale]], [real[(mapPoint.x-ggData.drag.startPoint.x)/scale]], [real[(mapPoint.y-ggData.drag.startPoint.y)/scale]] ] ELSE { Feedback.PutF[ggData.router, begin, $DuringMouse, "%g caret on %g at [%g, %g]. ", [rope[action]], [rope[GGUIUtility.DescribeFeature[feature, hitData, ggData]]], [real[mapPoint.x/scale]], [real[mapPoint.y/scale]] ]; Feedback.PutF[ggData.router, end, $DuringMouse, "Delta [%g, %g].", [real[(mapPoint.x-ggData.drag.startPoint.x)/scale]], [real[(mapPoint.y-ggData.drag.startPoint.y)/scale]] ]; }; }; <<>> <> UpdateSceneForCopy: PROC [scene: Scene, router: MsgRouter, ggData: GGData] RETURNS [newSlices: LIST OF Slice, success: BOOL _ TRUE] = { newSlices _ GGScene.CopySelectedParts[fromScene: scene, toScene: scene]; GGState.SetExtendMode[ggData, topLevel]; <> IF newSlices = NIL THEN RETURN ELSE { list: LIST OF Slice; sliceToExtend: Slice; extender: SliceDescriptor; FOR list _ newSlices, list.rest UNTIL list.rest = NIL DO ENDLOOP; sliceToExtend _ list.first; extender _ GGState.GetSliceToExtend[ggData]; IF extender#NIL AND extender.slice#sliceToExtend THEN { sliceD: SliceDescriptor _ GGSliceOps.NewParts[sliceToExtend, NIL, slice]; GGState.SetSliceToExtend[ggData, sliceD]; }; }; }; StartCopyAndDrag: PUBLIC StartProc = { <> opName: Rope.ROPE = "Copy and drag"; sliceList: LIST OF Slice; GGHistory.NewCapture[opName, ggData]; -- capture scene BEFORE UPDATE SaveSavedState[ggData]; -- must do this before any possible aborts occur [sliceList, success] _ UpdateSceneForCopy[ggData.scene, ggData.router, ggData]; -- adds new shapes and sets slice to extend IF NOT success THEN RETURN; GGCaret.SitOn[ggData.caret, NIL]; -- avoids extending old outline if Add follows CopyAndDrag GGAlign.UpdateBagsForNewSlices[sliceList, ggData]; success _ StartMotion[ggData: ggData, opName: opName, bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE, newCurrent: FALSE]; -- put moving objects on the overlay plane. backgroundOK is TRUE because we are strictly adding. IF NOT success THEN RETURN[FALSE]; DragUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartCopyAndDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; <<>> <> <<>> TransformObjectsAfterMove: PUBLIC PROC [scene: Scene, transform: ImagerTransformation.Transformation, editConstraints: GGModelTypes.EditConstraints, history: HistoryEvent] = { DoTransform: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSliceOps.Transform[sliceD.slice, sliceD.parts, transform, editConstraints, history]; }; [] _ GGScene.WalkSelectedSlices[scene, first, DoTransform, normal]; }; StartMotion: PUBLIC PROC [ggData: GGData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, saveState: BOOL _ TRUE, needAnchor: BOOL _ FALSE, backgroundOK: BOOL _ FALSE, newCurrent: BOOL _ TRUE] RETURNS [success: BOOL _ TRUE] = { <> EnsureAnchor: PROC RETURNS [anySelections: BOOL _ TRUE] = { IF needAnchor THEN { IF GGCaret.Exists[ggData.anchor] THEN { ggData.drag.anchorPoint _ GGCaret.GetPoint[ggData.anchor]; ggData.drag.anchorNormal _ GGCaret.GetNormal[ggData.anchor]; } ELSE { tightBox: BoundBox _ GGScene.TightBoxOfSelected[ggData.scene, normal]; success: BOOL _ TRUE; [ggData.drag.anchorPoint, success] _ GGBoundBox.Centroid[tightBox]; IF NOT success THEN RETURN[FALSE]; ggData.drag.anchorNormal _ [0, -1]; }; }; }; CodeTimer.StartInt[$StartMotion, $Gargoyle]; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay IF saveState THEN SaveSavedState[ggData]; -- must do this before any possible aborts occur BEGIN IF GGSelect.NoSelections[ggData.scene, normal] OR NOT EnsureAnchor[] THEN GOTO NoSelections; IF NOT GGCaret.Exists[ggData.caret] THEN GOTO NoCaret; IF newCurrent THEN [] _ GGHistory.NewCurrent[Rope.Concat["Motion: ", opName], ggData]; GGRefresh.MoveAllSelectedToOverlay[ggData, normal]; ggData.drag.startPoint _ GGCaret.GetPoint[ggData.caret]; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; ggData.embed.scaleCaretActive _ FALSE; ggData.embed.scaleCaretCurrent _ [0,0]; <> <> IF NOT backgroundOK THEN { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE]; GGWindow.RestoreScreenAndInvariants[paintAction: $RepairBackground, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; GGCaret.NoAttractor[ggData.caret]; -- is this really needed? Bier, March 26, 1987 GGAlign.StaticToDynamicBags[ggData, TRUE]; -- bags and foreground bitmap GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, alignments: TRUE, attractor: TRUE, caret: TRUE]; <> EXITS NoSelections => { Feedback.PutF[ggData.router, oneLiner, $Complaint, "Motion failed: select some objects to %g", [rope[opName]]]; success _ FALSE; }; NoCaret => { Feedback.PutF[ggData.router, oneLiner, $Complaint, "Motion failed: Caret needed to %g", [rope[opName]]]; success _ FALSE; }; END; CodeTimer.StopInt[$StartMotion, $Gargoyle]; }; -- end StartMotion ContinueMotion: PUBLIC PROC [ggData: GGData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, startBox: BoundBox _ NIL] RETURNS [success: BOOL _ TRUE] = { <> movingBox: BoundBox; normal: Vector; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay GGRefresh.MoveAllSelectedToOverlay[ggData, normal]; ggData.drag.startPoint _ GGCaret.GetPoint[ggData.caret]; normal _ GGCaret.GetNormal[ggData.caret]; GGCaret.SetAttractor[ggData.caret, ggData.drag.startPoint, normal, NIL]; -- place caret. NIL attractor ggData.drag.transform _ ImagerTransformation.Scale[1.0]; GGAlign.StaticToDynamicBags[ggData, TRUE]; GGRefresh.UpdateForeground[ggData]; -- augmenting is always adequate GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, alignments: TRUE, attractor: TRUE, caret: TRUE]; <> }; StartDrag: PUBLIC StartProc = { startSuccess: BOOL; CodeTimer.StartInt[$StartDrag, $Gargoyle]; startSuccess _ StartMotion[ggData, "drag", $Drag, worldPt]; IF NOT startSuccess THEN RETURN[FALSE]; CodeTimer.StopInt[$StartDrag, $Gargoyle]; DragUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; StartRotate: PUBLIC StartProc = { startSuccess: BOOL; startSuccess _ StartMotion[ggData, "rotate", $Drag, worldPt, TRUE, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; RotateUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; StartScale: PUBLIC StartProc = { originalVector: Vector; startSuccess: BOOL; startSuccess _ StartMotion[ggData, "scale", $Drag, worldPt, TRUE, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; originalVector _ Vectors2d.Sub[ggData.drag.startPoint, ggData.drag.anchorPoint]; IF originalVector = [0.0, 0.0] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Scale failed: move caret away from anchor before scaling"]; RETURN[FALSE]; }; ScaleUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; StartSixPoint: PUBLIC StartProc = { OPEN Vectors2d; epsilon: REAL = 1.0e-3; p0, p1, p2: Point; crossProduct: REAL; anchor: Caret _ ggData.anchor; startSuccess: BOOL; startSuccess _ StartMotion[ggData, "six point", $Drag, worldPt, TRUE, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; p0 _ GGCaret.GetPoint[anchor]; p2 _ ggData.drag.startPoint; <> p1 _ Add[p0, VectorPlusAngle[GGCaret.GetNormal[anchor], 90.0]]; -- oriented anchor method crossProduct _ CrossProductScalar[Sub[p1,p0], Sub[p2, p0]]; IF ABS[crossProduct] < epsilon THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Sixpoint failed: move caret away from anchor before six point"]; RETURN[FALSE]; }; SixPointUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; <<>> DragTheCaret: PUBLIC PROC [worldPt: Point, ggData: GGData, opName: Rope.ROPE] RETURNS [mapPoint: Point] = { feature: FeatureData; normal: Vector; hitData: REF ANY; [mapPoint, normal, feature, hitData] _ GGMultiGravity.Map[worldPt, ggData.hitTest.t, ggData.hitTest.alignBag, ggData.hitTest.sceneBag, ggData, TRUE]; SetCaretAttractor[ggData, mapPoint, normal, feature, hitData, opName]; }; DuringDrag: PUBLIC MouseProc = { CodeTimer.StartInt[$DuringDrag, $Gargoyle]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, caret: TRUE, attractor: TRUE]; DragUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; CodeTimer.StopInt[$DuringDrag, $Gargoyle]; }; -- end DuringDrag DragUpdateCaretAndTransform: PROC [worldPt: Point, ggData: GGData] = { mapPoint: Point _ DragTheCaret[worldPt, ggData, "Dragging:"]; totalDragVector: Vector _ Vectors2d.Sub[mapPoint, ggData.drag.startPoint]; ggData.drag.transform _ ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]]; IF ggData.embed.scaleCaretActive THEN { epsilon: REAL = 1.0e-3; ratio: REAL; scaleCaretStart: Vector _ ggData.embed.scaleCaretStart; scaleCaretCurrent: Vector _ ggData.embed.scaleCaretCurrent; scaleT: ImagerTransformation.Transformation; IF RealFns.AlmostZero[scaleCaretCurrent.x, -10] AND RealFns.AlmostZero[scaleCaretCurrent.y, -10] THEN RETURN; -- can't scale to zero IF RealFns.AlmostZero[scaleCaretStart.x, -10] AND RealFns.AlmostZero[scaleCaretStart.y, -10] THEN RETURN; -- can't scale by infinity ratio _ Vectors2d.Magnitude[scaleCaretCurrent]/Vectors2d.Magnitude[scaleCaretStart]; scaleT _ GGTransform.ScaleAboutPoint[mapPoint, ratio]; ggData.drag.transform _ ImagerTransformation.Concat[ggData.drag.transform, scaleT]; }; }; DuringRotate: PUBLIC MouseProc = { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, caret: TRUE, attractor: TRUE]; RotateUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; RotateUpdateCaretAndTransform: PROC [worldPt: Point, ggData: GGData] = { anchorPoint: Point _ ggData.drag.anchorPoint; mapPoint: Point _ DragTheCaret[worldPt, ggData, "Rotating:"]; originalVector: Vector _ Vectors2d.Sub[ggData.drag.startPoint, anchorPoint]; newVector: Vector _ Vectors2d.Sub[mapPoint, anchorPoint]; degrees: REAL _ Vectors2d.AngleCCWBetweenVectors[originalVector, newVector]; ggData.drag.transform _ GGTransform.RotateAboutPoint[anchorPoint, degrees]; }; DuringScale: PUBLIC MouseProc = { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, caret: TRUE, attractor: TRUE]; ScaleUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; ScaleUpdateCaretAndTransform: PROC [worldPt: Point, ggData: GGData] = { anchorPoint: Point _ ggData.drag.anchorPoint; epsilon: REAL = 1.0e-3; mapPoint: Point _ DragTheCaret[worldPt, ggData, "Scaling:"]; originalVector: Vector _ Vectors2d.Sub[ggData.drag.startPoint, anchorPoint]; newVector: Vector _ Vectors2d.Sub[mapPoint, anchorPoint]; ratio: REAL; 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]; }; DuringSixPoint: PUBLIC MouseProc = { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, caret: TRUE, attractor: TRUE]; SixPointUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; SixPointUpdateCaretAndTransform: PROC [worldPt: Point, ggData: GGData] = { OPEN Vectors2d; epsilon: REAL = 1.0e-3; pts: ARRAY [0..5] OF Point; crossProduct: REAL; mapPoint: Point; pts[0] _ pts[3] _ ggData.drag.anchorPoint; pts[2] _ ggData.drag.startPoint; <> pts[1] _ pts[4] _ Add[pts[0], VectorPlusAngle[ggData.drag.anchorNormal, 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]; }; ContinueAdd: PUBLIC StartProc = { <> CodeTimer.StartInt[$ContinueAdd, $Gargoyle]; TransformObjectsAfterMove[ggData.scene, ggData.drag.transform, ggData.drag.editConstraints, NIL]; -- don't try to record undoable transforms here. A capture event is the current event. GGHistory.PushCurrent[ggData]; -- push captured history event onto history list GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; [] _ GGAlign.DynamicToStaticBags[ggData]; <> CodeTimer.StopInt[$ContinueAdd, $Gargoyle]; success _ StartAdd[LIST[$ContinueAdd], ggData, worldPt]; }; EndMotion: PUBLIC MouseProc = { <> CodeTimer.StartInt[$EndMotion, $Gargoyle]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, movingParts: TRUE, caret: TRUE, attractor: TRUE, alignments: TRUE]; TransformObjectsAfterMove[ggData.scene, ggData.drag.transform, ggData.drag.editConstraints, GGHistory.GetCurrent[ggData].event]; GGHistory.PushCurrent[ggData]; -- put new transforms on history list GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; GGAlign.DynamicToStaticBags[ggData, TRUE]; GGWindow.RestoreScreenAndInvariants[paintAction: $EndMotion, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$EndMotion, $Gargoyle]; }; EndMotionNoHistory: PUBLIC MouseProc = { <> CodeTimer.StartInt[$EndMotionNoHistory, $Gargoyle]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, movingParts: TRUE, caret: TRUE, attractor: TRUE, alignments: TRUE]; TransformObjectsAfterMove[ggData.scene, ggData.drag.transform, ggData.drag.editConstraints, NIL]; GGHistory.PushCurrent[ggData]; -- put scene captured by Start code on history GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; GGAlign.DynamicToStaticBags[ggData, TRUE]; GGWindow.RestoreScreenAndInvariants[paintAction: $EndMotion, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$EndMotionNoHistory, $Gargoyle]; }; <> SafelyGetCaretTraj: PUBLIC PROC [caret: Caret] RETURNS [chair: SliceDescriptor, traj: Traj, jointNum: NAT] = { success: BOOL _ FALSE; chairD: SliceDescriptor; tJointNum: INT; chair _ GGCaret.GetChair[caret]; [success, chairD, tJointNum] _ GGSliceOps.UnpackJoint[chair]; IF NOT success THEN ERROR Problem[msg: "Attempt to extend a trajectory without the caret on its end"]; jointNum _ tJointNum; traj _ chairD.slice; }; UpdateSceneForAdd: PROC [scene: Scene, worldPt: Point, caret: Caret, defaults: DefaultData] RETURNS [oldTraj, newTraj: Traj, trajEnd: TrajEnd, newParent: Slice] = { caretPoint: Point; jointNum: NAT; newSeg, extendSeg: Segment; chair: SliceDescriptor; success: BOOL; caretPoint _ GGCaret.GetPoint[caret]; newSeg _ GGSegment.MakeLine[worldPt, caretPoint, NIL]; <> IF GGCaret.SittingOnEnd[caret] THEN { [chair, 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; newParent _ chair.slice; } <> ELSE { oldTraj _ NIL; trajEnd _ hi; newTraj _ GGTraj.CreateTraj[caretPoint]; GGSliceOps.SetStrokeJoint[newTraj, NIL, defaults.strokeJoint, NIL]; GGSegment.SetDefaults[newSeg, defaults]; success _ GGTraj.AddSegment[newTraj, trajEnd, newSeg, hi]; IF NOT success THEN RETURN; newParent _ GGOutline.CreateOutline[newTraj, defaults.fillColor]; GGScene.AddSlice[scene, newParent, -1]; }; }; UpdateSelectionsForAdd: PUBLIC PROC [scene: Scene, oldTraj, newTraj: Traj, trajEnd: TrajEnd] RETURNS [newNormal, newHot: SliceDescriptor] = { <> jointNum: NAT _ SELECT trajEnd FROM lo => 0, hi => GGTraj.HiJoint[newTraj], ENDCASE => ERROR; <> newHot _ IF oldTraj # NIL THEN GGSelect.ReselectTraj[newTraj, trajEnd, scene, TRUE] ELSE GGSlice.DescriptorFromParts[newTraj, GGSequence.CreateEmpty[NARROW[newTraj.data]]]; -- creating from scratch IF newHot # NIL THEN newHot _ GGParent.TopLevelDescriptorFromChildDescriptor[newHot]; <> newNormal _ GGSlice.DescriptorFromParts[newTraj, GGSequence.CreateFromJoint[NARROW[newTraj.data], jointNum]]; newNormal _ GGParent.TopLevelDescriptorFromChildDescriptor[newNormal]; GGSelect.DeselectAll[scene, normal]; GGSelect.SelectSlice[newNormal, scene, normal]; }; UpdateCaretForAdd: PUBLIC PROC [caret: Caret, newOutline: Slice, newNormal: Sequence, worldPt: Point] = { jointD: SliceDescriptor _ GGParent.TopLevelDescriptorFromChildDescriptor[newNormal]; GGCaret.SetAttractor[caret, worldPt, [0,-1], NIL]; -- Can have better orientation. GGCaret.SitOn[caret, jointD]; }; StartAdd: PUBLIC StartProc = { <> IF GGCaret.Exists[ggData.caret] THEN { opName: Rope.ROPE = "Add line to"; continue: BOOL _ FALSE; caret: Caret; oldTraj, newTraj: Slice; ancestor, oldOutline: Slice; trajEnd: TrajEnd; newNormalD: SliceDescriptor; <> CodeTimer.StartInt[$StartAdd, $Gargoyle]; continue _ NARROW[input.first, ATOM] = $ContinueAdd; GGHistory.NewCapture[opName, ggData ]; -- capture scene BEFORE UPDATE caret _ ggData.caret; SaveSavedState[ggData]; [oldTraj, newTraj, trajEnd, ancestor] _ UpdateSceneForAdd[ggData.scene, worldPt, caret, ggData.defaults]; oldOutline _ IF oldTraj=NIL THEN NIL ELSE GGParent.GetTopLevelAncestor[oldTraj]; [newNormalD, ----] _ UpdateSelectionsForAdd[ggData.scene, oldTraj, newTraj, trajEnd]; UpdateCaretForAdd[caret, ancestor, newNormalD, worldPt]; [] _ GGAlign.UpdateBagsForAdd[oldOutline, newNormalD, trajEnd, ggData]; IF continue THEN { success _ ContinueMotion[ggData: ggData, opName: opName, bagType: $Drag, worldPt: worldPt, startBox: NIL]; DragUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; } ELSE { success _ StartMotion[ggData: ggData, opName: opName, bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE, newCurrent: FALSE]; <> DragUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; IF NOT success THEN RETURN[FALSE]; CodeTimer.StopInt[$StartAdd, $Gargoyle]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Start add failed: caret required for adding new line"]; }; -- end StartAdd AddNewBoxSlice: PROC [from, to: Point, ggData: GGData, withDefaults: BOOL _ TRUE] RETURNS [sliceD: 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]; sliceD _ ggData.drag.boxInProgress _ GGSlice.MakeBoxSlice[box, corner, GGTransform.Identity[]]; IF withDefaults THEN GGSliceOps.SetDefaults[sliceD.slice, NIL, ggData.defaults, NIL]; IF GGUserProfile.GetNewBoxesUnfilled[] THEN GGSliceOps.SetFillColor[sliceD.slice, NIL, NIL, $Set, NIL]; <> GGScene.AddSlice[ggData.scene, sliceD.slice, -1]; }; StartSelectWithBox: PUBLIC StartProc = { IF GGCaret.Exists[ggData.caret] THEN { boxSlideD: SliceDescriptor; caretPos: Point; CodeTimer.StartInt[$StartSelectWithBox, $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 <> ggData.drag.startPoint _ worldPt; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE]; boxSlideD _ AddNewBoxSlice[caretPos, worldPt, ggData, FALSE]; GGSliceOps.SetFillColor[boxSlideD.slice, NIL, NIL, $Set, NIL]; GGSliceOps.SetStrokeColor[boxSlideD.slice, NIL, Imager.black, $Set, NIL]; [] _ GGSliceOps.SetStrokeWidth[boxSlideD.slice, NIL, 1.0, NIL]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectSlice[boxSlideD, ggData.scene, normal]; <> GGRefresh.MoveToOverlay[boxSlideD, ggData]; -- box on overlay to be rubberbanded GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; DuringDrag[NIL, ggData, worldPt]; CodeTimer.StopInt[$StartSelectWithBox, $Gargoyle]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Select with box failed: caret required for select with box"]; }; EndSelectWithBox: PUBLIC MouseProc = { <> sliceD: SliceDescriptor _ NARROW[ggData.drag.boxInProgress]; slice: Slice _ sliceD.slice; box: BoundBox; CodeTimer.StartInt[$EndSelectWithBox, $Gargoyle]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, movingParts: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE]; GGSliceOps.Transform[sliceD.slice, sliceD.parts, ggData.drag.transform, ggData.drag.editConstraints, NIL]; GGRefresh.MoveOverlayToBackground[ggData]; -- doesn't actually draw box on background box _ GGSliceOps.GetBoundBox[slice]; GGScene.DeleteSlice[ggData.scene, slice]; GGScene.SelectInBox[ggData.scene, box, normal]; GGCaret.SetAttractor[ggData.caret, GGCaret.GetPoint[ggData.drag.savedCaret], GGCaret.GetNormal[ggData.drag.savedCaret], NIL]; -- put the caret back where it was before this selection operation, Bier, May 22, 1991 GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionAndCaretChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$EndSelectWithBox, $Gargoyle]; }; StartBox: PUBLIC StartProc = { IF GGCaret.Exists[ggData.caret] THEN { sliceD: SliceDescriptor; caretPos: Point; CodeTimer.StartInt[$StartBox, $Gargoyle]; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay GGHistory.NewCapture["Add box", ggData]; -- capture scene BEFORE UPDATE caretPos _ GGCaret.GetPoint[ggData.caret]; SaveSavedState[ggData]; -- must do this before any possible aborts occur GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, selectedCPs: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE]; <> sliceD _ AddNewBoxSlice[caretPos, worldPt, ggData, TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectSlice[sliceD: sliceD, scene: ggData.scene, selectClass: normal]; GGRefresh.MoveToOverlay[sliceD, ggData]; -- put new box on overlay to be rubberbanded ggData.drag.startPoint _ worldPt; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: bitMap, edited: FALSE, okToSkipCapture: TRUE]; DragUpdateCaretAndTransform[worldPt, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $StartCopyAndDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; CodeTimer.StopInt[$StartBox, $Gargoyle]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Start box failed: caret required for adding new box"]; }; EndBox: PUBLIC MouseProc = { <> sliceD: SliceDescriptor _ NARROW[ggData.drag.boxInProgress]; slice: Slice _ sliceD.slice; GGSliceOps.Transform[sliceD.slice, sliceD.parts, ggData.drag.transform, ggData.drag.editConstraints, NIL]; -- update the slice. No history event, because this is an Add and StartBox has already captured the original scene. GGHistory.PushCurrent[ggData]; -- push captured history event onto history list GGRefresh.MoveOverlayToBackground[ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: TRUE, movingParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE]; ggData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; FixupAbortedBox: PROC [ggData: GGData] = { <> sliceD: SliceDescriptor _ NARROW[ggData.drag.boxInProgress]; slice: Slice _ sliceD.slice; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[slice], NIL ]; GGScene.DeleteSlice[ggData.scene, slice]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; }; RegisterMouseActions: PROC [] = { <> GGUserInput.RegisterAction[$UnGuarded, GGMouseEvent.HandleMouse, none, FALSE]; <> GGUserInput.RegisterAction[$StartCaretPos, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartAdd, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartBox, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartBezier, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartSelectWithBox, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartDrag, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartCopyAndDrag, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartRotate, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartScale, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartSixPoint, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartSelectJoint, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartExtSelectJoint, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartSelectSegment, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartExtSelectSegment, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartSelectTrajectory, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartExtSelectTrajectory, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartSelectTopLevel, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartExtSelectTopLevel, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartExtendSelection, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartDeselectJoint, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartDeselectSegment, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartDeselectTrajectory, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$StartDeselectTopLevel, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$During, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$AllUp, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$GuardUp, GGMouseEvent.HandleMouse, none, FALSE]; GGUserInput.RegisterAction[$MouseUp, GGMouseEvent.HandleMouse, none, FALSE]; }; GuardPredicate: TYPE = GGDragTypes.GuardPredicate; modeTable: RefTab.Ref; ModeEntry: TYPE = REF ModeEntryObj; ModeEntryObj: TYPE = RECORD [ guardP: GuardPredicate, startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc ]; RegisterMode: PROC [modeName: ATOM, guardP: GuardPredicate, startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc] = { modeEntry: ModeEntry _ NEW[ModeEntryObj _ [ guardP: guardP, startProc: startProc, duringProc: duringProc, endProc: endProc, abortProc: abortProc, continueProc: continueProc ]]; [] _ RefTab.Store[modeTable, modeName, modeEntry]; }; FindMode: PROC [modeName: ATOM] RETURNS [modeEntry: ModeEntry _ NIL] = { found: BOOL; val: REF; [found, val] _ RefTab.Fetch[modeTable, modeName]; IF found THEN modeEntry _ NARROW[val]; }; RegisterModes: PROC [] = { modeTable _ RefTab.Create[29]; <> RegisterMode[$CaretPos, guarded, StartCaretPos, DuringCaretPos, EndCaretPos, EasyAbort, NIL]; RegisterMode[$Box, guarded, StartBox, DuringDrag, EndBox, AbortBox, NIL]; RegisterMode[$Add, guarded, StartAdd, DuringDrag, EndMotionNoHistory, AbortAdd, ContinueAdd]; RegisterMode[$AddBezier, guarded, GGMouseEvent.StartAddBezier, GGMouseEvent.DuringBezierDrag, GGMouseEvent.EndBezierAdd, GGMouseEvent.AbortBezierAdd, GGMouseEvent.ContinueBezierAdd]; RegisterMode[$Drag, guarded, StartDrag, DuringDrag, EndMotion, EasyAbort, NIL]; RegisterMode[$CopyAndDrag, guarded, StartCopyAndDrag, DuringDrag, EndMotionNoHistory, AbortCopyAndDrag, NIL]; RegisterMode[$Rotate, guarded, StartRotate, DuringRotate, EndMotion, EasyAbort, NIL]; RegisterMode[$Scale, guarded, StartScale, DuringScale, EndMotion, EasyAbort, NIL]; RegisterMode[$SixPoint, guarded, StartSixPoint, DuringSixPoint, EndMotion, EasyAbort, NIL]; RegisterMode[$SelectWithBox, unguarded, StartSelectWithBox, DuringDrag, EndSelectWithBox, AbortBox, NIL]; RegisterMode[$SelectJoint, unguarded, GGMouseEvent.StartSelect, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, NIL]; RegisterMode[$ExtSelectJoint, guarded, GGMouseEvent.StartExtendSelect, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL]; RegisterMode[$SelectSegment, unguarded, GGMouseEvent.StartSelect, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, NIL]; RegisterMode[$ExtSelectSegment, guarded, GGMouseEvent.StartExtendSelect, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL]; RegisterMode[$SelectTrajectory, unguarded, GGMouseEvent.StartSelect, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, NIL]; RegisterMode[$ExtSelectTrajectory, guarded, GGMouseEvent.StartExtendSelect, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL]; RegisterMode[$SelectTopLevel, unguarded, GGMouseEvent.StartSelect, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, NIL]; RegisterMode[$ExtSelectTopLevel, guarded, GGMouseEvent.StartExtendSelect, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL]; RegisterMode[$ExtendSelection, unguarded, GGMouseEvent.StartExtendSelection, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL]; RegisterMode[$DeselectJoint, guarded, GGMouseEvent.StartDeselect, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL]; RegisterMode[$DeselectSegment, guarded, GGMouseEvent.StartDeselect, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL]; RegisterMode[$DeselectTrajectory, guarded, GGMouseEvent.StartDeselect, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL]; RegisterMode[$DeselectTopLevel, guarded, GGMouseEvent.StartDeselect, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL]; }; RegisterMouseActions[]; RegisterModes[]; END.