<> <> <> <> <<>> DIRECTORY Atom, GGAlign, GGCaret, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGObjects, GGRefresh, GGSegment, GGSelect, GGTouch, GGTransform, GGVector, GGWindow, Imager, ImagerTransformation, InputFocus, IO, Menus, Rope, Rosary; GGMouseEventImpl: CEDAR PROGRAM IMPORTS Atom, GGAlign, GGCaret, GGError, GGGravity, GGObjects, GGRefresh, GGSegment, GGSelect, GGTouch, GGTransform, GGVector, GGWindow, ImagerTransformation, InputFocus, IO, Rosary EXPORTS GGMouseEvent = BEGIN Caret: TYPE = GGInterfaceTypes.Caret; Cluster: TYPE = GGModelTypes.Cluster; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; FeatureData: TYPE = GGGravity.FeatureData; Joint: TYPE = GGModelTypes.Joint; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; MouseButton: TYPE = Menus.MouseButton; ObjectBag: TYPE = GGGravity.ObjectBag; Outline: TYPE = GGModelTypes.Outline; Point: TYPE = GGModelTypes.Point; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGModelTypes.Segment; Sequence: TYPE = GGModelTypes.Sequence; TouchGroup: TYPE = GGModelTypes.TouchGroup; TouchItem: TYPE = GGModelTypes.TouchItem; TouchItemGenerator: TYPE = GGTouch.TouchItemGenerator; Traj: TYPE = GGModelTypes.Traj; TrajGenerator: TYPE = GGObjects.TrajGenerator; Vector: TYPE = GGModelTypes.Vector; NotYetImplemented: PUBLIC SIGNAL = CODE; SittingOnEnd: PROC [caret: Caret] RETURNS [BOOL] = { chair: Traj; isJoint: BOOL; jointNum: NAT; [chair, isJoint, ----, jointNum, ----] _ GGCaret.GetChair[caret]; IF chair = NIL THEN RETURN[FALSE]; IF NOT isJoint THEN RETURN[FALSE]; RETURN[GGObjects.IsEndJoint[chair, jointNum]]; }; ExtendTrajToMouse: PRIVATE PROC [scene: Scene, worldPt: Point, gargoyleData: GargoyleData] RETURNS [traj: Traj, new: BOOL, success: BOOL] = { caret: Caret _ gargoyleData.caret; newLine: Segment; caretPoint: Point; jointNum: NAT; caretPoint _ GGCaret.GetPoint[caret]; IF SittingOnEnd[caret] THEN { -- extend the existing trajectory new _ FALSE; [traj, ----, ----, jointNum, ----] _ GGCaret.GetChair[caret]; IF jointNum = 0 THEN { -- add to the low end of the trajectory newLine _ GGSegment.MakeLine[worldPt, caretPoint]; success _ GGObjects.AddSegment[traj, lo, newLine, hi]; IF NOT success THEN RETURN; GGCaret.Update[gargoyleData, worldPt, NIL]; GGCaret.SitOnJoint[gargoyleData.caret, traj, 0]; } ELSE IF jointNum = GGObjects.HiJoint[traj] THEN { -- add to the high end of the trajectory newLine _ GGSegment.MakeLine[caretPoint, worldPt]; success _ GGObjects.AddSegment[traj, hi, newLine, lo]; IF NOT success THEN RETURN; GGCaret.Update[gargoyleData, worldPt, NIL]; GGCaret.SitOnJoint[gargoyleData.caret, traj, jointNum + 1]; } ELSE ERROR; } ELSE { -- Create a new trajectory starting at the caret (making touching constraints, if any). newOutline: Outline; new _ TRUE; newLine _ GGSegment.MakeLine[caretPoint, worldPt]; traj _ GGObjects.CreateTraj[caretPoint]; success _ GGObjects.AddSegment[traj, hi, newLine, lo]; IF NOT success THEN RETURN; newOutline _ GGObjects.OutlineFromTraj[traj]; GGObjects.AddOutline[scene, newOutline, -1]; GGCaret.MakeChairTouchTrajJoint[gargoyleData.caret, gargoyleData, traj, 0]; GGCaret.Update[gargoyleData, worldPt, NIL]; GGCaret.SitOnJoint[gargoyleData.caret, traj, 1]; }; }; UpdateCaretToFeature: PROC [gargoyleData: GargoyleData, mapPoint: Point, feature: FeatureData] = { IF feature = NIL THEN GGCaret.Update[gargoyleData, mapPoint, NIL] ELSE SELECT feature.resultType FROM joint => GGCaret.Update[gargoyleData, mapPoint, feature.tseq.traj, TRUE, feature.jointNum]; segment => GGCaret.Update[gargoyleData, mapPoint, feature.tseq.traj, FALSE,,feature.segNum]; ENDCASE => GGCaret.Update[gargoyleData, mapPoint, NIL]; ReportFeature[feature, gargoyleData]; }; StartAdd: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> jointSeq: Sequence; trajUnderCaret: Traj; objectBag: ObjectBag; jointNum: NAT; new, success: BOOL; [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; gargoyleData.currentAction _ $StartAdd; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay GGWindow.SaveCaretPos[gargoyleData]; <> [trajUnderCaret, new, success] _ ExtendTrajToMouse[gargoyleData.scene, worldPt, gargoyleData]; IF NOT success THEN { gargoyleData.currentAction _ $None; RETURN; }; <> objectBag _ GGGravity.CreateObjectBag[]; gargoyleData.hitTest.environ _ objectBag; GGAlign.AddItemsForAction[gargoyleData, objectBag, $Add]; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, objectBag]; <> [----, ----, ----, jointNum, ----] _ GGCaret.GetChair[gargoyleData.caret]; jointSeq _ GGObjects.CreateSimpleSequence[trajUnderCaret, jointNum, jointNum]; GGCaret.SetSequence[gargoyleData.caret, jointSeq]; GGRefresh.MoveToOverlay[jointSeq, gargoyleData]; -- traj on overlay (why = joint). GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay <> gargoyleData.drag.startPoint _ worldPt; gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; }; FirstDuringAdd: PRIVATE PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF NOT CheckProperAction[gargoyleData, $StartAdd] THEN RETURN; gargoyleData.currentAction _ $Add; GGRefresh.StoreBackground[gargoyleData]; -- all but caret and traj on background DuringAdd[input, gargoyleData, worldPt]; }; DuringAdd: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> totalDragVector: Vector; mapPoint: Point; feature: FeatureData; environ: ObjectBag _ NARROW[gargoyleData.hitTest.environ]; IF gargoyleData.currentAction = $StartAdd THEN { FirstDuringAdd[input, gargoyleData, worldPt]; RETURN; }; IF NOT CheckProperAction[gargoyleData, $Add] THEN RETURN; GGWindow.Painter[$EraseOverlay, gargoyleData]; -- Draw the background [mapPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, mapPoint, feature]; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, environ]; totalDragVector _ GGVector.Sub[mapPoint, gargoyleData.drag.startPoint]; gargoyleData.drag.transform _ ImagerTransformation.Translate[[totalDragVector[1], totalDragVector[2]]]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; }; EndAdd: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> selSeq: Sequence; IF NOT CheckProperActions[gargoyleData, $Add, $StartAdd] THEN RETURN; selSeq _ GGCaret.GetSequence[gargoyleData.caret]; GGObjects.TransformSequence[selSeq, gargoyleData.drag.transform]; GGCaret.MakeChairTouchAttractor[gargoyleData.caret, gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; gargoyleData.currentAction _ $None; GGWindow.Painter[$PaintEntireScene, gargoyleData]; }; ReportFeature: PROC [feature: FeatureData, gargoyleData: GargoyleData] = { IF feature = NIL THEN { GGError.Append["", oneLiner]; } ELSE { SELECT feature.resultType FROM joint => GGError.Append["Caret on joint", oneLiner]; segment => GGError.Append["Caret on segment", oneLiner]; distanceLine => GGError.Append["Caret on distance line", oneLiner]; slopeLine => GGError.Append["Caret on slope line", oneLiner]; symmetryLine => GGError.Append["Caret on symmetry line", oneLiner]; radiiCircle => GGError.Append["Caret on compass circle", oneLiner]; intersectionPoint => GGError.Append["Caret on intersection point", oneLiner]; ENDCASE => ERROR; }; }; CheckProperAction: PROC [gargoyleData: GargoyleData, properState: ATOM] RETURNS [BOOL] = { msgRope: Rope.ROPE; IF gargoyleData.currentAction = properState THEN RETURN[TRUE]; IF gargoyleData.currentAction = $None THEN RETURN[FALSE]; -- but don't complain msgRope _ IO.PutFR["User switched from %g to %g. Gargoyle confused.", [rope[Atom.GetPName[properState]]], [rope[Atom.GetPName[gargoyleData.currentAction]]]]; GGError.Append[msgRope, oneLiner]; GGError.Blink[]; ResetMouseMachinery[gargoyleData]; RETURN[FALSE]; }; CheckProperActions: PROC [gargoyleData: GargoyleData, properState1, properState2: ATOM] RETURNS [BOOL] = { msgRope: Rope.ROPE; IF gargoyleData.currentAction = properState1 OR gargoyleData.currentAction = properState2 THEN RETURN[TRUE]; msgRope _ IO.PutFR["User switched from %g or %g to %g. Gargoyle confused.", [rope[Atom.GetPName[properState1]]], [rope[Atom.GetPName[properState2]]], [rope[Atom.GetPName[gargoyleData.currentAction]]]]; GGError.Append[msgRope, oneLiner]; GGError.Blink[]; ResetMouseMachinery[gargoyleData]; RETURN[FALSE]; }; ResetMouseMachinery: PUBLIC PROC [gargoyleData: GargoyleData] = { gargoyleData.currentAction _ $None; gargoyleData.hitTest.responsibleFor _ NIL; GGSelect.DeselectAll[gargoyleData, normal]; GGSelect.DeselectAll[gargoyleData, copy]; GGSelect.DeselectAll[gargoyleData, hot]; GGSelect.DeselectAll[gargoyleData, active]; GGRefresh.MoveOverlayToBackground[gargoyleData]; }; SitOnFeature: PROC [caret: Caret, feature: FeatureData] = { IF feature = NIL THEN { GGCaret.DoNotSit[caret]; RETURN; }; SELECT feature.resultType FROM joint => GGCaret.SitOnJoint[caret, feature.tseq.traj, feature.jointNum]; segment => GGCaret.SitOnSegment[caret, feature.tseq.traj, feature.segNum]; ENDCASE => GGCaret.DoNotSit[caret]; }; SelectAllTouchingFeature: PROC [feature: FeatureData, gargoyleData: GargoyleData] = { item: TouchItem; jointSeq, segSeq: Sequence; SELECT feature.resultType FROM joint => { item _ TouchItemOfJoint[feature.tseq.traj, feature.jointNum]; IF item = NIL THEN { jointSeq _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.jointNum, feature.jointNum]; [----,----] _ GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; IF GGObjects.IsEndJoint[feature.tseq.traj, feature.jointNum] THEN { -- an end joint GGError.Append["End Joint selected", oneLiner]; } ELSE { -- a middle joint <> GGError.Append["Middle Joint selected", oneLiner]; }; } ELSE { group: TouchGroup _ GGTouch.TouchGroupOfItem[item]; jointNum, segNum: NAT; itemGen: TouchItemGenerator; itemGen _ GGTouch.AllTouchItems[group]; FOR thisItem: TouchItem _ GGTouch.NextTouchItem[itemGen], GGTouch.NextTouchItem[itemGen] UNTIL thisItem = NIL DO SELECT thisItem.touchingPartType FROM joint => { jointNum _ GGObjects.IndexOfJoint[thisItem.joint, thisItem.traj]; jointSeq _ GGObjects.CreateSequenceFromJoint[thisItem.traj, jointNum]; [----,----] _ GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; }; segment => { segNum _ GGObjects.IndexOfSegment[thisItem.seg, thisItem.traj]; segSeq _ GGObjects.CreateSequenceFromSegment[thisItem.traj, segNum]; [----,----] _ GGSelect.SelectSequence[segSeq, gargoyleData, normal]; }; ENDCASE; ENDLOOP; GGError.Append["Multiple entities selected", oneLiner]; }; }; ENDCASE => ERROR NotYetImplemented; }; TouchItemOfJoint: PROC [traj: Traj, jointNum: NAT] RETURNS [item: TouchItem] = { joint: Joint _ NARROW[Rosary.Fetch[traj.joints, jointNum]]; item _ joint.touchItem; }; UpdateCaretSelectionsAndOverlay: PRIVATE PROC [gargoyleData: GargoyleData, worldPt: Point] = { resultPoint: Point; feature: FeatureData; environ: ObjectBag; environ _ NARROW[gargoyleData.hitTest.environ]; [resultPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, resultPoint, feature]; GGSelect.DeselectAll[gargoyleData, normal]; IF feature = NIL THEN { -- no near trajectories, place caret in free space <> IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; gargoyleData.hitTest.responsibleFor _ NIL; }; } ELSE { SELECT feature.resultType FROM joint => { jointSeq: Sequence _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.jointNum, feature.jointNum]; IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; }; gargoyleData.hitTest.responsibleFor _ jointSeq; [----,----] _ GGSelect.SelectSequence[jointSeq, gargoyleData, normal]; GGRefresh.MoveJointsToOverlay[feature.tseq.traj, gargoyleData]; }; segment => { -- we are in the middle of a segment seq: Sequence _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.segNum, GGObjects.FollowingJoint[feature.tseq.traj, feature.segNum]]; IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; }; gargoyleData.hitTest.responsibleFor _ seq; [----,----] _ GGSelect.SelectSequence[seq, gargoyleData, normal]; GGRefresh.MoveJointsToOverlay[feature.tseq.traj, gargoyleData]; }; slopeLine, distanceLine, intersectionPoint, radiiCircle => { }; ENDCASE => ERROR NotYetImplemented; }; GGGravity.AdjustVisibilityPoint[worldPt, gargoyleData.hitTest.tolerance, environ]; }; StartSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> environ: ObjectBag; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; GGWindow.SaveCaretPos[gargoyleData]; gargoyleData.currentAction _ $StartSelectPoint; GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay <> environ _ GGGravity.CreateObjectBag[]; gargoyleData.hitTest.environ _ environ; GGAlign.AddItemsForAction[gargoyleData, environ, $SelectPoint]; GGSelect.DeselectAll[gargoyleData, normal]; }; FirstDuringSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> IF NOT CheckProperAction[gargoyleData, $StartSelectPoint] THEN RETURN; gargoyleData.currentAction _ $SelectPoint; GGRefresh.StoreBackground[gargoyleData]; -- all but caret is background GGWindow.Painter[$EraseOverlay, gargoyleData]; -- show all but caret UpdateCaretSelectionsAndOverlay[gargoyleData, worldPt]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; -- show caret in new position }; <<>> DuringSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { IF gargoyleData.currentAction = $StartSelectPoint THEN { FirstDuringSelectPoint[input, gargoyleData, worldPt]; RETURN; }; IF NOT CheckProperAction[gargoyleData, $SelectPoint] THEN RETURN; GGWindow.Painter[$EraseOverlay, gargoyleData]; -- show all but caret UpdateCaretSelectionsAndOverlay[gargoyleData, worldPt]; GGWindow.Painter[$PaintDragOverlay, gargoyleData]; -- show caret in new position }; EndSelectPoint: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> resultPoint: Point; feature: FeatureData; environ: ObjectBag; IF NOT CheckProperActions[gargoyleData, $SelectPoint, $StartSelectPoint] THEN RETURN; GGWindow.Painter[$EraseOverlay, gargoyleData]; -- show all but caret environ _ NARROW[gargoyleData.hitTest.environ]; [resultPoint, feature] _ GGGravity.Map[worldPt, gargoyleData.hitTest.criticalR, environ, gargoyleData]; UpdateCaretToFeature[gargoyleData, resultPoint, feature]; SitOnFeature[gargoyleData.caret, feature]; GGSelect.DeselectAll[gargoyleData, normal]; IF gargoyleData.hitTest.responsibleFor # NIL THEN { GGRefresh.RemoveJointsFromOverlay[gargoyleData.hitTest.responsibleFor.traj, gargoyleData]; gargoyleData.hitTest.responsibleFor _ NIL; }; IF feature = NIL THEN { -- no near trajectories, place caret in free space } ELSE { GGSelect.DeselectAll[gargoyleData, normal]; SELECT feature.resultType FROM joint => { SelectAllTouchingFeature[feature, gargoyleData]; gargoyleData.extendMode _ joint; }; segment => { -- we are in the middle of a segment seq: Sequence _ GGObjects.CreateSimpleSequence[feature.tseq.traj, feature.segNum, GGObjects.FollowingJoint[feature.tseq.traj, feature.segNum]]; [----,----] _ GGSelect.SelectSequence[seq, gargoyleData, normal]; GGError.Append["Segment selected", oneLiner]; gargoyleData.extendMode _ segment; }; slopeLine => { GGError.Append["Direction line hit.", oneLiner]; }; intersectionPoint => { GGError.Append["Intersection point hit.", oneLiner]; }; radiiCircle => { GGError.Append["Compass circle hit.", oneLiner]; }; distanceLine => { GGError.Append["Distance line hit.", oneLiner]; }; ENDCASE => ERROR NotYetImplemented; }; GGRefresh.MoveOverlayToBackground[gargoyleData]; -- caret is back in background gargoyleData.currentAction _ $None; GGWindow.Painter[$PaintEntireScene, gargoyleData]; -- caret in proper place }; UpdateSelectedAfterMove: PROC [gargoyleData: GargoyleData] = { entityGen: EntityGenerator; GGTouch.InitializeTouching[gargoyleData]; entityGen _ GGSelect.SelectedEntities[gargoyleData, normal]; FOR entity: REF ANY _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM selSeq: Sequence => { GGObjects.TransformSequence[selSeq, gargoyleData.drag.transform]; GGTouch.SequenceMoved[selSeq, gargoyleData]; }; outline: Outline => { FOR trajs: LIST OF Traj _ outline.children, trajs.rest UNTIL trajs = NIL DO GGObjects.TransformTraj[trajs.first, gargoyleData.drag.transform]; GGTouch.TrajMoved[trajs.first, gargoyleData]; ENDLOOP; }; cluster: Cluster => ERROR NotYetImplemented; ENDCASE => ERROR NotYetImplemented; ENDLOOP; }; StartDrag: PUBLIC PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { <> <> <> startUpBag, duringBag: ObjectBag; mapPoint: Point; feature: FeatureData; IF NOT CheckProperAction[gargoyleData, $None] THEN { gargoyleData.currentAction _ $None; RETURN; }; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF GGSelect.NoSelections[gargoyleData, normal] THEN { GGError.Append["Select some objects to drag.", oneLiner]; GGError.Blink[]; gargoyleData.currentAction _ $None; RETURN; }; GGWindow.SaveCaretPos[gargoyleData]; gargoyleData.currentAction _ $Drag; GGRefresh.MoveToOverlay[gargoyleData.caret, gargoyleData]; -- caret on overlay GGRefresh.MoveAllSelectedToOverlay[gargoyleData, normal]; -- selected objects on overlay <<>> <> startUpBag _ GGGravity.CreateObjectBag[]; GGAlign.AddItemsForAction[gargoyleData, startUpBag, $DragStartUp]; <