using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace RyzStudio.Windows.Forms { public class MovableTreeView : System.Windows.Forms.TreeView { public enum IconSet { Root = 0, Folder1, Folder2, Default } public MovableTreeViewSelectedNode SNode { get; set; } public delegate void NodeCountUpdated(ulong v); public EventHandler OnChanged = null; public NodeCountUpdated OnNodeCountUpdate = null; protected const char pathSeparator = '|'; //protected const int folderImageIndex = 1; //protected const int folderSelectedImageIndex = 2; protected TreeNode draggingNode = null; protected bool allowBeginEdit = false; //// public int[] folderImageIndex = { 1, 2 }; protected ulong nodeCount = 0; protected bool hasChanged = false; public MovableTreeView() { this.SNode = new MovableTreeViewSelectedNode(this); } #region public properties /* [Category("Data")] public char CustomPathSeparator { get { return customPathSeparator; } set { customPathSeparator = value; } }*/ [Browsable(false)] public TreeNode[] NodeList { get { TreeNode[] rv = new TreeNode[0]; if (this.Nodes.Count <= 0) { return rv; } foreach (TreeNode tn in this.Nodes) { traverseNodeList(ref rv, tn); } return rv; } } //[Browsable(false)] //public string[] NodeNameList //{ // get // { // string[] rv = new string[0]; // if (this.Nodes.Count <= 0) // { // return rv; // } // foreach (TreeNode tn in this.Nodes) // { // traverseNodeNameList(ref rv, tn); // } // return rv; // } //} [Browsable(false)] public ulong NodeCount { get { return nodeCount; } } [Browsable(false)] public ulong NodeCountCalc { get { ulong rv = 0; if (this.Nodes.Count <= 0) { return rv; } foreach (TreeNode tn in this.Nodes) { traverseNodeCount(ref rv, tn); } return rv; } } [Browsable(false)] public bool HasChanged { get { return hasChanged; } set { hasChanged = value; OnChanged?.Invoke(null, null); } } #endregion #region public methods //public TreeNode AddFolder() //{ // return this.AddFolder("New Folder " + (new Random()).Next(10001, 99999).ToString()); //} //public TreeNode AddFolder(string name) //{ // if (this.SelectedNode == null) // { // return null; // } // if (this.SelectedNode.Tag != null) // { // return null; // } // this.HasChanged = true; // TreeNode tn = this.SelectedNode.Nodes.Add(PathEncode(name), name, folderImageIndex, folderSelectedImageIndex); // this.SelectedNode = tn; // OnAddFolderNode(tn); // return tn; //} //public TreeNode AddBookmarkPage() //{ // return this.AddBookmarkPage("New Page " + (new Random()).Next(10001, 99999).ToString()); //} //public TreeNode AddBookmarkPage(string name, int icon = 3) //{ // if (this.SelectedNode == null) // { // return null; // } // if (this.SelectedNode.Tag != null) // { // return null; // } // this.HasChanged = true; // TreeNode tn = this.SelectedNode.Nodes.Add(PathEncode(name), name, icon, icon); // tn.Tag = new object(); // tn.ToolTipText = name; // nodeCount++; // NodeCountUpdate(nodeCount); // this.SelectedNode = tn; // OnAddItemNode(tn); // return tn; //} //public TreeNode AddBookmarkPageFullPath(string name, int icon = 3) //{ // if (this.Nodes.Count <= 0) // { // return null; // } // this.HasChanged = true; // TreeNode tn2; // if (!name.Contains(pathSeparator.ToString())) // { // tn2 = this.Nodes[0].Nodes.Add(name, PathDecode(name), icon, icon); // tn2.ToolTipText = name; // nodeCount++; // } // else // { // tn2 = this.Nodes[0]; // string[] folders = name.Split(pathSeparator); // for (int x = 0; x < (folders.Length - 1); x++) // { // string dr = folders[x].Trim(); // if (tn2.Nodes.ContainsKey(dr)) // { // tn2 = tn2.Nodes[dr]; // } // else // { // tn2 = tn2.Nodes.Add(dr, PathDecode(dr), folderImageIndex, folderSelectedImageIndex); // } // } // string tm = folders[(folders.Length - 1)].Trim(); // tn2 = tn2.Nodes.Add(tm, PathDecode(tm), icon, icon); // tn2.Tag = new object(); // tn2.ToolTipText = tm; // nodeCount++; // } // NodeCountUpdate(nodeCount); // return tn2; //} public void EditNode() { this.HasChanged = true; if (this.SelectedNode == null) { return; } if (!this.SelectedNode.IsEditing) { allowBeginEdit = true; this.SelectedNode.BeginEdit(); } } //public void DeleteNode() //{ // if (this.SelectedNode == null) // { // return; // } // if (this.Nodes.Count <= 0) // { // return; // } // if (this.SelectedNode.Equals(this.Nodes[0])) // { // return; // } // this.HasChanged = true; // this.SelectedNode.Remove(); // if (this.SelectedNode.Tag == null) // { // nodeCount = this.NodeCountCalc; // } // else // { // nodeCount--; // } // NodeCountUpdate(nodeCount); //} //public void SortNode() //{ // TreeNode tn = this.SelectedNode; // string[] tnv = new string[0]; // TreeNode[] tna = new TreeNode[0]; // this.HasChanged = true; // foreach (TreeNode tn2 in tn.Nodes) // { // Array.Resize(ref tna, (tna.Length + 1)); // tna[(tna.Length - 1)] = tn2; // Array.Resize(ref tnv, (tnv.Length + 1)); // tnv[(tnv.Length - 1)] = tn2.Text; // } // Array.Sort(tnv, tna); // tn.Nodes.Clear(); // foreach (TreeNode tn2 in tna) // { // tn.Nodes.Add(tn2); // } //} //public void MoveNodeUp() //{ // TreeNode tn = this.SelectedNode; // if (tn.Parent == null) // { // return; // } // if (tn.Index == 0) // { // return; // } // this.HasChanged = true; // int n = tn.Index - 1; // TreeNode tn1 = tn.Parent; // tn1.Nodes.Remove(tn); // tn1.Nodes.Insert(n, tn); // this.SelectedNode = tn; //} //public void MoveNodeDown() //{ // TreeNode tn = this.SelectedNode; // if (tn.Parent == null) // { // return; // } // TreeNode tn1 = tn.Parent; // this.HasChanged = true; // if (tn.Index >= (tn1.Nodes.Count - 1)) // { // return; // } // int n = tn.Index + 1; // tn1.Nodes.Remove(tn); // tn1.Nodes.Insert(n, tn); // this.SelectedNode = tn; //} public string GetNodeFullPath(TreeNode node) { string rv = PathEncode(node.Text); TreeNode tn = node; while (true) { tn = tn.Parent; if (tn == null) { break; } if (tn.Level == 0) { break; } rv = PathEncode(tn.Text) + pathSeparator.ToString() + rv; } return rv; } public bool FindTextNode(TreeNode node, string term) { if (node == null) { return false; } if (this.Nodes.Count <= 0) { return false; } bool rt = false; bool inclusive = false; TreeNode tn = node; while (true) { if (tn == null) { break; } if (inclusive) { if (tn.Text.ToLower().Contains(term.ToLower())) { this.SelectedNode = tn; this.SelectedNode.EnsureVisible(); rt = true; break; } } if (tn.Nodes.Count > 0) { tn = tn.Nodes[0]; inclusive = true; } else { if (tn.NextNode != null) { tn = tn.NextNode; inclusive = true; } else { while (true) { tn = tn.Parent; if (tn == null) { break; } if (tn.NextNode != null) { tn = tn.NextNode; break; } } inclusive = true; } } } return rt; } public void Clear() { nodeCount = 0; NodeCountUpdate(nodeCount); this.Nodes.Clear(); this.HasChanged = true; } #endregion #region integrated behaviour protected override void OnItemDrag(ItemDragEventArgs e) { base.OnItemDrag(e); draggingNode = (TreeNode)e.Item; DoDragDrop(e.Item, DragDropEffects.Move); } protected override void OnDragDrop(DragEventArgs e) { base.OnDragDrop(e); if (draggingNode.Level <= 0) { return; } TreeNode en = this.GetNodeAt(this.PointToClient(new Point(e.X, e.Y))); if (en == null) { return; } if (isNodeChild(draggingNode, en)) { return; } TreeNode dn = draggingNode; if (en.Tag == null) { dn.Parent.Nodes.Remove(dn); en.Nodes.Insert(0, dn); } else { en.Parent.Nodes.Remove(dn); en.Parent.Nodes.Insert(en.Index + 1, dn); } this.HasChanged = true; } protected override void OnDragEnter(DragEventArgs e) { base.OnDragEnter(e); e.Effect = DragDropEffects.Move; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); this.SelectedNode = this.GetNodeAt(e.Location); } protected override void OnDragOver(DragEventArgs e) { base.OnDragOver(e); this.SelectedNode = this.GetNodeAt(this.PointToClient(new Point(e.X, e.Y))); } protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e) { if (!allowBeginEdit) { e.CancelEdit = true; return; } this.HasChanged = true; base.OnBeforeLabelEdit(e); if (e.Node == null) { e.CancelEdit = true; } else { if (e.Node.Tag == null) { // do it } else { e.CancelEdit = true; } } } protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e) { base.OnAfterLabelEdit(e); if (e.Node.Tag == null) { if (e.Label == null) { e.CancelEdit = true; } else { if (e.Label.Trim().Length <= 0) { e.CancelEdit = true; } } } else { e.CancelEdit = true; } allowBeginEdit = false; } protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e) { TreeNode tn = this.SelectedNode; if (tn == null) { return; } switch (e.KeyCode) { case Keys.Insert: if (e.Modifiers == Keys.Shift) { this.SelectedNode = this.SNode.AddFolder(); } else { //##AddBookmarkPage(); } break; case Keys.Delete: if (!tn.IsEditing) { this.SNode.Delete(); } break; case Keys.F2: if (tn.Tag == null) { this.EditNode(); } break; case Keys.Up: if (e.Modifiers == Keys.Control) { this.SNode.MoveUp(); } break; case Keys.Down: if (e.Modifiers == Keys.Control) { this.SNode.MoveDown(); } break; default: break; } base.OnPreviewKeyDown(e); } protected virtual void NodeCountUpdate(ulong v) { this.OnNodeCountUpdate?.Invoke(v); } #endregion protected bool isNodeChild(TreeNode drag_node, TreeNode drop_node) { TreeNode tn = drop_node; while (true) { if (tn.Parent == null) { break; } if (tn.Equals(drag_node)) { return true; } tn = tn.Parent; } return false; } protected void traverseNodeList(ref TreeNode[] results, TreeNode node) { foreach (TreeNode tn in node.Nodes) { if (tn.Tag == null) { traverseNodeList(ref results, tn); } else { Array.Resize(ref results, (results.Length + 1)); results[(results.Length - 1)] = tn; } } } //protected void traverseNodeNameList(ref string[] results, TreeNode node) //{ // foreach (TreeNode tn in node.Nodes) // { // if (tn.Tag == null) // { // traverseNodeNameList(ref results, tn); // } // else // { // Array.Resize(ref results, (results.Length + 1)); // results[(results.Length - 1)] = this.GetNodeFullPath(tn); // } // } //} protected void traverseNodeCount(ref ulong results, TreeNode node) { foreach (TreeNode tn in node.Nodes) { if (tn.Tag == null) { traverseNodeCount(ref results, tn); } else { results++; } } } protected virtual void OnAddFolderNode(TreeNode node) { } protected virtual void OnAddItemNode(TreeNode node) { } ////protected string PathEncode(string text) { return RyzStudio.String.EncodeTo64(text); } //protected string PathDecode(string text) { return RyzStudio.String.DecodeFrom64(text); } protected string PathEncode(string text) { return System.Web.HttpUtility.UrlEncode(text); } protected string PathDecode(string text) { return System.Web.HttpUtility.UrlDecode(text); } } }