/// <summary> /// Handles the drawing and updating of the graph. /// </summary> protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); // calculate the mouse position in the graph PointF graphMousePos= _nodeLayoutManager.ViewToGraph(_lastMousePosition); // when the layout was changed it needs to be recalculated bool layoutChanged= _nodeLayoutManager.LayoutChanged; if(layoutChanged) _nodeLayoutManager.UpdateLayout(e.Graphics); // centre the root behaviour if requested if(_pendingCentreBehavior) { _pendingCentreBehavior= false; CenterNode(_rootNode); } // select the pending node if(_selectedNodePending !=null) { SelectedNode= _selectedNodePendingParent.GetChild(_selectedNodePending); _selectedNodePending= null; _selectedNodePendingParent= null; if(ClickNode !=null) ClickNode(_selectedNode); } // check if we must keep the original position of the mouse if(_maintainMousePosition) { _maintainMousePosition= false; // move the graph so that _graphOrigin is at the same position in the view as it was before float mouseX= (graphMousePos.X - _graphOrigin.X) * _nodeLayoutManager.Scale + _nodeLayoutManager.Offset.X; float mouseY= (graphMousePos.Y - _graphOrigin.Y) * _nodeLayoutManager.Scale + _nodeLayoutManager.Offset.Y; _nodeLayoutManager.Offset= new PointF(mouseX, mouseY); } // check if we must keep the original position of _maintainNodePosition else if(_maintainNodePosition !=null) { // move the graph so that _graphOrigin is at the same position in the view as it was before RectangleF bbox= _maintainNodePosition.BoundingBox; PointF viewpos= new PointF(bbox.Location.X * _nodeLayoutManager.Scale, bbox.Location.Y * _nodeLayoutManager.Scale); _nodeLayoutManager.Offset= new PointF(_graphOrigin.X - viewpos.X, _graphOrigin.Y - viewpos.Y); } // reset the node whose position we want to keep _maintainNodePosition= null; // draw the graph to the view _nodeLayoutManager.DrawGraph(e.Graphics, _currentNode, _selectedNode, graphMousePos); // check if we are currently dragging a node and we must draw additional data if(_dragTargetNode !=null && _dragTargetNode.Node !=_movedNode) { if(_dragAttachMode == NodeAttachMode.Event) { // we could draw some stuff for events here } else { // draw the arrows for the attach modes // get the bounding box of the node RectangleF bbox= _dragTargetNode.BoundingBox; // get the bounding box of the connector _dragTargetConnector= null; // the depth of the area for the mouse const float offset= 20.0f; // the distance of the arrow from the border and its height const float innerOffset= 6.0f; // the horizintal middle of the node float centerX= bbox.Left + bbox.Width *0.5f; // the half width of the arrow depending of the node's height float arrowHalfWidth= (bbox.Height - innerOffset - innerOffset) *0.5f; // calculate the mouse areas for the different attach modes RectangleF top= new RectangleF(bbox.X + offset, bbox.Top, bbox.Width - offset - offset, offset); RectangleF bottom= new RectangleF(bbox.X + offset, bbox.Bottom - offset, bbox.Width - offset - offset, offset); RectangleF left= new RectangleF(bbox.X, bbox.Y, offset, bbox.Height); // update for dragging in a new node BehaviorNode behavior= _dragNodeDefaults as BehaviorNode; if(behavior !=null && behavior.FileManager ==null) behavior= null; bool hasParentBehavior= _dragTargetNode.HasParentBehavior(behavior); bool parentHasParentBehavior= _dragTargetNode.Parent !=null && _dragTargetNode.Parent.HasParentBehavior(behavior); bool mayTop= _dragTargetNode.Node.Parent !=null && !parentHasParentBehavior && _dragTargetNode.Node.ParentConnector.AcceptsChildren(1); bool mayBottom= _dragTargetNode.Node.Parent !=null && !parentHasParentBehavior && _dragTargetNode.Node.ParentConnector.AcceptsChildren(1); bool mayLeft= _dragTargetNode.Parent !=null && !parentHasParentBehavior && !hasParentBehavior && !_dragTargetNode.Node.ParentConnector.IsReadOnly && _dragNodeDefaults is Node && ((Node)_dragNodeDefaults).CanAdoptNode(_dragTargetNode.Node) && !(_dragNodeDefaults.GetType() is BehaviorNode); // update for moving an existing node bool dragTargetHasParentMovedNode= false; if(_movedNode !=null) { dragTargetHasParentMovedNode= _keyShiftIsDown && _dragTargetNode.HasParent(_movedNode); // a node may not dragged on itself and may not dragged on one of its own children if(_dragTargetNode.Node ==_movedNode || dragTargetHasParentMovedNode) { mayTop= false; mayBottom= false; mayLeft= false; } else { // a dragged node cannot be placed in the same position again mayTop= mayTop && _dragTargetNode.Node.PreviousNode !=_movedNode; mayBottom= mayBottom && _dragTargetNode.Node.NextNode !=_movedNode; mayLeft= mayLeft && _movedNode.CanAdoptChildren(_dragTargetNode.Node) && (!_keyShiftIsDown || _movedNode.Children.Count ==0); } } else if(_copiedNode !=null) { mayLeft= mayLeft && _copiedNode.CanAdoptChildren(_dragTargetNode.Node) && (!_keyShiftIsDown || _copiedNode.Children.Count ==0); } // reset the attach mode _dragAttachMode= NodeAttachMode.None; // the vertices needed to draw the arrows PointF[] vertices= new PointF[3]; // draw the top arrow if this action is allowed if(mayTop) { vertices[0]= new PointF(centerX - arrowHalfWidth, top.Bottom - innerOffset); vertices[1]= new PointF(centerX, top.Top + innerOffset); vertices[2]= new PointF(centerX + arrowHalfWidth, top.Bottom - innerOffset); if(top.Contains(graphMousePos)) { _dragAttachMode= NodeAttachMode.Top; e.Graphics.FillPolygon(Brushes.White, vertices); } else e.Graphics.FillPolygon(Brushes.Black, vertices); } // draw the bottom arrow if this action is allowed if(mayBottom) { vertices[0]= new PointF(centerX - arrowHalfWidth, bottom.Top + innerOffset); vertices[1]= new PointF(centerX + arrowHalfWidth, bottom.Top + innerOffset); vertices[2]= new PointF(centerX, bottom.Bottom - innerOffset); if(_dragAttachMode ==NodeAttachMode.None && bottom.Contains(graphMousePos)) { _dragAttachMode= NodeAttachMode.Bottom; e.Graphics.FillPolygon(Brushes.White, vertices); } else e.Graphics.FillPolygon(Brushes.Black, vertices); } // draw the left arrow if this action is allowed if(mayLeft) { vertices[0]= new PointF(left.Right - innerOffset, left.Top + innerOffset); vertices[1]= new PointF(left.Right - innerOffset, left.Bottom - innerOffset); vertices[2]= new PointF(left.Left + innerOffset, left.Top + left.Height *0.5f); if(_dragAttachMode ==NodeAttachMode.None && left.Contains(graphMousePos)) { _dragAttachMode= NodeAttachMode.Left; e.Graphics.FillPolygon(Brushes.White, vertices); } else e.Graphics.FillPolygon(Brushes.Black, vertices); } // draw the right arrow if this action is allowed foreach(Node.Connector connector in _dragTargetNode.Node.Connectors) { RectangleF bboxConnector= _dragTargetNode.Node.GetConnectorBoundingBox(bbox, connector); //e.Graphics.DrawRectangle(Pens.Red, bboxConnector.X, bboxConnector.Y, bboxConnector.Width, bboxConnector.Height); RectangleF right= new RectangleF(bboxConnector.Right - offset, bboxConnector.Y, offset, bboxConnector.Height); bool mayRight= !dragTargetHasParentMovedNode && !hasParentBehavior && connector.AcceptsChildren(1); if(mayRight && _movedNode !=null && connector ==_movedNode.ParentConnector) mayRight= false; if(mayRight) { float inOffset= bboxConnector.Height >innerOffset *4.0f ? innerOffset : 3.0f; vertices[0]= new PointF(right.Left + inOffset, right.Top + inOffset); vertices[1]= new PointF(right.Right - inOffset, right.Top + right.Height *0.5f); vertices[2]= new PointF(right.Left + inOffset, right.Bottom - inOffset); if(_dragAttachMode ==NodeAttachMode.None && right.Contains(graphMousePos)) { _dragTargetConnector= connector; _dragAttachMode= NodeAttachMode.Right; e.Graphics.FillPolygon(Brushes.White, vertices); } else e.Graphics.FillPolygon(Brushes.Black, vertices); } } } } // draw last mouse pos //e.Graphics.DrawRectangle(Pens.Red, graphMousePos.X -1.0f, graphMousePos.Y -1.0f, 2.0f, 2.0f); //when we are dragging an existing node we draw a small graph representing it if(_movedNodeGraph !=null) { // update the layout for the graph. This happens only once inside the function. _movedNodeGraph.UpdateLayout(e.Graphics); // offset the graph to the mouse position _movedNodeGraph.Offset= new PointF(_nodeLayoutManager.Offset.X + graphMousePos.X * _nodeLayoutManager.Scale, _nodeLayoutManager.Offset.Y + graphMousePos.Y * _nodeLayoutManager.Scale - _movedNodeGraph.RootNodeLayout.LayoutRectangle.Height *0.5f * _movedNodeGraph.Scale); // draw the graph _movedNodeGraph.DrawGraph(e.Graphics, null, null, graphMousePos); } //if(_currentNode !=null && _currentNode.SubItems.Count >0) // Invalidate(); }