/// <summary> /// Attaches a dragged node from the node explorer to an existing node. /// </summary> /// <param name="nvd">The node the new node will be attached to.</param> /// <param name="mode">The way the new node will be attached.</param> /// <param name="nodetag">The tag of the you want to create.</param> /// <param name="label">The label of the new node.</param> private void InsertNewNode(NodeViewData nvd, NodeAttachMode mode, NodeTag nodetag, PointF mousePos) { if (nodetag.Type != NodeTagType.Behavior && nodetag.Type != NodeTagType.Prefab && nodetag.Type != NodeTagType.Node) { throw new Exception("Only behaviours, prefabs and nodes can be attached to a behaviour tree"); } Node newnode = null; bool isPrefabInstance = false; // when we attach a behaviour we must create a special referenced behaviour node if (nodetag.Type == NodeTagType.Behavior || nodetag.Type == NodeTagType.Prefab) { if (!File.Exists(nodetag.Filename)) { if (!SaveBehavior(nodetag.Filename)) { return; } } // get the behavior we want to reference BehaviorNode behavior = _behaviorTreeList.LoadBehavior(nodetag.Filename); // a behaviour reference itself if (nvd != null && nvd.IsBehaviorReferenced(behavior)) { if (DialogResult.Cancel == MessageBox.Show(Resources.CircularReferencedInfo, Resources.Warning, MessageBoxButtons.OKCancel, MessageBoxIcon.Warning)) { return; } } if (nodetag.Type == NodeTagType.Prefab) { behavior = (BehaviorNode)behavior.Clone(); } Behavior rootB = _rootNodeView.RootBehavior as Behavior; Behavior b = behavior as Behavior; if (rootB.AgentType == null) { rootB.AgentType = b.AgentType; } else if (!IsCompatibleAgentType(rootB, b)) { return; } // behavior if (nodetag.Type == NodeTagType.Behavior) { // create the referenced behaviour node for the behaviour ReferencedBehavior refnode = Node.CreateReferencedBehaviorNode(_rootNodeView.RootBehavior, behavior, mode == NodeAttachMode.None || ((Node)this.RootNode).IsFSM); newnode = (Node)refnode; //the comment seems too long to overlap the node //newnode.CommentText = Resources.ThisIsReferenceTree; newnode.CommentBackground = Node.CommentColor.Gray; } // prefab else { // Copy all Pars from the prefab file into the current behavior node. List<ParInfo> pars = new List<ParInfo>(); foreach(ParInfo par in b.LocalVars) { bool found = false; foreach(ParInfo rootPar in rootB.LocalVars) { if (par.Name == rootPar.Name) { if (par.Type != rootPar.Type) { string errorMsg = string.Format(Resources.ParErrorInfo, par.Name, b.Label, rootB.Label); MessageBox.Show(errorMsg, Resources.LoadError, MessageBoxButtons.OK); return; } found = true; break; } } if (!found) { pars.Add(par); } } rootB.LocalVars.AddRange(pars); // The first child should be the root node of the prefab tree. Node behaviorNode = (Node)behavior; if (behaviorNode.Children.Count > 0) { newnode = (Node)behaviorNode.Children[0]; string prefab = Path.GetFileNameWithoutExtension(behavior.Filename); newnode.CommentText = string.Format("Prefab[{0}]", prefab); isPrefabInstance = true; string prefabName = FileManagers.FileManager.GetRelativePath(behavior.Filename); newnode.SetPrefab(prefabName); } } } else { // simply create the node which is supposed to be created. newnode = Node.Create(nodetag.NodeType); if (newnode != null) newnode.PostCreatedByEditor(); } if (newnode == null) { return; } // update label newnode.OnPropertyValueChanged(false); Node node = (nvd != null) ? nvd.Node : null; if (node == null) { mode = NodeAttachMode.None; } Attachments.Attachment startCondition = null; // attach the new node with the correct mode switch (mode) { // the new node is inserted in front of the target node case NodeAttachMode.Left: if (node != null) { Node parent = (Node)node.Parent; int k = node.ParentConnector.GetChildIndex(node); Node.Connector conn = node.ParentConnector; Debug.Check(conn != null); parent.RemoveChild(conn, node); parent.AddChild(conn, newnode, k); Node.Connector newconn = newnode.GetConnector(conn.Identifier); Debug.Check(newconn != null); newnode.AddChild(newconn, node); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; } break; // the new node is simply added to the target node's children case NodeAttachMode.Right: if (newnode != null && newnode.IsFSM) { startCondition = this.addFSMNode(newnode, mousePos); newnode.ScreenLocation = new PointF(_rootNodeView.BoundingBox.Left + _rootNodeView.BoundingBox.Width * 1.5f, _rootNodeView.BoundingBox.Top); } else if (node != null) { node.AddChild(_dragTargetConnector, newnode); _dragTargetConnector.IsExpanded = true; // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd; } break; // the new node is placed above the target node case NodeAttachMode.Top: if (node != null) { int n = _dragTargetNode.Node.ParentConnector.GetChildIndex(node); ((Node)node.Parent).AddChild(_dragTargetNode.Node.ParentConnector, newnode, n); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; } break; // the new node is placed below the target node case NodeAttachMode.Bottom: if (node != null) { int m = _dragTargetNode.Node.ParentConnector.GetChildIndex(node); ((Node)node.Parent).AddChild(_dragTargetNode.Node.ParentConnector, newnode, m + 1); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; } break; // the node will replace the target node case NodeAttachMode.Center: if (node != null && replaceNode(node, newnode)) { // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; this.Redraw(); } break; case NodeAttachMode.None: if (newnode != null && newnode.IsFSM) { startCondition = this.addFSMNode(newnode, mousePos); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = this.RootNodeView; } break; } // After being created, its Id should be reset. if (newnode != null) { newnode.ResetId(true); // set the prefab dirty for the current parent if (newnode.Parent != null) { Node parent = (Node)newnode.Parent; if (!string.IsNullOrEmpty(parent.PrefabName)) { parent.HasOwnPrefabData = true; if (!isPrefabInstance) { newnode.SetPrefab(parent.PrefabName); newnode.HasOwnPrefabData = true; } } } if (startCondition != null) { startCondition.TargetFSMNodeId = newnode.Id; } UndoManager.Save(this.RootNode); } // the layout needs to be recalculated LayoutChanged(); }
/// <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, _forceChangeLayout); _forceChangeLayout = false; } // center the root behaviour if requested if (_pendingCenterBehavior) { _pendingCenterBehavior = false; CenterNode(_rootNodeView); } // select the pending node if (_selectedNodePending != null) { if (_selectedNodePendingParent != null) { if (_selectedNodePendingParent.CanBeExpanded() && !_selectedNodePendingParent.IsExpanded) { _selectedNodePendingParent.IsExpanded = true; LayoutChanged(); } SelectedNode = _selectedNodePendingParent.GetChild(_selectedNodePending); } else { SelectedNode = RootNodeView.FindNodeViewData(_selectedNodePending); } if (SelectedNode != null) { if (SelectedNode.CanBeExpanded() && !SelectedNode.IsExpanded) { SelectedNode.IsExpanded = true; LayoutChanged(); } SelectedNode.SelectedSubItem = SelectedNode.GetSubItem(_selectedAttachmentPending); ShowNode(SelectedNode); } _selectedNodePending = null; _selectedNodePendingParent = null; _selectedAttachmentPending = null; if (ClickEvent != null) { ClickEvent(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.IsFSM ? _maintainNodePosition.GetTotalBoundingBox() : _maintainNodePosition.BoundingBox; PointF viewpos = new PointF(bbox.X * _nodeLayoutManager.Scale, bbox.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, graphMousePos, _currentNode, SelectedNode, _highlightedNodeIds, _updatedNodeIds, _highlightedTransitionIds, _highlightBreakPoint, _profileInfos); // check if we are currently dragging a node and we must draw additional data if (_dragTargetNode != null && _dragAttachment == null && (KeyCtrlIsDown && _movedNode != null || _dragTargetNode.Node != _movedNode)) { if (_dragAttachMode == NodeAttachMode.Attachment) { // we could draw some stuff for attachements 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 = 12.0f; // the distance of the arrow from the border and its height const float innerOffset = 2.0f; // the horizintal middle of the node float centerX = bbox.Left + bbox.Width * 0.5f; float centerY = bbox.Top + bbox.Height * 0.5f; float centerBoxX = bbox.X + bbox.Width * 0.4f; float centerBoxWidth = bbox.Width * 0.2f; // 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(centerBoxX, bbox.Top, centerBoxWidth, offset); RectangleF bottom = new RectangleF(centerBoxX, bbox.Bottom - offset, centerBoxWidth, offset); RectangleF center = new RectangleF(centerBoxX, centerY - offset * 0.5f, centerBoxWidth, 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; } // the node that is currently dragged Node draggedNode = (_movedNode != null) ? _movedNode : (_dragNodeDefaults as Node); if (draggedNode == null) { return; } Node.Connector parentConnector = _dragTargetNode.Node.ParentConnector; bool canBeAdoptedByParent = parentConnector != null && (!parentConnector.IsAsChild || AdoptNodeByAncestor(_dragTargetNode.Node.Parent, draggedNode)); //bool targetCanBeAdoptedByParent = (_movedNode == null) || (_movedNode.ParentConnector != null) && _movedNode.ParentConnector.AcceptsChild(_dragTargetNode.Node); bool hasParentBehavior = _dragTargetNode.HasParentBehavior(behavior); bool parentHasParentBehavior = (_dragTargetNode.Parent == null); bool isFSM = _rootNodeView.IsFSM || (_rootNodeView.Children.Count == 0); bool mayTop = !isFSM && canBeAdoptedByParent /*&& targetCanBeAdoptedByParent*/ && !parentHasParentBehavior && parentConnector != null && parentConnector.AcceptsChild(draggedNode); bool mayBottom = mayTop; bool mayCenter = !parentHasParentBehavior && (_rootNodeView.IsFSM && draggedNode.IsFSM || canBeAdoptedByParent && draggedNode.GetType() != _dragTargetNode.Node.GetType() && parentConnector != null && !parentConnector.IsReadOnly && parentConnector.AcceptsChild(draggedNode, true) && draggedNode.CanAdoptChildren(_dragTargetNode.Node)); bool mayLeft = !isFSM && (_dragTargetNode.Node.Parent != _movedNode) && canBeAdoptedByParent && !parentHasParentBehavior && !hasParentBehavior && parentConnector != null && !parentConnector.IsReadOnly && parentConnector.AcceptsChild(draggedNode, true) && !(draggedNode is BehaviorNode) && draggedNode.CanAdoptNode(_dragTargetNode.Node); // update for moving an existing node bool dragTargetHasParentMovedNode = false; if (_movedNode != null) { mayCenter = false; dragTargetHasParentMovedNode = KeyShiftIsDown && _dragTargetNode.Node.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 = KeyCtrlIsDown; mayTop &= KeyCtrlIsDown && (_dragTargetNode.Node.ParentConnector != null) && _dragTargetNode.Node.ParentConnector.AcceptsChild(_movedNode); mayBottom = mayTop; mayLeft = false; } else { // a dragged node cannot be placed in the same position again mayTop &= (KeyCtrlIsDown || _dragTargetNode.Node.PreviousNode != _movedNode); mayBottom &= (KeyCtrlIsDown || _dragTargetNode.Node.NextNode != _movedNode); mayLeft &= _movedNode.CanAdoptChildren(_dragTargetNode.Node) && (!KeyShiftIsDown || _movedNode.Children.Count == 0); } } if (_copiedNode != null) { mayCenter = false; mayLeft &= _copiedNode.CanAdoptChildren(_dragTargetNode.Node) && (!KeyShiftIsDown || _copiedNode.Children.Count == 0); mayTop &= (_dragTargetNode.Node.ParentConnector != null) && _dragTargetNode.Node.ParentConnector.AcceptsChild(_copiedNode); mayBottom = mayTop; } else if (_clipboardPasteMode) { mayCenter = false; mayLeft &= !KeyShiftIsDown; } // 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 center rectangle if this action is allowed if (mayCenter) { if (center.Contains(graphMousePos)) { _dragAttachMode = NodeAttachMode.Center; e.Graphics.FillRectangle(Brushes.White, centerX - arrowHalfWidth * 0.5f, centerY - innerOffset * 2.0f, arrowHalfWidth, innerOffset * 4.0f); } else { e.Graphics.FillRectangle(Brushes.Black, centerX - arrowHalfWidth * 0.5f, centerY - innerOffset * 2.0f, arrowHalfWidth, innerOffset * 4.0f); } } // 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.Connectors) { RectangleF bboxConnector = _dragTargetNode.GetConnectorBoundingBox(bbox, connector); RectangleF right = new RectangleF(bboxConnector.Right - offset, bboxConnector.Y, offset, bboxConnector.Height); bool mayRight = !_rootNodeView.IsFSM && !dragTargetHasParentMovedNode && !hasParentBehavior && !connector.IsReadOnly && connector.AcceptsChild(draggedNode) && (!connector.IsAsChild || AdoptNodeByAncestor(_dragTargetNode.Node, draggedNode)); if (mayRight && draggedNode != null && connector == draggedNode.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 = _dragTargetNode.Node.GetConnector(connector.Identifier); _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, graphMousePos); } // attachment if (_currentNode != null && _dragTargetNode != null && _dragAttachment != null && _dragTargetAttachment != null && _dragAttachment != _dragTargetAttachment && _dragTargetNode.Node.AcceptsAttachment(_dragAttachment.Attachment)) { _dragAttachMode = NodeAttachMode.None; Attachments.Attachment sourceAttach = _dragAttachment.SelectableObject as Attachments.Attachment; Attachments.Attachment targetAttach = _dragTargetAttachment.SelectableObject as Attachments.Attachment; if (sourceAttach != null && targetAttach != null && sourceAttach.IsPrecondition == targetAttach.IsPrecondition && sourceAttach.IsTransition == targetAttach.IsTransition && sourceAttach.IsEffector == targetAttach.IsEffector) { SubItemRegin regin = _dragTargetNode.GetSubItemRegin(graphMousePos); int itemIndex = _currentNode.GetSubItemIndex(_dragAttachment); int targetItemIndex = _dragTargetNode.GetSubItemIndex(_dragTargetAttachment); if (regin != SubItemRegin.Out && itemIndex >= 0 && targetItemIndex >= 0) { RectangleF bbox = _dragTargetNode.GetSubItemBoundingBox(graphMousePos); const float offset = 8.0f; const float innerOffset = 2.0f; float centerX = bbox.Left + bbox.Width * 0.5f; float centerY = bbox.Top + bbox.Height * 0.5f; float centerBoxX = bbox.X + bbox.Width * 0.4f; float arrowHalfWidth = bbox.Width * 0.12f; RectangleF top = new RectangleF(centerX - arrowHalfWidth, bbox.Top, arrowHalfWidth * 2.0f, offset); RectangleF bottom = new RectangleF(centerX - arrowHalfWidth, bbox.Bottom - offset, arrowHalfWidth * 2.0f, offset); PointF[] vertices = new PointF[3]; switch (regin) { case SubItemRegin.Top: if (KeyCtrlIsDown || _currentNode != _dragTargetNode || itemIndex != targetItemIndex - 1) { 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); } } break; case SubItemRegin.Bottom: if (KeyCtrlIsDown || _currentNode != _dragTargetNode || itemIndex != targetItemIndex + 1) { 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 (bottom.Contains(graphMousePos)) { _dragAttachMode = NodeAttachMode.Bottom; e.Graphics.FillPolygon(Brushes.White, vertices); } else { e.Graphics.FillPolygon(Brushes.Black, vertices); } } break; } } } } if (_movedSubItem != null) { NodeViewData.SubItemText subitem = _movedSubItem as NodeViewData.SubItemText; if (subitem != null) { RectangleF boundingBox = new RectangleF(graphMousePos, new SizeF(50, 12)); e.Graphics.FillRectangle(subitem.BackgroundBrush, boundingBox); } } // draw FSM related DrawFSMArrow(e, graphMousePos); DrawFSMDragCurve(e, graphMousePos); //the first time of paint, to collapse plan failed branch by default Behavior b = this.RootNode as Behavior; if (b.PlanIsCollapseFailedBranch > 0) { if (b.PlanIsCollapseFailedBranch == Behavior.kPlanIsCollapseFailedBranch) { NodeViewData root = (NodeViewData)this.RootNodeView.Children[0]; CollapseFailedBrach(root); } b.PlanIsCollapseFailedBranch--; this.CenterNode(this._rootNodeView); this.LayoutChanged(); } }
/// <summary> /// Handles when a tree node is dragged on the view. /// </summary> private void BehaviorTreeView_DragOver(object sender, DragEventArgs e) { // get the node we are dragging over Point pt = PointToClient(new Point(e.X, e.Y)); NodeViewData nodeFound = _rootNodeView.GetInsideNode(new PointF(pt.X, pt.Y)); // update last know mouse position _lastMousePosition = new PointF(pt.X, pt.Y); // update the current node if (nodeFound != _currentNode) { _currentNode = nodeFound; Invalidate(); } // when we are moving on a node we must keep drawing as the ttach ode might change but not the node else if (nodeFound != null) { Invalidate(); } // store the target node _dragTargetNode = _currentNode; // deny drop by default e.Effect = DragDropEffects.None; // process dragging the property and method string value from the Meta Browser string dragItem = (string)e.Data.GetData(DataFormats.Text); if (!string.IsNullOrEmpty(dragItem) && _dragTargetNode != null && _dragTargetNode.Node != null && _dragTargetNode.Node.AcceptDefaultPropertyByDragAndDrop()) { e.Effect = DragDropEffects.Move; _dragAttachMode = NodeAttachMode.None; return; } // make sure the correct drag attach mode is set if (_dragTargetNode != null) { if (_dragNodeDefaults is Nodes.Node && _dragAttachMode == NodeAttachMode.Attachment) { _dragAttachMode = NodeAttachMode.None; } else if (_dragNodeDefaults is Attachments.Attachment && _dragAttachMode != NodeAttachMode.Attachment) { _dragAttachMode = NodeAttachMode.Attachment; } } // check if we are trying to drop a node on another one if (_dragTargetNode != null && (e.KeyState & 1/*left mouse button*/) > 0) { if (_dragNodeDefaults != null && (_dragNodeDefaults is Nodes.Node && (_dragAttachMode != NodeAttachMode.None || !(_dragNodeDefaults is Nodes.BehaviorNode) || !(_dragTargetNode.Node is Nodes.BehaviorNode)) || _dragNodeDefaults is Attachments.Attachment && _dragTargetNode.Node.AcceptsAttachment(_dragNodeDefaults))) { e.Effect = DragDropEffects.Move; } } // If this is an empty node, no effect for it. if (_dragTargetNode == null || _dragTargetNode.Node == null || _dragNodeDefaults == null || _dragAttachMode == NodeAttachMode.None && !_dragTargetNode.Node.AcceptsAttachment(_dragNodeDefaults)) { e.Effect = DragDropEffects.None; } if (_rootNodeView.IsFSM || _rootNodeView.Children.Count == 0) { // fsm or empty behavior if (_dragNodeDefaults != null) { if (_dragNodeDefaults is Node) { Node dragNode = _dragNodeDefaults as Node; if (dragNode.IsFSM || dragNode is Behavior) { if (_dragTargetNode == null) { e.Effect = DragDropEffects.Move; } //else if (_dragNodeDefaults is Nodes.BehaviorNode) { // e.Effect = DragDropEffects.None; //} } } else if (_dragNodeDefaults is Attachments.Attachment) { Attachments.Attachment dragAttachment = _dragNodeDefaults as Attachments.Attachment; if (dragAttachment.IsFSM) { if (_dragTargetNode != null && !(_dragTargetNode.Node is Nodes.BehaviorNode) && _dragTargetNode.Node.AcceptsAttachment(dragAttachment)) { e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } } } } }
/// <summary> /// Handles when dropping a tree node on the view. /// </summary> private void BehaviorTreeView_DragDrop(object sender, DragEventArgs e) { // make sure the view is focused Focus(); // drag the property or method into the node string dragItem = (string)e.Data.GetData(DataFormats.Text); if (!string.IsNullOrEmpty(dragItem) && _dragTargetNode != null && _dragTargetNode.Node != null) { if (_dragTargetNode.Node.SetDefaultPropertyByDragAndDrop(dragItem)) { if (ClickNode != null) { ClickNode(_dragTargetNode); } UndoManager.Save(this.RootNode); LayoutChanged(); } return; } // get source node TreeNode sourceNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode"); if (sourceNode == null) { return; } NodeTag sourceNodeTag = (NodeTag)sourceNode.Tag; // keep the current node position //KeepNodePosition(_dragTargetNode); bool bDragBTOverNode = !((Node)this.RootNode).IsFSM && sourceNodeTag.Type == NodeTagType.Behavior && _dragTargetNode is Behaviac.Design.NodeViewData; // check if we are dropping an attach // or if we are dropping a bt to a node and the indicator is not left/right/up/bottom/center if (_dragAttachMode == NodeAttachMode.Attachment || (bDragBTOverNode && (_dragAttachMode == NodeAttachMode.None || _dragAttachMode == NodeAttachMode.Attachment))) { Attachments.Attachment attach = null; // when we attach a behaviour we must create a special referenced behaviour node if (bDragBTOverNode) { //drag an event(a bt) to a node if (!File.Exists(sourceNodeTag.Filename)) { MainWindow.Instance.SaveBehavior(sourceNodeTag.Defaults as Nodes.BehaviorNode, false); } if (File.Exists(sourceNodeTag.Filename)) { // get the behavior we want to reference BehaviorNode behavior = _behaviorTreeList.LoadBehavior(sourceNodeTag.Filename); Behavior rootB = _rootNodeView.RootBehavior as Behavior; Behavior b = behavior as Behavior; if (!b.CanBeAttached || !IsCompatibleAgentType(rootB, b)) return; attach = Behaviac.Design.Attachments.Attachment.Create(typeof(Behaviac.Design.Attachments.Event), _dragTargetNode.Node); Behaviac.Design.Attachments.Event evt = (Behaviac.Design.Attachments.Event)attach; evt.ReferencedBehavior = behavior; } } else if (_dragTargetNode != null) { Debug.Check(_dragAttachMode == NodeAttachMode.Attachment); // add the attach to the target node attach = Behaviac.Design.Attachments.Attachment.Create(sourceNodeTag.NodeType, _dragTargetNode.Node); } if (_dragTargetNode != null && attach != null && _dragTargetNode.Node.AcceptsAttachment(attach)) { attach.OnPropertyValueChanged(false); attach.ResetId(); _dragTargetNode.Node.AddAttachment(attach); NodeViewData.SubItemAttachment sub = attach.CreateSubItem(); _dragTargetNode.AddSubItem(sub); SelectedNode = _dragTargetNode; SelectedNode.SelectedSubItem = sub; // call the ClickEvent event handler if (ClickEvent != null) { ClickEvent(SelectedNode); } UndoManager.Save(this.RootNode); LayoutChanged(); } } //else if (_dragAttachMode != NodeAttachMode.None) else { // attach a new node to the target node InsertNewNode(_dragTargetNode, _dragAttachMode, sourceNodeTag, this.PointToClient(new Point(e.X, e.Y))); } // reset drag stuff _dragTargetNode = null; _dragNodeDefaults = null; _dragAttachMode = NodeAttachMode.None; Invalidate(); }
/// <summary> /// Attaches a dragged node from the node explorer to an existing node. /// </summary> /// <param name="nvd">The node the new node will be attached to.</param> /// <param name="mode">The way the new node will be attached.</param> /// <param name="nodetag">The tag of the you want to create.</param> /// <param name="label">The label of the new node.</param> private void InsertNewNode(NodeViewData nvd, NodeAttachMode mode, NodeTag nodetag) { // check if the attach mode is valid if (mode == NodeAttachMode.Attachment || mode == NodeAttachMode.None) throw new Exception("A node cannot be created with the given attach mode"); if (nodetag.Type != NodeTagType.Behavior && nodetag.Type != NodeTagType.Prefab && nodetag.Type != NodeTagType.Node) throw new Exception("Only behaviours, prefabs and nodes can be attached to a behaviour tree"); Node node = nvd.Node; Node newnode = null; bool isPrefabInstance = false; // when we attach a behaviour we must create a special referenced behaviour node if (nodetag.Type == NodeTagType.Behavior || nodetag.Type == NodeTagType.Prefab) { if (!File.Exists(nodetag.Filename)) { if (!SaveBehavior(nodetag.Filename)) return; } // get the behavior we want to reference BehaviorNode behavior = _behaviorTreeList.LoadBehavior(nodetag.Filename); // a behaviour reference itself if (nvd.IsBehaviorReferenced(behavior)) { if (DialogResult.Cancel == MessageBox.Show(Resources.CircularReferencedInfo, Resources.Warning, MessageBoxButtons.OKCancel, MessageBoxIcon.Warning)) return; } if (nodetag.Type == NodeTagType.Prefab) { behavior = (BehaviorNode)behavior.Clone(); } Behavior rootB = _rootNode.RootBehavior as Behavior; Behavior b = behavior as Behavior; if (rootB.AgentType == null) { rootB.AgentType = b.AgentType; } else if (!IsCompatibleAgentType(rootB, b)) { return; } // behavior if (nodetag.Type == NodeTagType.Behavior) { // create the referenced behaviour node for the behaviour ReferencedBehaviorNode refnode = Node.CreateReferencedBehaviorNode(_rootNode.RootBehavior, behavior); // register the view so it gets updated when the referenced behaviour gets updated. refnode.ReferencedBehaviorWasModified += new ReferencedBehavior.ReferencedBehaviorWasModifiedEventDelegate(refnode_ReferencedBehaviorWasModified); newnode = (Node)refnode; newnode.CommentText = Resources.ThisIsReferenceTree; newnode.CommentBackground = Node.CommentColor.Gray; } // prefab else { // Copy all Pars from the prefab file into the current behavior node. List<ParInfo> pars = new List<ParInfo>(); foreach (ParInfo par in b.Pars) { bool found = false; foreach (ParInfo rootPar in rootB.Pars) { if (par.Name == rootPar.Name) { if (par.Type != rootPar.Type) { string errorMsg = string.Format(Resources.ParErrorInfo, par.Name, b.Label, rootB.Label); MessageBox.Show(errorMsg, Resources.LoadError, MessageBoxButtons.OK); return; } found = true; break; } } if (!found) pars.Add(par); } rootB.Pars.AddRange(pars); if (ParSettingsDock.IsVisible()) ParSettingsDock.Inspect((Node)_rootNode.RootBehavior); // The first child should be the root node of the prefab tree. Node behaviorNode = (Node)behavior; if (behaviorNode.Children.Count > 0) { newnode = (Node)behaviorNode.Children[0]; string prefab = Path.GetFileNameWithoutExtension(behavior.Filename); newnode.CommentText = string.Format("Prefab[{0}]", prefab); isPrefabInstance = true; string prefabName = FileManagers.FileManager.GetRelativePath(behavior.Filename); newnode.SetPrefab(prefabName); } } } else { // simply create the node which is supposed to be created. newnode = Node.Create(nodetag.NodeType); } if (newnode == null) return; // update label newnode.OnPropertyValueChanged(false); // attach the new node with the correct mode switch (mode) { // the new node is inserted in front of the target node case (NodeAttachMode.Left): Node parent = (Node)node.Parent; int k = node.ParentConnector.GetChildIndex(node); Node.Connector conn = node.ParentConnector; Debug.Check(conn != null); parent.RemoveChild(conn, node); parent.AddChild(conn, newnode, k); Node.Connector newconn = newnode.GetConnector(conn.Identifier); Debug.Check(newconn != null); newnode.AddChild(newconn, node); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; break; // the new node is simply added to the target node's children case (NodeAttachMode.Right): node.AddChild(_dragTargetConnector, newnode); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd; break; // the new node is placed above the target node case (NodeAttachMode.Top): int n = _dragTargetNode.Node.ParentConnector.GetChildIndex(node); ((Node)node.Parent).AddChild(_dragTargetNode.Node.ParentConnector, newnode, n); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; break; // the new node is placed below the target node case (NodeAttachMode.Bottom): int m = _dragTargetNode.Node.ParentConnector.GetChildIndex(node); ((Node)node.Parent).AddChild(_dragTargetNode.Node.ParentConnector, newnode, m + 1); // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; break; // the node will replace the target node case (NodeAttachMode.Center): if (replaceNode(node, newnode)) { // automatically select the new node _selectedNodePending = newnode; _selectedNodePendingParent = nvd.Parent; } break; } // After being created, its Id should be reset. if (newnode != null) { newnode.ResetId(isPrefabInstance); // set the prefab dirty for the current parent if (newnode.Parent != null) { Node parent = (Node)newnode.Parent; if (!string.IsNullOrEmpty(parent.PrefabName)) { parent.HasOwnPrefabData = true; if (!isPrefabInstance) { newnode.SetPrefab(parent.PrefabName); newnode.HasOwnPrefabData = true; } } } if (nodetag.Type == NodeTagType.Behavior || nodetag.Type == NodeTagType.Prefab) ExpandedNodePool.SetExpandedNode(newnode.Behavior.RelativePath, newnode.Id.ToString(), false); UndoManager.Save(this.RootNode, newnode.Behavior); } // the layout needs to be recalculated LayoutChanged(); }
/// <summary> /// Handles when a mouse button is let go of. /// </summary> protected override void OnMouseUp(MouseEventArgs e) { _movedSubItem = null; // check if we were dragging a transition for the FSM. if (e.Button == MouseButtons.Left && _currentNode != null && ((_objectDragType == ObjectDragTypes.kNode) && _currentNode.IsFSM || _fsmSubItem != null && _fsmDragMode != FSMDragModes.kNone)) { if (Plugin.EditMode == EditModes.Design && _startMousePosition != e.Location) { // drag and move the fsm node if ((_objectDragType == ObjectDragTypes.kNode) && _currentNode.IsFSM) { UndoManager.Save(this.RootNode); LayoutChanged(); } // drag and connector the target node else { NodeViewData targetNvd = _rootNodeView.GetInsideNode(e.Location); if (targetNvd != null && targetNvd.IsFSM && (targetNvd.Parent != null) && _fsmSubItem is NodeViewData.SubItemAttachment) { NodeViewData.SubItemAttachment subItemAttachment = _fsmSubItem as NodeViewData.SubItemAttachment; if (subItemAttachment.Attachment != null && subItemAttachment.Attachment.TargetFSMNodeId != targetNvd.Node.Id) { subItemAttachment.Attachment.TargetFSMNodeId = targetNvd.Node.Id; UndoManager.Save(this.RootNode); LayoutChanged(); } } } } } // check if we were dragging an existing sub item. else if (e.Button == MouseButtons.Left && _currentNode != null && _dragTargetNode != null && _dragAttachment != null) { NodeViewData.SubItem targetSubItem = _dragTargetNode.GetSubItem(_dragTargetNode, _nodeLayoutManager.ViewToGraph(e.Location)); if (KeyCtrlIsDown || targetSubItem != _dragAttachment) { NodeViewData.SubItemAttachment targetAttachment = targetSubItem as NodeViewData.SubItemAttachment; if ((_currentNode != _dragTargetNode || targetAttachment != null) && this.MoveSubItem(_currentNode, _dragTargetNode, _dragAttachment, targetAttachment, _dragAttachMode == NodeAttachMode.Top, KeyCtrlIsDown)) { _currentNode.ClickEvent(_currentNode, _nodeLayoutManager.ViewToGraph(e.Location)); LayoutChanged(); } } _dragAttachment = null; _dragTargetAttachment = null; _dragAttachMode = NodeAttachMode.None; } // check if we were dragging or copying an existing node. else if (e.Button == MouseButtons.Left && (_movedNode != null || _copiedNode != null || _clipboardPasteMode)) { // if we have a valid target node continue if (_dragTargetNode != null) { Node sourceNode = null; if (_copiedNode != null) { bool cloneBranch = !(_copiedNode is ReferencedBehavior); sourceNode = (KeyShiftIsDown && cloneBranch) ? (Nodes.Node)_copiedNode.CloneBranch() : (Nodes.Node)_copiedNode.Clone(); } else if (_clipboardPasteMode) { bool cloneBranch = !(_clipboardNode is ReferencedBehavior); sourceNode = (/*KeyShiftIsDown && */cloneBranch) ? (Nodes.Node)_clipboardNode.CloneBranch() : (Nodes.Node)_clipboardNode.Clone(); } else if (_movedNode != null) { sourceNode = _movedNode; } Debug.Check(sourceNode != null); Node sourceParent = (Node)sourceNode.Parent; BehaviorNode sourceBehavior = sourceNode.Behavior; if (_dragTargetNode.Node == sourceNode) { _dragAttachMode = NodeAttachMode.None; } if (_dragAttachMode == NodeAttachMode.Top || _dragAttachMode == NodeAttachMode.Bottom || _dragAttachMode == NodeAttachMode.Left || _dragAttachMode == NodeAttachMode.Right || _dragAttachMode == NodeAttachMode.Center) { // set the prefab dirty for its previous parent if (sourceParent != null && !string.IsNullOrEmpty(sourceParent.PrefabName)) { sourceParent.HasOwnPrefabData = true; } if (KeyShiftIsDown) { if (sourceParent != null) { sourceParent.RemoveChild(sourceNode.ParentConnector, sourceNode); } } else { sourceNode.ExtractNode(); } } // move the dragged node to the target node, according to the attach mode switch (_dragAttachMode) { // the node will be placed above the target node case (NodeAttachMode.Top): int n = _dragTargetNode.Node.ParentConnector.GetChildIndex(_dragTargetNode.Node); ((Node)_dragTargetNode.Node.Parent).AddChild(_dragTargetNode.Node.ParentConnector, sourceNode, n); _selectedNodePending = sourceNode; _selectedNodePendingParent = _dragTargetNode.Parent; LayoutChanged(); break; // the node will be placed below the target node case (NodeAttachMode.Bottom): int m = _dragTargetNode.Node.ParentConnector.GetChildIndex(_dragTargetNode.Node); ((Node)_dragTargetNode.Node.Parent).AddChild(_dragTargetNode.Node.ParentConnector, sourceNode, m + 1); _selectedNodePending = sourceNode; _selectedNodePendingParent = _dragTargetNode.Parent; LayoutChanged(); break; // the node will be placed in front of the target node case (NodeAttachMode.Left): Node parent = (Node)_dragTargetNode.Node.Parent; Node.Connector conn = _dragTargetNode.Node.ParentConnector; int o = conn.GetChildIndex(_dragTargetNode.Node); parent.RemoveChild(conn, _dragTargetNode.Node); parent.AddChild(conn, sourceNode, o); BaseNode.Connector sourceConn = sourceNode.GetConnector(conn.Identifier); Debug.Check(sourceConn != null); sourceNode.AddChild(sourceConn, _dragTargetNode.Node); _selectedNodePending = sourceNode; _selectedNodePendingParent = _dragTargetNode.Parent; LayoutChanged(); break; // the node will simply attached to the target node case (NodeAttachMode.Right): _dragTargetNode.Node.AddChild(_dragTargetConnector, sourceNode); _selectedNodePending = sourceNode; _selectedNodePendingParent = _dragTargetNode; LayoutChanged(); break; // the node will replace the target node case (NodeAttachMode.Center): if (replaceNode(_dragTargetNode.Node, sourceNode)) { LayoutChanged(); } break; } if (_dragAttachMode != NodeAttachMode.None) { // If cloning a node, its Id should be reset. if (_copiedNode != null || _clipboardPasteMode) { // Cross two different behavior files if (_clipboardPasteMode && _clipboardRootNode != this.RootNodeView) { try { // Copy the used Pars from the current behavior to the new one. if (_clipboardNode != null && _clipboardRootNode != null && _clipboardRootNode.Node is Behavior) { foreach(ParInfo par in((Behavior)(_clipboardRootNode.Node)).LocalVars) { List<Node.ErrorCheck> result = new List<Node.ErrorCheck>(); Plugin.CheckPar(_clipboardNode, par, ref result); if (result.Count > 0) { bool bExist = false; foreach(ParInfo p in((Behavior)this.RootNode).LocalVars) { if (p.Name == par.Name) { bExist = true; break; } } if (!bExist) { ((Behavior)this.RootNode).LocalVars.Add(par); } } } } // reset its properties and methods sourceNode.ResetMembers(false, this.RootNode.AgentType, true); } catch { } } // reset its Id sourceNode.ResetId(true); } // update the node's label sourceNode.OnPropertyValueChanged(false); // set the prefab dirty for its current parent if (sourceNode.Parent != null) { Node parent = (Node)sourceNode.Parent; if (!string.IsNullOrEmpty(parent.PrefabName)) { parent.HasOwnPrefabData = true; sourceNode.SetPrefab(parent.PrefabName, true); } } UndoManager.Save(this.RootNode); } } // reset all the drag data if (!_clipboardPasteMode) { _copiedNode = null; _movedNode = null; _dragTargetNode = null; _dragNodeDefaults = null; _movedNodeGraph = null; } // redraw the graph Invalidate(); } // popup the menu for the current hit node else if (e.Button == MouseButtons.Right && !KeyAltIsDown && !KeyCtrlIsDown && !KeyShiftIsDown) { bool itemEnabled = (SelectedNode != null && SelectedNode.Node.Parent != null); itemEnabled &= (Plugin.EditMode == EditModes.Design); deleteMenuItem.ShortcutKeys = Keys.Delete; deleteTreeMenuItem.ShortcutKeys = Keys.Shift | Keys.Delete; cutMenuItem.Enabled = SelectedNodeCanBeCut(); cutTreeMenuItem.Enabled = (SelectedNode != null) && !SelectedNode.IsFSM && SelectedTreeCanBeCut(); copyMenuItem.Enabled = itemEnabled; copySubtreeMenuItem.Enabled = (SelectedNode != null) && !SelectedNode.IsFSM; pasteMenuItem.Enabled = SelectedNodeCanBePasted(); deleteMenuItem.Enabled = SelectedNodeCanBeDeleted(); deleteTreeMenuItem.Enabled = (SelectedNode != null) && !SelectedNode.IsFSM && SelectedTreeCanBeDeleted(); bool isReferencedBehavior = SelectedNode != null && SelectedNode.Node is ReferencedBehavior; bool isEvent = SelectedNode != null && SelectedNode.SelectedSubItem != null && SelectedNode.SelectedSubItem.SelectableObject is Attachments.Event; referenceMenuItem.Enabled = itemEnabled || isReferencedBehavior || isEvent; referenceMenuItem.Text = (isReferencedBehavior || isEvent) ? Resources.OpenReference : Resources.SaveReference; disableMenuItem.Enabled = false; if (itemEnabled) { if (SelectedNode.SelectedSubItem != null && SelectedNode.SelectedSubItem.SelectableObject is Attachments.Attachment) { Attachments.Attachment attach = SelectedNode.SelectedSubItem.SelectableObject as Attachments.Attachment; disableMenuItem.Enabled = attach.CanBeDisabled(); disableMenuItem.Text = attach.Enable ? Resources.DisableNode : Resources.EnableNode; } else { disableMenuItem.Enabled = SelectedNode.Node.CanBeDisabled(); disableMenuItem.Text = SelectedNode.Node.Enable ? Resources.DisableNode : Resources.EnableNode; } } expandMenuItem.Enabled = (SelectedNode != null && SelectedNode.CanBeExpanded()); collapseMenuItem.Enabled = expandMenuItem.Enabled; expandAllMenuItem.Enabled = expandMenuItem.Enabled; collapseAllMenuItem.Enabled = expandMenuItem.Enabled; bool isPrefabInstance = SelectedNode != null && !string.IsNullOrEmpty(SelectedNode.Node.PrefabName); breakPrefabMenuItem.Enabled = itemEnabled && isPrefabInstance; if (isPrefabInstance) { string fullpath = FileManagers.FileManager.GetFullPath(SelectedNode.Node.PrefabName); isPrefabInstance = File.Exists(fullpath); } savePrefabMenuItem.Enabled = itemEnabled; savePrefabMenuItem.Text = isPrefabInstance ? Resources.OpenPrefab : Resources.SavePrefab; if (SelectedNode != null) { Node prefabRoot = SelectedNode.Node.GetPrefabRoot(); string relativeFilename = FileManagers.FileManager.GetRelativePath(this.RootNode.Filename); applyMenuItem.Enabled = itemEnabled && isPrefabInstance && SelectedNode.Node.PrefabName != relativeFilename && prefabRoot.IsPrefabDataDirty(); } breakpointMenuItem.Enabled = SelectedNode != null && SelectedNode.Parent != null; if (SelectedNode != null) { enterBreakpointMenuItem.Text = SelectedNode.GetBreakpointOperation(HighlightBreakPoint.kEnter); exitBreakpointMenuItem.Text = SelectedNode.GetBreakpointOperation(HighlightBreakPoint.kExit); this.beakpointPlanning.Visible = false; if (SelectedNode.Node is Task) { this.beakpointPlanning.Visible = true; beakpointPlanning.Text = SelectedNode.GetBreakpointOperation(HighlightBreakPoint.kPlanning); } } contextMenu.Show(this, new Point(e.X, e.Y)); } Cursor = Cursors.Default; _fsmDragMode = FSMDragModes.kNone; // redraw the graph Invalidate(); base.OnMouseUp(e); }
/// <summary> /// Handles when a tree node is dragged on the view. /// </summary> private void BehaviorTreeView_DragOver(object sender, DragEventArgs e) { // get the node we are dragging over Point pt = PointToClient(new Point(e.X, e.Y)); NodeViewData nodeFound = _rootNode.IsInside(new PointF(pt.X, pt.Y)); // update the current node if (nodeFound != _currentNode) { _currentNode = nodeFound; Invalidate(); } // when we are moving on a node we must keep drawing as the ttach ode might change but not the node else if (nodeFound != null) { Invalidate(); } // store the target node _dragTargetNode = _currentNode; // deny drop by default e.Effect = DragDropEffects.None; // make sure the correct drag attach mode is set if (_dragTargetNode != null) { if (_dragNodeDefaults is Nodes.Node && _dragAttachMode == NodeAttachMode.Attachment) { _dragAttachMode = NodeAttachMode.None; } else if (_dragNodeDefaults is Attachments.Attachment && _dragAttachMode != NodeAttachMode.Attachment) { _dragAttachMode = NodeAttachMode.Attachment; } } // check if we are trying to drop a node on another one if (_dragTargetNode != null && (e.KeyState & 1/*left mouse button*/) > 0 && _dragNodeDefaults is Nodes.Node) { e.Effect = DragDropEffects.Move; } // check if we are trying attach an attachement to a node else if (_dragTargetNode != null && (e.KeyState & 1/*left mouse button*/) > 0 && _dragNodeDefaults is Attachments.Attachment && _dragTargetNode.Node.AcceptsAttachment(_dragNodeDefaults.GetType())) { e.Effect = DragDropEffects.Move; } // If this is an empty node, no effect for it. if (_dragTargetNode == null || _dragTargetNode.Node == null) e.Effect = DragDropEffects.None; // update last know mouse position _lastMousePosition = new PointF(pt.X, pt.Y); }
/// <summary> /// Handles when dropping a tree node on the view. /// </summary> private void BehaviorTreeView_DragDrop(object sender, DragEventArgs e) { //make sure the view is focused Focus(); // get source node TreeNode sourceNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode"); NodeTag sourceNodeTag = (NodeTag)sourceNode.Tag; // keep the current node position KeepNodePosition(_dragTargetNode); bool bDragBTOverNode = sourceNodeTag.Type == NodeTagType.Behavior && _dragTargetNode is Behaviac.Design.NodeViewData; // check if we are dropping an attach // or if we are dropping a bt to a node and the indicator is not left/right/up/bottom/center if (_dragAttachMode == NodeAttachMode.Attachment || (bDragBTOverNode && (_dragAttachMode == NodeAttachMode.None || _dragAttachMode == NodeAttachMode.Attachment))) { Attachments.Attachment attach = null; // when we attach a behaviour we must create a special referenced behaviour node if (bDragBTOverNode) { //drag an event(a bt) to a node if (File.Exists(sourceNodeTag.Filename)) { // get the behavior we want to reference BehaviorNode behavior = _behaviorTreeList.LoadBehavior(sourceNodeTag.Filename); Behavior rootB = _rootNode.RootBehavior as Behavior; Behavior b = behavior as Behavior; if (!IsCompatibleAgentType(rootB, b)) { return; } attach = Behaviac.Design.Attachments.Attachment.Create(typeof(Behaviac.Design.Attachments.Event), _dragTargetNode.Node); Behaviac.Design.Attachments.Event evt = (Behaviac.Design.Attachments.Event)attach; evt.ReferencedBehavior = behavior; } } else { Debug.Check(_dragAttachMode == NodeAttachMode.Attachment); // add the attach to the target node attach = Behaviac.Design.Attachments.Attachment.Create(sourceNodeTag.NodeType, _dragTargetNode.Node); } attach.OnPropertyValueChanged(false); _dragTargetNode.Node.AddAttachment(attach); NodeViewData.SubItemAttachment sub = attach.CreateSubItem(); _dragTargetNode.AddSubItem(sub); _dragTargetNode.Node.BehaviorWasModified(); SelectedNode = _dragTargetNode; SelectedNode.SelectedSubItem = sub; // call the ClickEvent event handler if (ClickEvent != null) ClickEvent(SelectedNode); // After being created, its Id should be reset. attach.ResetId(); UndoManager.Save(this.RootNode, _dragTargetNode.Node.Behavior); LayoutChanged(); } else if (_dragAttachMode != NodeAttachMode.None) { // attach a new node to the target node InsertNewNode(_dragTargetNode, _dragAttachMode, sourceNodeTag); } // reset drag stuff _dragTargetNode = null; _dragNodeDefaults = null; _dragAttachMode = NodeAttachMode.None; Invalidate(); }
/// <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(); }
/// <summary> /// Attaches a dragged node from the node explorer to an existing node. /// </summary> /// <param name="nvd">The node the new node will be attached to.</param> /// <param name="mode">The way the new node will be attached.</param> /// <param name="nodetag">The tag of the you want to create.</param> /// <param name="label">The label of the new node.</param> private void InsertNewNode(NodeViewData nvd, NodeAttachMode mode, NodeTag nodetag) { // check if the attach mode is valid if(mode ==NodeAttachMode.Event || mode ==NodeAttachMode.None) throw new Exception("A node cannot be created with the given attach mode"); if(nodetag.Type !=NodeTagType.Behavior && nodetag.Type !=NodeTagType.Node) throw new Exception("Only behaviours and nodes can be attached to a behaviour tree"); Node node= nvd.Node; Node newnode; // when we attach a behaviour we must create a special referenced behaviour node if(nodetag.Type ==NodeTagType.Behavior) { // reset any previously loaded behaviour FileManagers.FileManager.ResetLoadedBehavior(); // get the behaviour we want to reference BehaviorNode behavior= _behaviorTreeList.LoadBehavior(nodetag.Filename); // a behaviour may not reference itself if(behavior ==_rootNode.RootBehavior) return; // create the referenced behaviour node for the behaviour ReferencedBehavior refnode= new ReferencedBehavior(_rootNode.RootBehavior, behavior); // register the view so it gets updated when the referenced behaviour gets updated. refnode.ReferencedBehaviorWasModified+= new ReferencedBehavior.ReferencedBehaviorWasModifiedEventDelegate(refnode_ReferencedBehaviorWasModified); newnode= refnode; } else { // simply create the node which is supposed to be created. newnode= Node.Create(nodetag.NodeType); } // update label newnode.OnPropertyValueChanged(false); // attach the new node with the correct mode switch(mode) { // the new node is inserted in front of the target node case(NodeAttachMode.Left): Node parent= node.Parent; int k= parent.Children.IndexOf(node); Node.Connector conn= node.ParentConnector; Debug.Check(conn !=null); parent.RemoveChild(conn, node); parent.AddChild(conn, newnode, k); Node.Connector newconn= newnode.GetConnector(conn.Identifier); Debug.Check(newconn !=null); newnode.AddChild(newconn, node); // automatically select the new node _selectedNodePending= newnode; _selectedNodePendingParent= nvd.Parent; break; // the new node is simply added to the target node's children case(NodeAttachMode.Right): node.AddChild(_dragTargetConnector, newnode); // automatically select the new node _selectedNodePending= newnode; _selectedNodePendingParent= nvd; break; // the new node is placed above the target node case(NodeAttachMode.Top): int n= _dragTargetNode.Node.ParentConnector.GetChildIndex(node); node.Parent.AddChild(_dragTargetNode.Node.ParentConnector, newnode, n); // automatically select the new node _selectedNodePending= newnode; _selectedNodePendingParent= nvd.Parent; break; // the new node is placed below the target node case(NodeAttachMode.Bottom): int m= _dragTargetNode.Node.ParentConnector.GetChildIndex(node); node.Parent.AddChild(_dragTargetNode.Node.ParentConnector, newnode, m +1); // automatically select the new node _selectedNodePending= newnode; _selectedNodePendingParent= nvd.Parent; break; } // the layout needs to be recalculated LayoutChanged(); }
/// <summary> /// Handles when dropping a tree node on the view. /// </summary> private void BehaviorTreeView_DragDrop(object sender, DragEventArgs e) { //make sure the view is focused Focus(); // get source node TreeNode sourceNode= (TreeNode) e.Data.GetData("System.Windows.Forms.TreeNode"); NodeTag sourceNodeTag= (NodeTag)sourceNode.Tag; // keep the current node position _maintainNodePosition= _dragTargetNode; _graphOrigin= _maintainNodePosition.DisplayBoundingBox.Location; // check if we are dropping an event if(_dragAttachMode ==NodeAttachMode.Event) { // add the event to the target node Events.Event evnt= Brainiac.Design.Events.Event.Create(sourceNodeTag.NodeType, _dragTargetNode.Node); evnt.OnPropertyValueChanged(false); Nodes.Node.SubItemEvent sub= new Nodes.Node.SubItemEvent(evnt); _dragTargetNode.Node.AddSubItem(sub); _dragTargetNode.Node.BehaviorWasModified(); SelectedNode= _dragTargetNode; _selectedNode.Node.SelectedSubItem= sub; // call the ClickEvent event handler if(ClickEvent !=null) ClickEvent(_selectedNode); LayoutChanged(); } else if(_dragAttachMode !=NodeAttachMode.None) { // attach a new node to the target node InsertNewNode(_dragTargetNode, _dragAttachMode, sourceNodeTag); } // reset drag stuff _dragTargetNode= null; _dragNodeDefaults= null; _dragAttachMode= NodeAttachMode.None; Invalidate(); }