/// <summary> /// Initializes our own drag and determines whether we are a slave or the master and if there are actual slave handles in that case /// </summary> public void InitializeDrag(IInputModeContext context) { coreHandle.InitializeDrag(context); if (context.ParentInputMode is MoveInputMode) { //If we are moved via MoveInputMode (happens when the whole edge is dragged) //We only delegate to the core handle return; } //If we are indirectly controlled from the other control point or the bend curve point //those implementations put a marker in the lookup //If such a marker is present, we DON'T delegate to the other handle and just move ourselves. var bcph = context.Lookup <InnerControlPointHandle>(); var cph = context.Lookup <OuterControlPointHandle>(); if (bcph == null && cph == null) { //We are the master handle and so we control the other one var index = bend.GetIndex(); //Whether this is the first or the last bend in such a control point triple var isFirstInTriplet = index % 3 == 1; IBend otherBend = null; IBend middleBend = null; if (isFirstInTriplet && index < bend.Owner.Bends.Count - 1) { //We are the first of the triple and there is a potential slave handle //So get the slave and the middle bend otherBend = bend.Owner.Bends[index + 2]; middleBend = bend.Owner.Bends[index + 1]; } else if (index >= 3) { //We are the last of the triple and there is a potential slave handle //So get the slave and the middle bend otherBend = bend.Owner.Bends[index - 2]; middleBend = bend.Owner.Bends[index - 1]; } if (otherBend != null && AreCollinear(bend.Location, middleBend.Location, otherBend.Location)) { slaveHandle = otherBend.Lookup <IHandle>(); middleLocation = middleBend.Location.ToPointD(); } if (slaveHandle != null) { //There not only a bend, but actually a handle to control //notify it that it is the slave //We just put ourselves in the context, so our presence serves as flag to the other handle //And from now on control its actions. var childContext = Contexts.CreateInputModeContext(context.ParentInputMode, context, Lookups.Single(this)); slaveHandle.InitializeDrag(childContext); slaveOrigin = slaveHandle.Location.ToPointD(); } } }
/// <summary> /// Initializes the drag. /// </summary> public void InitializeDrag(IInputModeContext context) { var imc = context.Lookup <IModelItemCollector>(); if (imc != null) { imc.Add(node); } rotationCenter = node.Layout.GetCenter(); initialAngle = GetAngle(); var graph = context.Lookup <IGraph>(); if (graph != null) { compoundEdit = graph.BeginEdit("Change Rotation Angle", "Change Rotation Angle"); } portHandles.Clear(); var portContext = new DelegatingContext(context); foreach (var port in node.Ports) { var portHandle = new DummyPortLocationModelParameterHandle(port); portHandle.InitializeDrag(portContext); portHandles.Add(portHandle); } if (reshapeHandler != null) { reshapeHandler.InitializeReshape(context); } // Collect other visible nodes and their angles if (SnapToSameAngleDelta > 0) { var canvas = context.CanvasControl; var rotatedNodes = canvas.GetCanvasObjects() .Select(co => co.UserObject) // only collect nodes .OfType <INode>() // ... that are in the viewport .Where(n => canvas.Viewport.Intersects(n.Layout.ToRectD())) // ... and can be rotated .Where(n => n.Style is RotatableNodeStyleDecorator) // ... and are not *this* node .Where(n => n != node); // Group nodes by identical angles nodeAngles = rotatedNodes.GroupBy(n => ((RotatableNodeStyleDecorator)n.Style).Angle) .Select(nodes => Tuple.Create(nodes.Key, nodes.ToList().AsEnumerable())).ToList(); } }
/// <summary> /// Creates a new bend at the given location. If this bend is on the first or last segment, /// a second bend is created and placed at a location that ensures that the newly create /// inner segment is orthogonal. /// </summary> public int CreateBend(IInputModeContext context, IGraph graph, IEdge edge, PointD location) { var edgePoints = GetEdgePoints(edge); var closestSegment = DetermineBendSegmentIndex(edgePoints, location); int firstSegment = 0; int lastSegment = edge.Bends.Count; // if bend wasn't created in first or last segment, call default action if (closestSegment != firstSegment && closestSegment != lastSegment) { return((new DefaultBendCreator()).CreateBend(context, graph, edge, location)); } // add created bend and another one to make the edge stay orthogonal if (closestSegment == -1 || context == null || !(context.ParentInputMode is CreateBendInputMode)) { return(-1); } var editingContext = context.Lookup <OrthogonalEdgeEditingContext>(); if (editingContext == null) { return(-1); } if (closestSegment == firstSegment) { IPoint nextPoint = edgePoints[1]; // get orientation of next edge segment to determine second bend location SegmentOrientation orientation = editingContext.GetSegmentOrientation(edge, 1); graph.AddBend(edge, location, 0); if (orientation == SegmentOrientation.Horizontal) { graph.AddBend(edge, new PointD(nextPoint.X, location.Y), 1); } else if (orientation == SegmentOrientation.Vertical) { graph.AddBend(edge, new PointD(location.X, nextPoint.Y), 1); } return(0); } if (closestSegment == lastSegment) { IPoint prevPoint = edgePoints[edge.Bends.Count]; // get orientation of next edge segment to determine second bend location SegmentOrientation orientation = editingContext.GetSegmentOrientation(edge, edge.Bends.Count - 1); graph.AddBend(edge, location, edge.Bends.Count); if (orientation == SegmentOrientation.Horizontal) { graph.AddBend(edge, new PointD(prevPoint.X, location.Y), edge.Bends.Count - 1); } else if (orientation == SegmentOrientation.Vertical) { graph.AddBend(edge, new PointD(location.X, prevPoint.Y), edge.Bends.Count - 1); } return(edge.Bends.Count - 1); } return(-1); }
/// <summary> /// The node is upon to be dragged. /// </summary> public void InitializeDrag(IInputModeContext context) { reparentHandler = context.Lookup <IReparentNodeHandler>(); var graphControl = context.CanvasControl as GraphControl; layoutHelper = new LayoutHelper(graphControl, node); layoutHelper.InitializeLayout(); handler.InitializeDrag(context); }
/// <summary> /// Finishes the drag and updates the angle of the rotated node. /// </summary> public void DragFinished(IInputModeContext context, PointD originalLocation, PointD newLocation) { var vector = (newLocation - rotationCenter).Normalized; var angle = CalculateAngle(vector); if (ShouldSnap(context)) { angle = SnapAngle(context, angle); } SetAngle(context, angle); // Switch width / height for 'vertical' rotations // Note that other parts of the application need support for this feature, too. var graph = context.GetGraph(); if (graph == null) { return; } var portContext = new DelegatingContext(context); foreach (var portHandle in portHandles) { portHandle.DragFinished(portContext, originalLocation, newLocation); } portHandles.Clear(); // Workaround: if the OrthogonalEdgeEditingContext is used to keep the edges orthogonal, it is not allowed // to change that edges manually. Therefore, we explicitly finish the OrthogonalEdgeEditingContext here and // then call the edge router. var edgeEditingContext = context.Lookup <OrthogonalEdgeEditingContext>(); if (edgeEditingContext != null && edgeEditingContext.IsInitialized) { edgeEditingContext.DragFinished(); } if (reshapeHandler != null) { reshapeHandler.ReshapeFinished(context, node.Layout.ToRectD(), node.Layout.ToRectD()); } if (compoundEdit != null) { compoundEdit.Commit(); } nodeAngles = null; ClearSameAngleHighlights(context); }
protected override IEnumerable <IPortCandidate> GetPortCandidates(IInputModeContext context) { var portCandidates = new List <IPortCandidate>(); // provide existing ports as candidates only if they use EventPortStyle and have no edges attached to them. foreach (IPort port in node.Ports) { if (port.Style is EventPortStyle && context.Lookup <IGraph>().EdgesAt(port).Count == 0) { portCandidates.Add(new DefaultPortCandidate(port)); } } if (node.Style is ActivityNodeStyle || node.Style is ChoreographyNodeStyle || node.Style is DataObjectNodeStyle || node.Style is AnnotationNodeStyle || node.Style is GroupNodeStyle || node.Style is DataStoreNodeStyle) { portCandidates.Add(new DefaultPortCandidate(node, FreeNodePortLocationModel.NodeTopAnchored)); portCandidates.Add(new DefaultPortCandidate(node, FreeNodePortLocationModel.NodeBottomAnchored)); portCandidates.Add(new DefaultPortCandidate(node, FreeNodePortLocationModel.NodeLeftAnchored)); portCandidates.Add(new DefaultPortCandidate(node, FreeNodePortLocationModel.NodeRightAnchored)); } else if (node.Style is EventNodeStyle || node.Style is GatewayNodeStyle) { double dmax = Math.Min(node.Layout.Width / 2, node.Layout.Height / 2); var model = FreeNodePortLocationModel.Instance; portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(0, -dmax)))); portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(dmax, 0)))); portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(0, dmax)))); portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(-dmax, 0)))); } else if (node.Style is ConversationNodeStyle) { double dx = 0.5 * Math.Min(node.Layout.Width, node.Layout.Height / BpmnConstants.ConversationWidthHeightRatio); double dy = dx * BpmnConstants.ConversationWidthHeightRatio; var model = FreeNodePortLocationModel.Instance; portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(0, -dy)))); portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(dx, 0)))); portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(0, dy)))); portCandidates.Add(new DefaultPortCandidate(node, model.CreateParameter(new PointD(0.5, 0.5), new PointD(-dx, 0)))); } var ceim = context.ParentInputMode as CreateEdgeInputMode; var canvasControl = context.CanvasControl; if (ceim == null || canvasControl == null || KeyEventRecognizers.ShiftPressed(canvasControl, canvasControl.LastMouse2DEvent)) { // add a dynamic candidate portCandidates.Add(new DefaultPortCandidate(node, new FreeNodePortLocationModel())); } return(portCandidates); }
protected virtual void Clear(IInputModeContext context) { var snapContext = context.Lookup <GraphSnapContext>(); if (snapContext != null) { snapContext.CollectSnapResults -= CollectSnapResults; } reshapeSnapResultProviders.Clear(); originalNodeLayouts.Clear(); reshapeHandlers.Clear(); orthogonalEdgeDragHandlers.Clear(); compoundEdit = null; }
public void Starting(object sender, InputModeEventArgs e) { IInputModeContext context = e.Context; var edgeEditingContext = context.Lookup <OrthogonalEdgeEditingContext>(); if (edgeEditingContext != null && !edgeEditingContext.IsInitializing && !edgeEditingContext.IsInitialized) { editingContext = edgeEditingContext; editingContext.InitializeDrag(context); } else { editingContext = null; } }
private bool IsTargetNodeOccupied(IInputModeContext context, INode node) { var ceim = context.ParentInputMode as CreateEdgeInputMode; var oeec = context.Lookup <OrthogonalEdgeEditingContext>(); if (oeec != null && oeec.Enabled) { // check whether there are already edges going in the same direction var graph = ceim.Graph; var dummyEdge = ceim.DummyEdge; var direction = dummyEdge.TargetPort.GetLocation() - (dummyEdge.Bends.Count > 0 ? dummyEdge.Bends.Last().Location.ToPointD() : dummyEdge.SourcePort.GetLocation()); var foldingView = graph.GetFoldingView(); if (foldingView != null && foldingView.Manager.MasterGraph.Contains(node)) { node = foldingView.GetViewItem(node); } if (node != null && graph.Contains(node)) { foreach (var edge in graph.EdgesAt(node)) { PointD p1, p2; if (edge.SourcePort.Owner == node) { p1 = edge.SourcePort.GetLocation(); p2 = edge.Bends.Count > 0 ? edge.Bends.First().Location.ToPointD() : edge.TargetPort.GetLocation(); } else { p1 = edge.TargetPort.GetLocation(); p2 = edge.Bends.Count > 0 ? edge.Bends.Last().Location.ToPointD() : edge.SourcePort.GetLocation(); } var edgeDirection = p2 - p1; // check whether the edge is orthogonal and points into the same direction. if ((edgeDirection.X == 0 || edgeDirection.Y == 0) && edgeDirection.ScalarProduct(direction) < 0) { return(true); } } } } return(false); }
public virtual void InitializeReshape(IInputModeContext context) { this.OriginalBounds = rectangle.TightRectangle; // register our CollectSnapResults callback var snapContext = context.Lookup <GraphSnapContext>(); if (snapContext != null) { snapContext.CollectSnapResults += CollectSnapResults; } // store original node layouts, reshape handlers and reshape snap result providers foreach (var node in ReshapeNodes) { originalNodeLayouts.Add(node, node.Layout.ToRectD()); // store reshape handler to change the shape of node var reshapeHandler = node.Lookup <IReshapeHandler>(); if (reshapeHandler != null) { reshapeHandler.InitializeReshape(context); reshapeHandlers.Add(node, reshapeHandler); } // store reshape snap result provider to collect snap results where node would snap to snaplines etc. var snapResultProvider = node.Lookup <INodeReshapeSnapResultProvider>(); if (snapContext != null && snapResultProvider != null) { reshapeSnapResultProviders.Add(node, snapResultProvider); } // store orthogonal edge drag handler that keeps edges at node orthogonal var orthogonalEdgeDragHandler = OrthogonalEdgeEditingContext.CreateOrthogonalEdgeDragHandler(context, node, false); if (orthogonalEdgeDragHandler != null) { orthogonalEdgeDragHandlers[node] = orthogonalEdgeDragHandler; } } // update the minimum/maximum size of the handle considering all initial node layouts Handle.MinimumSize = CalculateMinimumSize(); Handle.MaximumSize = CalculateMaximumSize(); // start a compound undo unit this.compoundEdit = context.GetGraph().BeginEdit("Undo Group Resize", "Redo Group Resize"); }
/// <summary> /// Snaps the angle to the rotation angles of other nodes and the coordinate axes. /// </summary> /// <remarks> /// Angles near such an angle are replaced with this angle. /// </remarks> private double SnapAngle(IInputModeContext context, double angle) { // Check for disabled snapping var snapContext = context.Lookup <SnapContext>(); if (snapContext != null && !snapContext.Enabled) { return(angle); } // Same angle snapping if (SnapToSameAngleDelta > 0 && nodeAngles != null) { // Find the first angle that is sufficiently similar var candidate = nodeAngles.Where(na => CachingOrientedRectangle.NormalizeAngle(Math.Abs(na.Item1 - angle)) < SnapToSameAngleDelta).OrderBy(na => na.Item1).FirstOrDefault(); if (candidate != null) { // Add highlight to every matching node var canvas = (GraphControl)context.CanvasControl; if (sameAngleHighlightedNodes != candidate.Item2) { ClearSameAngleHighlights(context); } foreach (var matchingNode in candidate.Item2) { canvas.HighlightIndicatorManager.AddHighlight(matchingNode); } sameAngleHighlightedNodes = candidate.Item2; return(candidate.Item1); } ClearSameAngleHighlights(context); } if (SnapDelta <= 0.0 || SnapStep == 0) { return(angle); } var mod = Math.Abs(angle % SnapStep); return((mod < SnapDelta || mod > SnapStep - SnapDelta) ? SnapStep * Math.Round(angle / SnapStep) : angle); }
/// <summary> /// Delegates to the wrapped context's lookup but cancels the snap context. /// </summary> public object Lookup(Type type) { return(type == typeof(SnapContext) ? null : context.Lookup(type)); }
/// <summary> /// Returns the group node at the given location. /// </summary> /// <remarks> /// If there is no group node, <code>null</code> is returned. /// </remarks> private INode GetHitGroupNode(IInputModeContext context, PointD location) { return(context.Lookup <IHitTester <INode> >() .EnumerateHits(context, location) .FirstOrDefault(n => graphControl.Graph.IsGroupNode(n))); }