DIRECTORY Atom, Basics, GGAlign, GGBasicTypes, GGBoundBox, GGButtons, GGSlice, GGCaret, GGDescribe, GGMouseEvent, 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, GGButtons, GGSlice, GGCaret, GGDescribe, GGError, GGEvent, GGGravity, GGMouseEvent, 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; StartProc: TYPE = GGMouseEvent.StartProc; NotYetImplemented: PUBLIC SIGNAL = CODE; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = GGError.Problem; EasyAbort: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { 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]; }; HandleMouseless: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { 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]; $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: ATOM _ NARROW[input.first]; RETURN[ (mouseMode = $CaretPos AND state = $StartCaretPos) OR (mouseMode = $Add AND state = $StartAdd) OR (mouseMode = $Box AND state = $StartBox) 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"]; }; SortNewEntities: PROC [entityList: LIST OF REF ANY] RETURNS [sorted: LIST OF REF ANY] = { CompareProc: GList.CompareProc = { 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 = { 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 { GGError.AppendHerald[gargoyleData.feedback, ". . . Cannot Copy Joints or CPs", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN[FALSE]; }; 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; } 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 { 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; entityList _ SortNewEntities[entityList]; 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; -- nothing on overlay SaveSavedState[gargoyleData]; }; DuringSelect: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; IF gargoyleData.drag.selectState = joint THEN [resultPoint, feature] _ GGGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE] ELSE [resultPoint, feature] _ GGGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE]; GGMouseEvent.SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature]; GGMouseEvent.DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.selectState, "Selecting"]; GGSelect.DeselectAll[gargoyleData.scene, normal]; IF feature = NIL THEN { -- no near trajectories, caret in free space } ELSE { 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]= { 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; 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] = { 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; 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] = { 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] = { 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; ENDCASE => ERROR NotYetImplemented; }; EndSelect: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; IF gargoyleData.drag.selectState = joint THEN [resultPoint, feature] _ GGGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE] ELSE [resultPoint, feature] _ GGGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE]; GGMouseEvent.SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature]; GGWindow.NewCaretPos[gargoyleData]; GGSelect.DeselectAll[gargoyleData.scene, normal]; 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] = { IF feature = NIL THEN { -- no preparation needed GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, "No near joint found.", oneLiner]; } ELSE { 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] = { 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."]; } 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 { 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."]; } 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 { 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."]; } 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; }; StartCaretPos: PUBLIC StartProc = { GGStatistics.StartInterval[$StartCaretPos, GGStatistics.GlobalTable[]]; StartSelectAux[gargoyleData, worldPt]; DuringCaretPos[NIL, gargoyleData, worldPt]; GGStatistics.StopInterval[$StartCaretPos, GGStatistics.GlobalTable[]]; }; DuringCaretPos: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; GGStatistics.StartInterval[$DuringCaretPos, GGStatistics.GlobalTable[]]; [resultPoint, feature] _ GGGravity.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] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, currentObjects, sceneObjects, gargoyleData, TRUE]; 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 _ ""] = { 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]] ]; }; StartMotionAux: PROC [gargoyleData: GargoyleData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, needAnchor: BOOL _ FALSE] 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]; }; DuringDrag: PUBLIC MouseProc = { totalDragVector: Vector; mapPoint: Point; feature: FeatureData; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, mapPoint, feature, "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; feature: FeatureData; degrees: REAL; anchorPoint: Point _ GGCaret.GetPoint[gargoyleData.anchor]; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, mapPoint, feature, "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; feature: FeatureData; ratio: REAL; anchorPoint: Point _ GGCaret.GetPoint[gargoyleData.anchor]; [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, mapPoint, feature, "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; feature: FeatureData; 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, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, mapPoint, feature, "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]; }; 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]; }; EndMotion: PUBLIC MouseProc = { 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[]]; }; 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; 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]; 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 = { 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; GGRefresh.SplitBackgroundAndOverlay[gargoyleData, GGBoundBox.emptyBoundBox]; repaintNeeded _ GGAlign.UpdateBagsForAction[gargoyleData, $Drag]; IF NOT repaintNeeded OR (continue AND GGButtons.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 [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]; 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]; 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] = { totalDragVector: Vector; mapPoint: Point; feature: FeatureData; currentObjects: ObjectBag _ NARROW[gargoyleData.hitTest.currentObjectBag]; sceneObjects: TriggerBag _ NARROW[gargoyleData.hitTest.sceneTriggerBag]; [mapPoint, feature] _ GGGravity.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 = { 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: 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]; gargoyleData.drag.boxInProgress _ sliceD _ GGSlice.MakeBoxSlice[box, corner, GGTransform.Identity[]]; GGObjects.AddSlice[gargoyleData.scene, sliceD.slice, -1]; }; 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]; GGRefresh.MoveToOverlay[sliceD, gargoyleData]; -- slice on overlay to be rubberbanded 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 = { 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 = { 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] = { 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. ðGGMouseEventImplA.mesa Last edited by Bier on January 15, 1987 1:16:26 am 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 PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point]; PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] RETURNS [success: BOOL _ TRUE]; 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. The FSM An easy way to handle the Abort action. $Circle => HandleGuarded[StartCircle, DuringAdd, EndCircle, AbortCircle, NIL, event, gargoyleData, point]; (mouseMode = $Circle AND state = $StartCircle) OR SELECT atom FROM $SelectWithBox, $StartSelectJoint, $StartSelectSegment, $StartSelectTrajectory, $StartSelectTopLevel, $StartExtendSelection => genericAction _ $Start; ENDCASE => genericAction _ atom; Selection Procs Sort back to front (in increasing order). [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] 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. -- no segments, only joints selected If the whole outline is selected, copy it as a whole. Otherwise, each piece becomes a new slice. -- no segments, only joints selected Sort the new slices Add all the new slices to the scene. Check overlay invariant and grab input focus. 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. Use StrictDistance gravity except for SelectJoint. Put Caret on a joint (if any). Deselect all. Do the Selections Find out which point is being selected and highlight it. There shouldn't be any alignment lines in the object bag. Find out which segment is being selected and highlight it. There shouldn't be any alignment lines in the object bag. SelectFeedbackProc Find out which traj is being selected and highlight it. SelectFeedbackProc Find out which traj is being selected and highlight it. There shouldn't be any alignment lines in the object bag. Use StrictDistance gravity except for SelectJoint. Put Caret on a joint (if any). PUT THIS LINE OF CODE IN AND COMMENT OUT THE PutFHerald CALLS IN THE EndSelect PROCS IF YOU WANT MORE EXPLICIT MESSAGES. GGMouseEvent.DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.selectState, "Selected"]; Deselect all. Dispatch to the proper EndSelect handler for final selection. Prepare for a subsequent Add operation. Make Selection and Prepare for Extend. Prepare for a subsequent Add operation and for Extend. [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]; Select the Trajectory or Slice. Prepare for Add and for Extend. [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]; Perform the selection. Prepare for a subsequent Add or Extend. [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]; Caret Procs The user wishes to place the caret without changing the current selection. Only hot objects trigger alignment lines. 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. Move caret to new position. 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. Motion Procs 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. The dragging is done. Update all of the drag entities with the total drag vector and repaint the entire scene. Addition and Extension Procs The trajectory is about to change size. Any sequences stored on the selection lists are obsolete. Update them. The trajectory is about to change size. Any sequences stored on the selection lists are obsolete. Update them. The dragging is done. Update the endpoint with the current transform. Then begin a new add operation. Update the Drawing data structures. Add a new segment. Move the trajectory's outline to the overlay plane. Initialize the transformation and remember the starting position. Map the endpoint and the caret and reposition them. The dragging is done. Update the endpoint with the current transform. do no painting yet Move new box to overlay Initialize the transformation and remember the starting position. The dragging is done. Update the box slice with the current transform. The box is cached in the gargoyleData. 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. This routine is called instead of EndBox. Find the box that is being added and delete it. �Ê0û˜Icode™Kšœ6™6šœ…™…Kšœ%™%K™(—K™šÏk ˜ Kšœý˜ýK˜—šÏnœ�œ�˜ Kš�œ¸˜¿Kš�œ�˜—˜Kšœ�œ#˜7Kšœ �œ˜'Kšœ�œ˜%Kšœ�œ ˜5Kšœ �œ˜-Kšœ�œ!˜3Kšœ�œ˜!Kšœ �œ˜&Kšœ �œ˜&Kšœ �œ˜%Kšœ�œ�œ˜3Kšœ�œ%˜?Kšœ�œ˜!Kšœ�œ"˜9Kšœ�œ˜!Kšœ �œ˜'Kšœ �œ˜'Kšœ�œ˜!Kšœ �œ˜+Kšœ�œ˜3Kšœ�œ ˜5Kšœ�œ#˜;Kšœ�œ)˜GKšœ �œ˜-Kšœ �œ˜+Kšœ�œ˜6Kšœ�œ˜Kšœ �œ˜%Kšœ�œ˜.Kšœ�œ˜/Kšœ �œ˜&Kšœ�œ˜#K˜šœ �œ˜)Kš �œ �œ�œ�œ�œ.™J—Kšœ �œ˜)Kš�œ �œ�œ�œ�œ.�œ � œ™i—K˜Kšžœ�œ�œ�œ˜(Kšžœ�œ�œ �œ˜:˜K˜—š ž œ�œ �œ�œ�œ�œ1˜XK™�Kšœ"˜"Kšœ#˜#KšœJÏc˜bKšœ˜K˜K˜—š žœ�œ �œ�œ�œ�œ1˜WKšœ�œ˜.Kšœ˜K˜K˜—š žœ�œ �œ�œ�œ�œ1˜WKšœ˜Kšœ˜K˜K˜—šž œ�œ!˜2Kšœ0˜0KšœC˜CKšœ�œ �œ�œ˜¯K˜K˜—šžœ�œ�œ!˜AKšœ˜Kšœ˜Kšœ0˜0K˜K˜—šžœ�œ!˜5Kšœ-˜-Kšœ$˜$K˜IK˜—K™K™šžœ�œ�œ �œ�œ�œ�œ�œ�œ˜NK™'Kšœ+˜+K˜K˜—šž œ�œ�œ �œ�œ�œ�œ�œ�œ˜XKšœ�œ˜ Kšœ�œ ˜0š�œ�˜"KšœR�œ˜sKšœf˜fKšœ=�œ˜^KšœI�œ™jKšœD�œ˜eKšœN�œ˜oKšœJ�œ˜kKšœG�œ˜hKšœP�œ˜qKšœo˜oKšœr˜rKšœ–�œ˜·Kšœv˜vKšœš�œ˜»Kšœ|˜|Kšœš�œ˜»Kšœx˜xKšœœ�œ˜½Kšœª˜ªKšœƒ�œ˜¤Kšœ‡�œ˜¨Kšœ��œ˜®Kšœ‰�œ˜ª˜ Kšœ�œ˜š�œ�˜šœ˜KšœL˜LK˜—šœ˜KšœG˜GK˜—šœ˜KšœG˜GK˜—šœ˜KšœQ˜QK˜—šœ˜KšœH˜HK˜—šœ˜KšœO˜OK˜—šœ˜KšœJ˜JK˜—šœ˜KšœI˜IK˜—šœ˜KšœL˜LK˜—šœ˜KšœO˜OK˜—šœ˜KšœR˜RK˜—šœ˜KšœQ˜QK˜—šœ˜KšœT˜TK˜—šœ˜KšœT˜TK˜—šœ˜KšœW˜WK˜—šœ˜KšœR˜RK˜—šœ˜KšœU˜UK˜—šœ˜KšœJ˜JK˜—šœ˜KšœQ˜QK˜—šœ˜KšœS˜SK˜—šœ˜KšœV˜VK˜—šœ˜KšœT˜TK˜—Kš�œŸ˜ —K˜—Kš�œ�œ$˜5—˜K˜—K™—š ž œ�œc�œ�œ�œ�œ1˜¶Kšœ�œ˜Kšœ�œ˜KšœG˜Gš�œ �œ�˜Kšœ �œ�œ�œŸ)˜FKš�œ˜—Kšœ�œ˜Kšœ˜Kš œ�œ2�œ�œ�œ˜`Kšœw˜wKšœF˜FK˜K˜—šžœ�œ �œ�œ�œ�œ�œ�œ˜UKšœ �œ˜)Kšœ�œ�œ˜"š�œ˜Kšœ�œ�˜5Kšœ�œ�˜+Kšœ�œ�˜+Kšœ�œ�™1Kšœ�œ�˜-Kšœ�œ�˜;Kšœ�œ�˜1Kšœ�œ�˜/Kšœ�œ�˜;Kšœ�œ�˜?Kšœ�œ!�˜EKšœ�œ�˜AKšœ�œ �˜CKšœ�œ�˜?Kšœ�œ �˜CKšœ!�œ#�˜IKšœ�œ"˜D—˜K˜——šžœ�œk�œ �œ�œ�œ�œ1˜Îš�œ�˜˜ š�œ�˜šœ ˜ Kšœ)˜)Kšœ7˜7Kš�œÏb œ�œ˜JKš�œ  œC˜R—Kš�œ˜—K˜—˜ š�œ�˜šœ ˜ Kš  œ˜)Kšœ)˜)K˜—Kšœ   œ?˜SKšœ*˜*Kšœ*˜*šœ Ÿ}˜‰Kš  œ;˜DKšœ˜Kšœ*˜*K˜—Kš�œ˜—K˜—šœ ˜ š�œ�˜šœ ˜ Kš œ œ˜=Kšœ˜Kšœ˜K˜—Kšœ   œ?˜SKš�œ˜—K˜—˜ š�œ�˜šœ ˜ Kš œ œ˜=Kšœ˜Kšœ˜K˜—Kšœ   œ?˜Sšœ ŸE˜Qš�œ�œ�œ�œ˜=š�œ  œ�˜4Kšœ˜Kšœ)˜)K˜—Kš�œ  œ@˜OK˜—š�œ˜Kšœ˜Kš œ œ˜=Kšœ˜Kšœ*˜*K˜—K˜—Kš�œ˜—K˜—šœ ˜ Kš�œ�˜KšœG˜GKš�œ˜K˜—Kš�œ�œ'˜8—K˜K˜—š žœ�œJ�œ�œ�œ�œ1˜ŸKšœ�œ˜Kšœ�œ˜š�œ �œ�˜Kšœ �œ�œ�œŸ)˜FKš�œ˜—Kšœ�œ˜Kšœ˜Kš œ�œ2�œ�œ�œ˜`š�œ�™Kšœ–™–Kš�œ™ —Kšœk˜kK˜K˜—šžœ�œR�œ �œ�œ�œ�œ1˜·š�œ�˜˜ š�œ�˜šœ ˜ Kšœ7˜7Kš�œ  œ�œ˜JKš�œ  œ>˜MKšœ˜—Kš�œ˜—K˜—˜ š�œ�˜Kšœ   œ˜4šœ˜Kš œ˜&Kšœ˜Kšœ˜K˜—Kšœ   œ?˜Sšœ Ÿ}˜‰Kš  œ;˜DKšœ˜Kšœ*˜*K˜—Kš�œ˜—K˜—šœ ˜ š�œ�˜KšœG˜GKšœI˜IKš�œ˜—K˜—Kš�œ�œ'˜8—K˜K˜—K™K™K™šžœ�œ�œ�œ�œ�œ�œ �œ�œ�œ�œ˜YK•StartOfExpansion> -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]™)šž œ˜"KšÐck:™:Kšœ�œ˜š�œ�œ� ˜K˜+K˜1Kš�œ˜—š�œ�œ�˜K˜+K˜1Kš�œ˜—Kš�œ*˜0K˜—Kšœ �œ&˜5K˜K˜—šžœ�œ˜*Kšœ�œ˜KšœF˜Fš �œ �œ�œD�œ �œ�˜lš�œ�œ�˜K˜'K˜-Kš�œ�œ˜—Kšœ˜Kš�œ˜—K˜K˜—šž œ�œ˜"Kšœõ™õKšœ˜K˜Kš œ �œ�œ�œ�œ˜Kšœ˜Kšœ˜K˜-Kšœ$˜$Kšœ"˜"KšœŸ0˜NKšœ&˜&Kšœ+ œ˜3š�œl�œ �œ�˜„Jšœ?˜?JšœB˜BJšœ �œ˜(Jšœ*˜*Jš�œ˜—Jšœ=˜=š�œw�œ �œ�˜�Jšœ˜š�œ�œ�œ˜š�œH�œ�œ˜TKšœ$™$KšœY˜YKšœ%˜%Kš�œ�œ˜K˜—Jš 5™5š�œ3�œ˜;Jšœ)˜)JšœO˜OJšœ �œ˜*Jšœ'˜'JšÐbkœ˜J˜—Jš *™*š�œ˜Jšœ2˜2J–f[traj: GGModelTypes.Traj, lineEnds: Imager.StrokeEnd _ square, fillColor: ImagerColorDefs.Color]šœS˜SJšœ:˜:Jšœ �œ˜*Jšœ'˜'J˜—J˜—š�œh�œ �œ�˜�š�œ@�œ�œ˜LKšœ$™$KšœY˜YKšœ%˜%Kš�œ�œ˜K˜—Jšœ*˜*J–f[traj: GGModelTypes.Traj, lineEnds: Imager.StrokeEnd _ square, fillColor: ImagerColorDefs.Color]šœS˜SJšœ2˜2Jšœ �œ˜*Jšœ'˜'Jš�œ˜—Jš�œ˜—Jš ™Jšœ)˜)Jš $™$š �œ � œ�œ�œ �œ�˜Tš�œ�œ�˜ šœ˜Jšœ5˜5Jšœ-�œ ˜9Jšœ8˜8J˜—šœ˜Jšœ9˜9Jšœ1�œ ˜=Jšœ<˜˜>Kšœ?˜?Kšœ˜—Kšœ �œŸ;˜MšœP�œ˜VK™9—Kš�œ�œ˜#—KšœŸ˜K˜—šž œ� œG˜^Kšœ8˜8K˜K˜—šžœ�œD�œ�œ˜�K™K™7š�œ�˜šœ ˜ Kšœ�œ)˜BKšœ?˜?KšœC˜CKšœ˜—šœ ˜ Kšœ�œ'˜˜>Kšœ8˜8Kš�œ�œ˜%—K˜—K˜K˜—šžœ�œR�œ ˜wKšœÅ™ÅKš�œ �œ�œ4�œ˜Mš�œ˜š�œ�˜šœ ˜ K˜ Kšœ#˜#Kšœ�œ˜Kšœ˜K˜K˜Kšœ�œ˜4KšœT˜Tš�œ �˜šœ ˜ Kšœ1˜1K˜—šœ˜Kšœ=˜=K˜—šœ ˜ Kšœ7˜7K˜—Kš�œ�œ˜—Kšœ9˜9Kšœ �œ1˜=Kšœ;˜;K˜—šœ ˜ KšœB˜BK˜—Kš�œ7�œ˜C—K˜—šœQ˜QKšœJ˜JKšœ&˜&Kšœ˜—K˜—K™K™ K™šžœ�œ+�œ �œ�œ�œ�œ�œ˜�Kšœ˜Kšœ�œ˜Kš �œ�œ&�œ�œŸ˜NKšœŸ0˜Nš�œ3�œ˜;Kšœa˜aKšœ%˜%Kš�œ�œ˜K˜—š�œ �œ�œ%�œ˜@Kšœ[˜[Kšœ%˜%Kš�œ�œ˜K˜—Kšœ9˜9KšœD˜DKšœG�œŸ˜iKšœ>˜>Kšœ=˜=Kšœ2˜2Kšœ>˜>KšœC˜CKš �œ�œ] œ �œ �œ�œ˜³Kš �œ] œ �œ �œ�œ˜¡Kš�œ�œ˜ Kšœ˜K˜—šž œ�œ˜Kšœ�œ8˜JKš �œ�œ�œ�œ�œ˜'Kšœ �œ˜'K˜K˜—šž œ�œ˜!Kšœ�œ:�œ˜RKš �œ�œ�œ�œ�œ˜'Kšœ �œ˜)K˜K˜—šž œ�œ˜ Kšœ˜Kšœ˜Kšœ�œ9�œ˜QKš �œ�œ�œ�œ�œ˜'Kšœ4˜4KšœI˜Iš�œ�œ˜%Kšœe˜eKšœ%˜%Kš�œ�œ˜K˜—Kšœ �œ˜(K˜K˜—šž œ�œ˜ K™éK˜Kšœ˜Kšœ˜Kšœ  œ…�œ˜®Kšœ@˜@KšœG˜GKšœ3  œ)˜eKšœu�œ �œ�œ˜¤KšœŸ˜K˜—šž œ�œ˜"K˜"Kšœ˜Kšœ˜Kšœ �œ˜Kšœ;˜;Kšœ  œ…�œ˜®Kšœ@˜@KšœI˜IKšœ0˜0KšœE˜EKšœ* œ˜QKšœu�œ �œ�œ˜¤K˜K˜—šž œ�œ˜!Kšœ �œ ˜K˜"Kšœ˜Kšœ˜Kšœ�œ˜ Kšœ;˜;Kšœ  œ…�œ˜®Kšœ?˜?KšœI˜IKšœ0˜0Kš �œ&�œ&�œ�œŸ˜tKšœI˜IKšœ* œ˜NKšœu�œ �œ�œ˜¤K˜K˜—šžœ�œ˜$Kš�œ ˜Kšœ �œ ˜Kšœ�œ�œ˜Kšœ�œ˜Kšœ˜Kšœ˜Kšœ8˜8Kšœ&˜&KšœJ˜JKšœ¨�œ˜®KšœK˜KKšœ˜KšœJ˜JKš �œ�œ�œ�œŸ˜JKšœ9˜9Kšœu�œ �œ�œ˜¤K˜K˜—šž œ�œ˜#Kš�œ ˜Kšœ �œ ˜Kšœ˜Kšœ�œ˜Kšœ�œ=�œ˜UKš �œ�œ�œ�œ�œ˜'Kšœ+˜+Kšœ"˜"Kšœ1˜1Kšœ;˜;š�œ�œ�œ˜%Kšœg˜gKšœ%˜%Kš�œ�œ˜K˜—Kšœ�œ˜+K˜K˜—šž œ�œ˜K™oK˜CKšœ(˜(Kšœ0˜0Kšœ#˜#Kšœ�œ �œ�œ˜®K˜BK˜—K˜K™K˜š žœ�œ�œ<�œ1�œ˜œKšœ"˜"Kšœ�œ�œ˜K˜Kšœ �œ˜K˜K˜Kšœ˜K˜Kšœ%˜%š�œ�œŸ!˜GKšœ˜Kšœ!Ÿ˜7Kš�œ�œ�œ�œ*˜VKšœ �œ˜KšœZ˜ZKš �œ�œ �œ�œ�œ˜.š�œ�œŸ'˜>Kšœ2�œ˜7Kšœ3˜3š�œ�œ �œ�œ˜K™p—Kšœ œ�œ˜:Kšœ2�œ˜7Kšœ5˜5KšœI˜IKšœ �œ6˜BKšœ*˜*K˜—š�œ�œ!�œŸ(˜WKšœ2�œ˜7Kšœ3˜3Kš�œ�œ �œ�œ˜šœ œ�œ˜:K™p—Kšœ2�œ˜7KšœH˜HKšœI˜IKšœ �œ6˜BKšœ*˜*K˜—Kš�œ�œ˜ K˜—š�œŸW˜^K˜Kšœ2�œ˜7Kšœ%˜%Kšœ3˜3Kš�œ�œ �œ�œ˜Kšœ+˜+Kšœ,˜,Kšœ2�œ˜7Kšœ5˜5KšœE˜EKšœ �œ2˜>Kšœ*˜*K˜—Kšœ1˜1KšœD˜DK˜K˜—šž œ�œ˜!K™gKšœ3˜3Kšœ>˜>Kšœ#˜#Kšœ0˜0K–h[input: LIST OF REF ANY, gargoyleData: GGInterfaceTypes.GargoyleData, worldPt: GGBasicTypes.Point]šœ�œ'˜>K˜K˜—šžœ�œ˜Kšœ�œ˜Kšœ˜K˜BKšœ �œ�œ˜4Kšœ;˜;š�œ�œ �œ�œ˜Kš #™#—KšœL˜LKšœA˜Aš�œ�œ�˜Kšœ �œH�œ˜[Kš œ\ œ �œ �œ�œ˜œK˜—š�œ˜Kš œ\ œ �œ �œ�œ˜žK˜—Kšœ �œ˜&K˜AKšœŸ˜K˜—šž œ�œ.�œ �œ˜oK˜Kšœ˜Kšœ �œ˜Kšœ�œ�œ˜Kšœ˜K˜K˜K˜KšœŸ0˜Nš �œ�œ&�œ�œŸ˜NKš ™—Kšœ, œ,˜iKš �œ�œ �œ�œ�œ�œ˜'Kšœ5˜5Kšœ:˜:šœL˜LKš 3™3—Kšœ-˜-Kšœ �œ˜K˜EKšœM˜MKšœ-Ÿ˜FK–?[outline: GGModelTypes.Outline, seq: GGModelTypes.Sequence]šœA˜AKšœ �œ4˜Bšœ0˜0Kš A™A—Kšœ(Ÿ˜BKšœ>˜>KšœŸ˜K˜—šž œ�œ˜Kšœ'˜'K˜BKšœu�œ �œ�œ˜¤K˜AK˜K˜—šžœ�œ1˜FK™3K˜Kšœ˜Kšœ˜Kšœ�œ(˜JKšœ�œ'˜HK˜Kšœy�œ˜Kšœ>˜>KšœG˜GKšœe˜eKšœ˜K˜—šžœ�œ˜K™FKšœ˜K˜@Kšœ'˜'Kšœ)˜)Kšœ>˜>Kšœ#˜#Kšœ0˜0Kšœ<˜˜>KšœL˜LKšœo�œ �œ�œ˜žKšœ �œ˜&KšœA˜AKšœ˜K˜—šžœ�œ˜K™nKšœ�œ"˜BKšœ˜KšœIŸ˜\Kšœ  œ˜0Kšœ7Ÿ(˜_Kšœ)˜)Kšœ}�œ �œ�œ˜¬K˜K˜—šžœ�œ˜&Kšœ«™«Kšœ�œ"˜BKšœ˜KšœIŸ˜\Kšœ  œ˜0KšœF�œ=˜†Kšœ�œ˜2Kšœ7Ÿ(˜_Kšœ}�œ �œ�œ˜­K˜K˜—šžœ�œ!˜6KšœZ™ZKšœ�œ"˜BKšœ˜KšœRŸ˜eKšœ5Ÿ˜QKšœ=˜=Kšœ/˜/Kšœ2˜2KšœŒ�œ �œ�œ˜¼K˜—K˜šž œ�œ˜K˜ K˜/K˜-K˜/K˜;Kšœ2�œ˜QKšœ?˜?K˜Kšœ0˜0Kšœ?˜?K˜Kšœ7˜7Kšœ?˜?Kšœ8˜8Kšœ?˜?Kšœ5˜5Kšœ?˜?Kšœ3˜3Kšœ?˜?Kšœ2˜2Kšœ?˜?Kšœ7˜7Kšœ?˜?K˜—K˜K˜ K˜Kš�œ˜—…—âö,á