diff --git a/Model/DataNode.cs b/Model/DataNode.cs
index 8b22ce4..728e7de 100644
--- a/Model/DataNode.cs
+++ b/Model/DataNode.cs
@@ -193,7 +193,7 @@ namespace NBTExplorer.Model
}
}
- #region Capabilities
+ #region Node Capabilities
protected virtual NodeCapabilities Capabilities
{
@@ -262,6 +262,55 @@ namespace NBTExplorer.Model
#endregion
+ #region Group Capabilities
+
+ public virtual GroupCapabilities RenameNodeCapabilities
+ {
+ get { return GroupCapabilities.Single; }
+ }
+
+ public virtual GroupCapabilities EditNodeCapabilities
+ {
+ get { return GroupCapabilities.Single; }
+ }
+
+ public virtual GroupCapabilities DeleteNodeCapabilities
+ {
+ get { return GroupCapabilities.MultiMixedType | GroupCapabilities.ElideChildren; }
+ }
+
+ public virtual GroupCapabilities CutNodeCapabilities
+ {
+ get { return GroupCapabilities.Single; } //SiblingMixedType
+ }
+
+ public virtual GroupCapabilities CopyNodeCapabilities
+ {
+ get { return GroupCapabilities.Single; } //SiblingMixedType
+ }
+
+ public virtual GroupCapabilities PasteIntoNodeCapabilities
+ {
+ get { return GroupCapabilities.Single; }
+ }
+
+ public virtual GroupCapabilities SearchNodeCapabilites
+ {
+ get { return GroupCapabilities.Single; }
+ }
+
+ public virtual GroupCapabilities ReorderNodeCapabilities
+ {
+ get { return GroupCapabilities.Single; } //SiblingMixedType
+ }
+
+ public virtual GroupCapabilities RefreshNodeCapabilites
+ {
+ get { return GroupCapabilities.Single; } // MultiMixedType | ElideChildren
+ }
+
+ #endregion
+
#region Operations
public virtual bool CreateNode (TagType type)
diff --git a/Model/NodeCapabilities.cs b/Model/NodeCapabilities.cs
index 45f35c3..dcfffca 100644
--- a/Model/NodeCapabilities.cs
+++ b/Model/NodeCapabilities.cs
@@ -17,4 +17,16 @@ namespace NBTExplorer.Model
Reorder = 0x100,
Refresh = 0x200,
}
+
+ [Flags]
+ public enum GroupCapabilities
+ {
+ Single = 0x0,
+ SiblingSameType = 0x1,
+ SiblingMixedType = 0x2 | SiblingSameType,
+ MultiSameType = 0x4 | SiblingSameType,
+ MultiMixedType = 0x8 | MultiSameType | SiblingMixedType,
+ ElideChildren = 0x10,
+ All = MultiMixedType | ElideChildren,
+ }
}
diff --git a/Model/TagDataNode.cs b/Model/TagDataNode.cs
index 465cad6..8c5a92f 100644
--- a/Model/TagDataNode.cs
+++ b/Model/TagDataNode.cs
@@ -55,8 +55,8 @@ namespace NBTExplorer.Model
| NodeCapabilities.Cut
| NodeCapabilities.Delete
| NodeCapabilities.PasteInto
- | (TagParent.IsNamedContainer ? NodeCapabilities.Rename : NodeCapabilities.None)
- | (TagParent.IsOrderedContainer ? NodeCapabilities.Reorder : NodeCapabilities.None)
+ | ((TagParent != null && TagParent.IsNamedContainer) ? NodeCapabilities.Rename : NodeCapabilities.None)
+ | ((TagParent != null && TagParent.IsOrderedContainer) ? NodeCapabilities.Reorder : NodeCapabilities.None)
| NodeCapabilities.Search;
}
}
@@ -158,8 +158,8 @@ namespace NBTExplorer.Model
| NodeCapabilities.Cut
| NodeCapabilities.Delete
| NodeCapabilities.Edit
- | (TagParent.IsNamedContainer ? NodeCapabilities.Rename : NodeCapabilities.None)
- | (TagParent.IsOrderedContainer ? NodeCapabilities.Reorder : NodeCapabilities.None);
+ | ((TagParent != null && TagParent.IsNamedContainer) ? NodeCapabilities.Rename : NodeCapabilities.None)
+ | ((TagParent != null && TagParent.IsOrderedContainer) ? NodeCapabilities.Reorder : NodeCapabilities.None);
}
}
@@ -167,7 +167,7 @@ namespace NBTExplorer.Model
{
get
{
- if (TagParent.IsOrderedContainer)
+ if (TagParent != null && TagParent.IsOrderedContainer)
return TagParent.OrderedTagContainer.GetTagIndex(Tag) > 0;
return false;
}
@@ -177,7 +177,7 @@ namespace NBTExplorer.Model
{
get
{
- if (TagParent.IsOrderedContainer)
+ if (TagParent != null && TagParent.IsOrderedContainer)
return TagParent.OrderedTagContainer.GetTagIndex(Tag) < (TagParent.TagCount - 1);
return false;
}
@@ -210,8 +210,9 @@ namespace NBTExplorer.Model
public override bool DeleteNode ()
{
- if (CanDeleteNode) {
+ if (CanDeleteNode && TagParent != null) {
TagParent.DeleteTag(Tag);
+ IsParentModified = true;
return Parent.Nodes.Remove(this);
}
@@ -220,7 +221,7 @@ namespace NBTExplorer.Model
public override bool RenameNode ()
{
- if (CanRenameNode && TagParent.IsNamedContainer && FormRegistry.EditString != null) {
+ if (CanRenameNode && TagParent != null && TagParent.IsNamedContainer && FormRegistry.EditString != null) {
RestrictedStringFormData data = new RestrictedStringFormData(TagParent.NamedTagContainer.GetTagName(Tag));
data.RestrictedValues.AddRange(TagParent.NamedTagContainer.TagNamesInUse);
@@ -247,10 +248,11 @@ namespace NBTExplorer.Model
public override bool CutNode ()
{
- if (CanCutNode) {
+ if (CanCutNode && TagParent != null) {
NbtClipboardController.CopyToClipboard(new NbtClipboardData(NodeName, Tag));
TagParent.DeleteTag(Tag);
+ IsParentModified = true;
Parent.Nodes.Remove(this);
return true;
}
@@ -260,7 +262,7 @@ namespace NBTExplorer.Model
public override bool ChangeRelativePosition (int offset)
{
- if (CanReoderNode) {
+ if (CanReoderNode && TagParent != null) {
int curIndex = TagParent.OrderedTagContainer.GetTagIndex(Tag);
int newIndex = curIndex + offset;
diff --git a/NBTExplorer.csproj b/NBTExplorer.csproj
index 9ceaf8b..9b94b1a 100644
--- a/NBTExplorer.csproj
+++ b/NBTExplorer.csproj
@@ -83,6 +83,9 @@
+
+ Component
+
Form
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index c9d4599..b55c05a 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.1.0.0")]
-[assembly: AssemblyFileVersion("2.1.0.0")]
+[assembly: AssemblyVersion("2.2.0.0")]
+[assembly: AssemblyFileVersion("2.2.0.0")]
diff --git a/Vendor/MultiSelectTreeView/MultiSelectTreeview.cs b/Vendor/MultiSelectTreeView/MultiSelectTreeview.cs
new file mode 100644
index 0000000..a40a373
--- /dev/null
+++ b/Vendor/MultiSelectTreeView/MultiSelectTreeview.cs
@@ -0,0 +1,585 @@
+// Copyright 2007 Andrew D. Weiss
+// Licensed under CPOL
+// http://www.codeproject.com/Articles/20581/Multiselect-Treeview-Implementation
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace NBTExplorer.Vendor.MultiSelectTreeView
+{
+ public class MultiSelectTreeView : TreeView
+ {
+
+ #region Selected Node(s) Properties
+
+ private List m_SelectedNodes = null;
+ public List SelectedNodes
+ {
+ get
+ {
+ return m_SelectedNodes;
+ }
+ set
+ {
+ ClearSelectedNodes();
+ if( value != null )
+ {
+ foreach( TreeNode node in value )
+ {
+ ToggleNode( node, true );
+ }
+ }
+ }
+ }
+
+ // Note we use the new keyword to Hide the native treeview's SelectedNode property.
+ private TreeNode m_SelectedNode;
+ public new TreeNode SelectedNode
+ {
+ get { return m_SelectedNode; }
+ set
+ {
+ ClearSelectedNodes();
+ if( value != null )
+ {
+ SelectNode( value );
+ }
+ }
+ }
+
+ #endregion
+
+ public MultiSelectTreeView()
+ {
+ m_SelectedNodes = new List();
+ base.SelectedNode = null;
+ }
+
+ #region Overridden Events
+
+ protected override void OnGotFocus( EventArgs e )
+ {
+ // Make sure at least one node has a selection
+ // this way we can tab to the ctrl and use the
+ // keyboard to select nodes
+ try
+ {
+ if( m_SelectedNode == null && this.TopNode != null )
+ {
+ ToggleNode( this.TopNode, true );
+ }
+
+ base.OnGotFocus( e );
+ }
+ catch( Exception ex )
+ {
+ HandleException( ex );
+ }
+ }
+
+ protected override void OnMouseDown( MouseEventArgs e )
+ {
+ // If the user clicks on a node that was not
+ // previously selected, select it now.
+
+ try
+ {
+ base.SelectedNode = null;
+
+ TreeNode node = this.GetNodeAt( e.Location );
+ if( node != null )
+ {
+ int leftBound = node.Bounds.X; // - 20; // Allow user to click on image
+ int rightBound = node.Bounds.Right + 10; // Give a little extra room
+ if( e.Location.X > leftBound && e.Location.X < rightBound )
+ {
+ if( ModifierKeys == Keys.None && ( m_SelectedNodes.Contains( node ) ) )
+ {
+ // Potential Drag Operation
+ // Let Mouse Up do select
+ }
+ else
+ {
+ SelectNode( node );
+ }
+ }
+ }
+
+ base.OnMouseDown( e );
+ }
+ catch( Exception ex )
+ {
+ HandleException( ex );
+ }
+ }
+
+ protected override void OnMouseUp( MouseEventArgs e )
+ {
+ // If the clicked on a node that WAS previously
+ // selected then, reselect it now. This will clear
+ // any other selected nodes. e.g. A B C D are selected
+ // the user clicks on B, now A C & D are no longer selected.
+ try
+ {
+ // Check to see if a node was clicked on
+ TreeNode node = this.GetNodeAt( e.Location );
+ if( node != null )
+ {
+ if( ModifierKeys == Keys.None && m_SelectedNodes.Contains( node ) )
+ {
+ int leftBound = node.Bounds.X; // -20; // Allow user to click on image
+ int rightBound = node.Bounds.Right + 10; // Give a little extra room
+ if( e.Location.X > leftBound && e.Location.X < rightBound )
+ {
+
+ SelectNode( node );
+ }
+ }
+ }
+
+ base.OnMouseUp( e );
+ }
+ catch( Exception ex )
+ {
+ HandleException( ex );
+ }
+ }
+
+ protected override void OnItemDrag( ItemDragEventArgs e )
+ {
+ // If the user drags a node and the node being dragged is NOT
+ // selected, then clear the active selection, select the
+ // node being dragged and drag it. Otherwise if the node being
+ // dragged is selected, drag the entire selection.
+ try
+ {
+ TreeNode node = e.Item as TreeNode;
+
+ if( node != null )
+ {
+ if( !m_SelectedNodes.Contains( node ) )
+ {
+ SelectSingleNode( node );
+ ToggleNode( node, true );
+ }
+ }
+
+ base.OnItemDrag( e );
+ }
+ catch( Exception ex )
+ {
+ HandleException( ex );
+ }
+ }
+
+ protected override void OnBeforeSelect( TreeViewCancelEventArgs e )
+ {
+ // Never allow base.SelectedNode to be set!
+ try
+ {
+ base.SelectedNode = null;
+ e.Cancel = true;
+
+ base.OnBeforeSelect( e );
+ }
+ catch( Exception ex )
+ {
+ HandleException( ex );
+ }
+ }
+
+ protected override void OnAfterSelect( TreeViewEventArgs e )
+ {
+ // Never allow base.SelectedNode to be set!
+ try
+ {
+ base.OnAfterSelect( e );
+ base.SelectedNode = null;
+ }
+ catch( Exception ex )
+ {
+ HandleException( ex );
+ }
+ }
+
+ protected override void OnKeyDown( KeyEventArgs e )
+ {
+ // Handle all possible key strokes for the control.
+ // including navigation, selection, etc.
+
+ base.OnKeyDown( e );
+
+ if( e.KeyCode == Keys.ShiftKey ) return;
+
+ //this.BeginUpdate();
+ bool bShift = ( ModifierKeys == Keys.Shift );
+
+ try
+ {
+ // Nothing is selected in the tree, this isn't a good state
+ // select the top node
+ if( m_SelectedNode == null && this.TopNode != null )
+ {
+ ToggleNode( this.TopNode, true );
+ }
+
+ // Nothing is still selected in the tree, this isn't a good state, leave.
+ if( m_SelectedNode == null ) return;
+
+ if( e.KeyCode == Keys.Left )
+ {
+ if( m_SelectedNode.IsExpanded && m_SelectedNode.Nodes.Count > 0 )
+ {
+ // Collapse an expanded node that has children
+ m_SelectedNode.Collapse();
+ }
+ else if( m_SelectedNode.Parent != null )
+ {
+ // Node is already collapsed, try to select its parent.
+ SelectSingleNode( m_SelectedNode.Parent );
+ }
+ }
+ else if( e.KeyCode == Keys.Right )
+ {
+ if( !m_SelectedNode.IsExpanded )
+ {
+ // Expand a collpased node's children
+ m_SelectedNode.Expand();
+ }
+ else
+ {
+ // Node was already expanded, select the first child
+ SelectSingleNode( m_SelectedNode.FirstNode );
+ }
+ }
+ else if( e.KeyCode == Keys.Up )
+ {
+ // Select the previous node
+ if( m_SelectedNode.PrevVisibleNode != null )
+ {
+ SelectNode( m_SelectedNode.PrevVisibleNode );
+ }
+ }
+ else if( e.KeyCode == Keys.Down )
+ {
+ // Select the next node
+ if( m_SelectedNode.NextVisibleNode != null )
+ {
+ SelectNode( m_SelectedNode.NextVisibleNode );
+ }
+ }
+ else if( e.KeyCode == Keys.Home )
+ {
+ if( bShift )
+ {
+ if( m_SelectedNode.Parent == null )
+ {
+ // Select all of the root nodes up to this point
+ if( this.Nodes.Count > 0 )
+ {
+ SelectNode( this.Nodes[0] );
+ }
+ }
+ else
+ {
+ // Select all of the nodes up to this point under this nodes parent
+ SelectNode( m_SelectedNode.Parent.FirstNode );
+ }
+ }
+ else
+ {
+ // Select this first node in the tree
+ if( this.Nodes.Count > 0 )
+ {
+ SelectSingleNode( this.Nodes[0] );
+ }
+ }
+ }
+ else if( e.KeyCode == Keys.End )
+ {
+ if( bShift )
+ {
+ if( m_SelectedNode.Parent == null )
+ {
+ // Select the last ROOT node in the tree
+ if( this.Nodes.Count > 0 )
+ {
+ SelectNode( this.Nodes[this.Nodes.Count - 1] );
+ }
+ }
+ else
+ {
+ // Select the last node in this branch
+ SelectNode( m_SelectedNode.Parent.LastNode );
+ }
+ }
+ else
+ {
+ if( this.Nodes.Count > 0 )
+ {
+ // Select the last node visible node in the tree.
+ // Don't expand branches incase the tree is virtual
+ TreeNode ndLast = this.Nodes[0].LastNode;
+ while( ndLast.IsExpanded && ( ndLast.LastNode != null ) )
+ {
+ ndLast = ndLast.LastNode;
+ }
+ SelectSingleNode( ndLast );
+ }
+ }
+ }
+ else if( e.KeyCode == Keys.PageUp )
+ {
+ // Select the highest node in the display
+ int nCount = this.VisibleCount;
+ TreeNode ndCurrent = m_SelectedNode;
+ while( ( nCount ) > 0 && ( ndCurrent.PrevVisibleNode != null ) )
+ {
+ ndCurrent = ndCurrent.PrevVisibleNode;
+ nCount--;
+ }
+ SelectSingleNode( ndCurrent );
+ }
+ else if( e.KeyCode == Keys.PageDown )
+ {
+ // Select the lowest node in the display
+ int nCount = this.VisibleCount;
+ TreeNode ndCurrent = m_SelectedNode;
+ while( ( nCount ) > 0 && ( ndCurrent.NextVisibleNode != null ) )
+ {
+ ndCurrent = ndCurrent.NextVisibleNode;
+ nCount--;
+ }
+ SelectSingleNode( ndCurrent );
+ }
+ else
+ {
+ // Assume this is a search character a-z, A-Z, 0-9, etc.
+ // Select the first node after the current node that
+ // starts with this character
+ string sSearch = ( (char) e.KeyValue ).ToString();
+
+ TreeNode ndCurrent = m_SelectedNode;
+ while( ( ndCurrent.NextVisibleNode != null ) )
+ {
+ ndCurrent = ndCurrent.NextVisibleNode;
+ if( ndCurrent.Text.StartsWith( sSearch ) )
+ {
+ SelectSingleNode( ndCurrent );
+ break;
+ }
+ }
+ }
+ }
+ catch( Exception ex )
+ {
+ HandleException( ex );
+ }
+ finally
+ {
+ this.EndUpdate();
+ }
+ }
+
+ #endregion
+
+ #region Helper Methods
+
+ private void SelectNode( TreeNode node )
+ {
+ try
+ {
+ this.BeginUpdate();
+
+ if( m_SelectedNode == null || ModifierKeys == Keys.Control )
+ {
+ // Ctrl+Click selects an unselected node, or unselects a selected node.
+ bool bIsSelected = m_SelectedNodes.Contains( node );
+ ToggleNode( node, !bIsSelected );
+ }
+ else if( ModifierKeys == Keys.Shift )
+ {
+ // Shift+Click selects nodes between the selected node and here.
+ TreeNode ndStart = m_SelectedNode;
+ TreeNode ndEnd = node;
+
+ if( ndStart.Parent == ndEnd.Parent )
+ {
+ // Selected node and clicked node have same parent, easy case.
+ if( ndStart.Index < ndEnd.Index )
+ {
+ // If the selected node is beneath the clicked node walk down
+ // selecting each Visible node until we reach the end.
+ while( ndStart != ndEnd )
+ {
+ ndStart = ndStart.NextVisibleNode;
+ if( ndStart == null ) break;
+ ToggleNode( ndStart, true );
+ }
+ }
+ else if( ndStart.Index == ndEnd.Index )
+ {
+ // Clicked same node, do nothing
+ }
+ else
+ {
+ // If the selected node is above the clicked node walk up
+ // selecting each Visible node until we reach the end.
+ while( ndStart != ndEnd )
+ {
+ ndStart = ndStart.PrevVisibleNode;
+ if( ndStart == null ) break;
+ ToggleNode( ndStart, true );
+ }
+ }
+ }
+ else
+ {
+ // Selected node and clicked node have same parent, hard case.
+ // We need to find a common parent to determine if we need
+ // to walk down selecting, or walk up selecting.
+
+ TreeNode ndStartP = ndStart;
+ TreeNode ndEndP = ndEnd;
+ int startDepth = Math.Min( ndStartP.Level, ndEndP.Level );
+
+ // Bring lower node up to common depth
+ while( ndStartP.Level > startDepth )
+ {
+ ndStartP = ndStartP.Parent;
+ }
+
+ // Bring lower node up to common depth
+ while( ndEndP.Level > startDepth )
+ {
+ ndEndP = ndEndP.Parent;
+ }
+
+ // Walk up the tree until we find the common parent
+ while( ndStartP.Parent != ndEndP.Parent )
+ {
+ ndStartP = ndStartP.Parent;
+ ndEndP = ndEndP.Parent;
+ }
+
+ // Select the node
+ if( ndStartP.Index < ndEndP.Index )
+ {
+ // If the selected node is beneath the clicked node walk down
+ // selecting each Visible node until we reach the end.
+ while( ndStart != ndEnd )
+ {
+ ndStart = ndStart.NextVisibleNode;
+ if( ndStart == null ) break;
+ ToggleNode( ndStart, true );
+ }
+ }
+ else if( ndStartP.Index == ndEndP.Index )
+ {
+ if( ndStart.Level < ndEnd.Level )
+ {
+ while( ndStart != ndEnd )
+ {
+ ndStart = ndStart.NextVisibleNode;
+ if( ndStart == null ) break;
+ ToggleNode( ndStart, true );
+ }
+ }
+ else
+ {
+ while( ndStart != ndEnd )
+ {
+ ndStart = ndStart.PrevVisibleNode;
+ if( ndStart == null ) break;
+ ToggleNode( ndStart, true );
+ }
+ }
+ }
+ else
+ {
+ // If the selected node is above the clicked node walk up
+ // selecting each Visible node until we reach the end.
+ while( ndStart != ndEnd )
+ {
+ ndStart = ndStart.PrevVisibleNode;
+ if( ndStart == null ) break;
+ ToggleNode( ndStart, true );
+ }
+ }
+ }
+ }
+ else
+ {
+ // Just clicked a node, select it
+ SelectSingleNode( node );
+ }
+
+ OnAfterSelect( new TreeViewEventArgs( m_SelectedNode ) );
+ }
+ finally
+ {
+ this.EndUpdate();
+ }
+ }
+
+ private void ClearSelectedNodes()
+ {
+ try
+ {
+ foreach( TreeNode node in m_SelectedNodes )
+ {
+ node.BackColor = this.BackColor;
+ node.ForeColor = this.ForeColor;
+ }
+ }
+ finally
+ {
+ m_SelectedNodes.Clear();
+ m_SelectedNode = null;
+ }
+ }
+
+ private void SelectSingleNode( TreeNode node )
+ {
+ if( node == null )
+ {
+ return;
+ }
+
+ ClearSelectedNodes();
+ ToggleNode( node, true );
+ node.EnsureVisible();
+ }
+
+ private void ToggleNode( TreeNode node, bool bSelectNode )
+ {
+ if( bSelectNode )
+ {
+ m_SelectedNode = node;
+ if( !m_SelectedNodes.Contains( node ) )
+ {
+ m_SelectedNodes.Add( node );
+ }
+ node.BackColor = SystemColors.Highlight;
+ node.ForeColor = SystemColors.HighlightText;
+ }
+ else
+ {
+ m_SelectedNodes.Remove( node );
+ node.BackColor = this.BackColor;
+ node.ForeColor = this.ForeColor;
+ }
+ }
+
+ private void HandleException( Exception ex )
+ {
+ // Perform some error handling here.
+ // We don't want to bubble errors to the CLR.
+ MessageBox.Show( ex.Message );
+ }
+
+ #endregion
+ }
+}
diff --git a/Windows/MainForm.Designer.cs b/Windows/MainForm.Designer.cs
index 3443b5e..3f9069d 100644
--- a/Windows/MainForm.Designer.cs
+++ b/Windows/MainForm.Designer.cs
@@ -41,7 +41,7 @@
this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator();
this.searchToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this._nodeTree = new System.Windows.Forms.TreeView();
+ this._nodeTree = new NBTExplorer.Vendor.MultiSelectTreeView.MultiSelectTreeView();
this.imageList1 = new System.Windows.Forms.ImageList(this.components);
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
@@ -697,7 +697,7 @@
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem searchToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem;
- private System.Windows.Forms.TreeView _nodeTree;
+ private NBTExplorer.Vendor.MultiSelectTreeView.MultiSelectTreeView _nodeTree;
private System.Windows.Forms.ToolStrip toolStrip1;
private System.Windows.Forms.ToolStripButton _buttonOpen;
private System.Windows.Forms.ToolStripButton _buttonSave;
diff --git a/Windows/MainForm.cs b/Windows/MainForm.cs
index d6b93c4..9777a2d 100644
--- a/Windows/MainForm.cs
+++ b/Windows/MainForm.cs
@@ -413,6 +413,294 @@ namespace NBTExplorer.Windows
}
}
+ private void DeleteNode (IList nodes)
+ {
+ bool? elideChildren = null;
+ if (!CanOperateOnNodesEx(nodes, DeleteNodePred, out elideChildren))
+ return;
+
+ if (elideChildren == true)
+ nodes = ElideChildren(nodes);
+
+ foreach (TreeNode node in nodes) {
+ DataNode dataNode = node.Tag as DataNode;
+ if (dataNode.DeleteNode()) {
+ UpdateNodeText(node.Parent);
+ node.Remove();
+ }
+ }
+
+ UpdateUI();
+ }
+
+ /*private bool CanDeleteNodes (IList nodes)
+ {
+ bool? elideChildren = null;
+ return CanDeleteNodesEx(nodes, out elideChildren);
+ }
+
+ private bool CanDeleteNodesEx (IList nodes, out bool? elideChildren)
+ {
+ GroupCapabilities caps = GroupCapabilities.All;
+ elideChildren = null;
+
+ foreach (TreeNode node in nodes) {
+ if (node == null || !(node.Tag is DataNode))
+ return false;
+
+ DataNode dataNode = node.Tag as DataNode;
+ if (!dataNode.CanDeleteNode)
+ return false;
+
+ caps &= dataNode.DeleteNodeCapabilities;
+
+ bool elideChildrenFlag = (dataNode.DeleteNodeCapabilities & GroupCapabilities.ElideChildren) == GroupCapabilities.ElideChildren;
+ if (elideChildren == null)
+ elideChildren = elideChildrenFlag;
+ if (elideChildren != elideChildrenFlag)
+ return false;
+ }
+
+ if (nodes.Count > 1 && !SufficientCapabilities(nodes, caps))
+ return false;
+
+ return true;
+ }*/
+
+ delegate bool DataNodePredicate (DataNode dataNode, out GroupCapabilities caps);
+
+ private bool CreateByteNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_BYTE);
+ }
+
+ private bool CreateShortNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_SHORT);
+ }
+
+ private bool CreateIntNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_INT);
+ }
+
+ private bool CreateLongNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_LONG);
+ }
+
+ private bool CreateFloatNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_FLOAT);
+ }
+
+ private bool CreateDoubleNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_DOUBLE);
+ }
+
+ private bool CreateByteArrayNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_BYTE_ARRAY);
+ }
+
+ private bool CreateIntArrayNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_INT_ARRAY);
+ }
+
+ private bool CreateStringNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_STRING);
+ }
+
+ private bool CreateListNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_LIST);
+ }
+
+ private bool CreateCompoundNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag(TagType.TAG_COMPOUND);
+ }
+
+ private bool RenameNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.RenameNodeCapabilities;
+ return (dataNode != null) && dataNode.CanRenameNode;
+ }
+
+ private bool EditNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.EditNodeCapabilities;
+ return (dataNode != null) && dataNode.CanEditNode;
+ }
+
+ private bool DeleteNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.DeleteNodeCapabilities;
+ return (dataNode != null) && dataNode.CanDeleteNode;
+ }
+
+ private bool CutNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.CutNodeCapabilities;
+ return (dataNode != null) && dataNode.CanCutNode;
+ }
+
+ private bool CopyNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.CopyNodeCapabilities;
+ return (dataNode != null) && dataNode.CanCopyNode;
+ }
+
+ private bool PasteIntoNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.PasteIntoNodeCapabilities;
+ return (dataNode != null) && dataNode.CanPasteIntoNode;
+ }
+
+ private bool SearchNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.SearchNodeCapabilites;
+ return (dataNode != null) && dataNode.CanSearchNode;
+ }
+
+ private bool ReorderNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.ReorderNodeCapabilities;
+ return (dataNode != null) && dataNode.CanReoderNode;
+ }
+
+ private bool RefreshNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = dataNode.RefreshNodeCapabilites;
+ return (dataNode != null) && dataNode.CanRefreshNode;
+ }
+
+ /*private bool CreateTagNodePred (DataNode dataNode, out GroupCapabilities caps)
+ {
+ caps = GroupCapabilities.Single;
+ return (dataNode != null) && dataNode.CanCreateTag
+ }*/
+
+ private bool CanOperateOnNodes (IList nodes, DataNodePredicate pred)
+ {
+ bool? elideChildren = null;
+ return CanOperateOnNodesEx(nodes, pred, out elideChildren);
+ }
+
+ private bool CanOperateOnNodesEx (IList nodes, DataNodePredicate pred, out bool? elideChildren)
+ {
+ GroupCapabilities caps = GroupCapabilities.All;
+ elideChildren = null;
+
+ foreach (TreeNode node in nodes) {
+ if (node == null || !(node.Tag is DataNode))
+ return false;
+
+ DataNode dataNode = node.Tag as DataNode;
+ GroupCapabilities dataCaps;
+ if (!pred(dataNode, out dataCaps))
+ return false;
+
+ caps &= dataCaps;
+
+ bool elideChildrenFlag = (dataNode.DeleteNodeCapabilities & GroupCapabilities.ElideChildren) == GroupCapabilities.ElideChildren;
+ if (elideChildren == null)
+ elideChildren = elideChildrenFlag;
+ if (elideChildren != elideChildrenFlag)
+ return false;
+ }
+
+ if (nodes.Count > 1 && !SufficientCapabilities(nodes, caps))
+ return false;
+
+ return true;
+ }
+
+ private IList ElideChildren (IList nodes)
+ {
+ List filtered = new List();
+
+ foreach (TreeNode node in nodes) {
+ TreeNode parent = node.Parent;
+ bool foundParent = false;
+
+ while (parent != null) {
+ if (nodes.Contains(parent)) {
+ foundParent = true;
+ break;
+ }
+ parent = parent.Parent;
+ }
+
+ if (!foundParent)
+ filtered.Add(node);
+ }
+
+ return filtered;
+ }
+
+ private bool CommonContainer (IEnumerable nodes)
+ {
+ object container = null;
+ foreach (TreeNode node in nodes) {
+ DataNode dataNode = node.Tag as DataNode;
+ if (container == null)
+ container = dataNode.Parent;
+
+ if (container != dataNode.Parent)
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool CommonType (IEnumerable nodes)
+ {
+ Type datatype = null;
+ foreach (TreeNode node in nodes) {
+ DataNode dataNode = node.Tag as DataNode;
+ if (datatype == null)
+ datatype = dataNode.GetType();
+
+ if (datatype != dataNode.GetType())
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool SufficientCapabilities (IEnumerable nodes, GroupCapabilities commonCaps)
+ {
+ bool commonContainer = CommonContainer(nodes);
+ bool commonType = CommonType(nodes);
+
+ bool pass = true;
+ if (commonContainer && commonType)
+ pass &= ((commonCaps & GroupCapabilities.SiblingSameType) == GroupCapabilities.SiblingSameType);
+ else if (commonContainer && !commonType)
+ pass &= ((commonCaps & GroupCapabilities.SiblingMixedType) == GroupCapabilities.SiblingMixedType);
+ else if (!commonContainer && commonType)
+ pass &= ((commonCaps & GroupCapabilities.MultiSameType) == GroupCapabilities.MultiSameType);
+ else if (!commonContainer && !commonType)
+ pass &= ((commonCaps & GroupCapabilities.MultiMixedType) == GroupCapabilities.MultiMixedType);
+
+ return pass;
+ }
+
private void CopyNode (TreeNode node)
{
if (node == null || !(node.Tag is DataNode))
@@ -685,19 +973,42 @@ namespace NBTExplorer.Windows
private void UpdateUI ()
{
TreeNode selected = _nodeTree.SelectedNode;
- if (selected != null && selected.Tag is DataNode) {
+ if (_nodeTree.SelectedNodes.Count > 1) {
+ UpdateUI(_nodeTree.SelectedNodes);
+ }
+ else if (selected != null && selected.Tag is DataNode) {
UpdateUI(selected.Tag as DataNode);
}
else {
+ DisableButtons(_buttonAddTagByte, _buttonAddTagByteArray, _buttonAddTagCompound, _buttonAddTagDouble, _buttonAddTagFloat,
+ _buttonAddTagInt, _buttonAddTagIntArray, _buttonAddTagList, _buttonAddTagLong, _buttonAddTagShort,
+ _buttonAddTagString, _buttonCopy, _buttonCut, _buttonDelete, _buttonEdit, _buttonPaste, _buttonRefresh,
+ _buttonRename);
+
_buttonSave.Enabled = CheckModifications();
_buttonFindNext.Enabled = false;
+ DisableMenuItems(_menuItemCopy, _menuItemCut, _menuItemDelete, _menuItemEditValue, _menuItemPaste, _menuItemRefresh,
+ _menuItemRename);
+
_menuItemSave.Enabled = _buttonSave.Enabled;
_menuItemFind.Enabled = false;
_menuItemFindNext.Enabled = _searchState != null;
}
}
+ private void DisableButtons (params ToolStripButton[] buttons)
+ {
+ foreach (ToolStripButton button in buttons)
+ button.Enabled = false;
+ }
+
+ private void DisableMenuItems (params ToolStripMenuItem[] buttons)
+ {
+ foreach (ToolStripMenuItem button in buttons)
+ button.Enabled = false;
+ }
+
private void UpdateUI (DataNode node)
{
if (node == null)
@@ -736,6 +1047,47 @@ namespace NBTExplorer.Windows
_menuItemRefresh.Enabled = node.CanRefreshNode;
_menuItemFind.Enabled = node.CanSearchNode;
_menuItemFindNext.Enabled = _searchState != null;
+
+ UpdateUI(_nodeTree.SelectedNodes);
+ }
+
+ private void UpdateUI (IList nodes)
+ {
+ if (nodes == null)
+ return;
+
+ _buttonAddTagByte.Enabled = CanOperateOnNodes(nodes, CreateByteNodePred);
+ _buttonAddTagShort.Enabled = CanOperateOnNodes(nodes, CreateShortNodePred);
+ _buttonAddTagInt.Enabled = CanOperateOnNodes(nodes, CreateIntNodePred);
+ _buttonAddTagLong.Enabled = CanOperateOnNodes(nodes, CreateLongNodePred);
+ _buttonAddTagFloat.Enabled = CanOperateOnNodes(nodes, CreateFloatNodePred);
+ _buttonAddTagDouble.Enabled = CanOperateOnNodes(nodes, CreateDoubleNodePred);
+ _buttonAddTagByteArray.Enabled = CanOperateOnNodes(nodes, CreateByteArrayNodePred);
+ _buttonAddTagIntArray.Enabled = CanOperateOnNodes(nodes, CreateIntArrayNodePred);
+ _buttonAddTagString.Enabled = CanOperateOnNodes(nodes, CreateStringNodePred);
+ _buttonAddTagList.Enabled = CanOperateOnNodes(nodes, CreateListNodePred);
+ _buttonAddTagCompound.Enabled = CanOperateOnNodes(nodes, CreateCompoundNodePred);
+
+ _buttonSave.Enabled = CheckModifications();
+ _buttonRename.Enabled = CanOperateOnNodes(nodes, RenameNodePred);
+ _buttonEdit.Enabled = CanOperateOnNodes(nodes, EditNodePred);
+ _buttonDelete.Enabled = CanOperateOnNodes(nodes, DeleteNodePred);
+ _buttonCut.Enabled = CanOperateOnNodes(nodes, CutNodePred) && NbtClipboardController.IsInitialized; ;
+ _buttonCopy.Enabled = CanOperateOnNodes(nodes, CopyNodePred) && NbtClipboardController.IsInitialized; ;
+ _buttonPaste.Enabled = CanOperateOnNodes(nodes, PasteIntoNodePred) && NbtClipboardController.IsInitialized; ;
+ _buttonFindNext.Enabled = CanOperateOnNodes(nodes, SearchNodePred) || _searchState != null;
+ _buttonRefresh.Enabled = CanOperateOnNodes(nodes, RefreshNodePred);
+
+ _menuItemSave.Enabled = _buttonSave.Enabled;
+ _menuItemRename.Enabled = _buttonRename.Enabled;
+ _menuItemEditValue.Enabled = _buttonEdit.Enabled;
+ _menuItemDelete.Enabled = _buttonDelete.Enabled;
+ _menuItemCut.Enabled = _buttonCut.Enabled;
+ _menuItemCopy.Enabled = _buttonCopy.Enabled;
+ _menuItemPaste.Enabled = _buttonPaste.Enabled;
+ _menuItemFind.Enabled = CanOperateOnNodes(nodes, SearchNodePred);
+ _menuItemRefresh.Enabled = _buttonRefresh.Enabled;
+ _menuItemFindNext.Enabled = _searchState != null;
}
private void UpdateOpenMenu ()
@@ -786,6 +1138,23 @@ namespace NBTExplorer.Windows
list.Insert(0, entry);
}
+ private GroupCapabilities CommonCapabilities (IEnumerable capabilities)
+ {
+ GroupCapabilities caps = GroupCapabilities.All;
+ foreach (GroupCapabilities cap in capabilities)
+ caps &= cap;
+ return caps;
+ }
+
+ public void ActionDeleteNode ()
+ {
+ DeleteNode(_nodeTree.SelectedNodes);
+
+ _nodeTree.SelectedNodes.Clear();
+ _nodeTree.SelectedNode = null;
+ UpdateUI();
+ }
+
#region Event Handlers
private void MainForm_Closing (object sender, CancelEventArgs e)
@@ -867,7 +1236,7 @@ namespace NBTExplorer.Windows
private void _buttonDelete_Click (object sender, EventArgs e)
{
- DeleteNode(_nodeTree.SelectedNode);
+ ActionDeleteNode();
}
private void _buttonCopy_Click (object sernder, EventArgs e)
@@ -995,7 +1364,7 @@ namespace NBTExplorer.Windows
private void _menuItemDelete_Click (object sender, EventArgs e)
{
- DeleteNode(_nodeTree.SelectedNode);
+ ActionDeleteNode();
}
private void _menuItemCopy_Click (object sender, EventArgs e)