This repository has been archived on 2022-09-30. You can view files and clone it, but cannot push or open issues or pull requests.
bookmark-manager/RyzStudio/Windows/Forms/MovableTreeView.cs

730 lines
18 KiB
C#

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); }
}
}