Exemplo n.º 1
0
		/// <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();
		}