This repository has been archived on 2024-12-29. You can view files and clone it, but cannot push or open issues or pull requests.
bookmark-manager-r4/Windows/Forms/BookmarkTreeView.cs
2026-05-22 01:16:08 +01:00

533 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using BukkuBuddy;
using BukkuBuddy.DTOs.SaveFile;
using BukkuBuddy.Resources;
namespace RyzStudio.Windows.Forms
{
public partial class BookmarkTreeView : TTreeView
{
public delegate bool EditNodeDelegate(BookmarkTreeView sender, TreeNode node, App6Options.Item model);
public delegate void OnChangedDelegate(BookmarkTreeView sender, bool hasChanged);
public enum NodeIcon
{
Root = 0,
Folder1,
Folder2,
Default
}
public enum NodeType
{
None = 0,
Root,
Folder,
Page
}
protected const string DEFAULT_NEW_FOLDER_NAME = "New Folder";
protected const string DEFAULT_NEW_PAGE_NAME = "New Page";
public BookmarkTreeView()
{
if (this.ImageList == null)
{
this.ImageList = new ImageList();
}
this.ImageList.ColorDepth = ColorDepth.Depth32Bit;
this.ImageList.ImageSize = new Size(16, 16);
this.ImageList.TransparentColor = Color.Transparent;
this.Clear(false, true);
}
#region integrated behaviour
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
switch (this.GetNodeType())
{
case BookmarkTreeView.NodeType.Root:
if (this.RootContextMenu != null) this.RootContextMenu.Show(e.Node.TreeView, e.X, e.Y);
break;
case BookmarkTreeView.NodeType.Folder:
if (this.FolderContextMenu != null) this.FolderContextMenu.Show(e.Node.TreeView, e.X, e.Y);
break;
case BookmarkTreeView.NodeType.Page:
if (this.PageContextMenu != null) this.PageContextMenu.Show(e.Node.TreeView, e.X, e.Y);
break;
default:
break;
}
}
base.OnNodeMouseClick(e);
}
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
var tn = this.SelectedNode;
if (tn == null)
{
return;
}
NodeType nodeType = this.GetNodeType();
switch (e.KeyCode)
{
case Keys.Insert:
if (e.Modifiers == Keys.Shift)
{
if ((nodeType == NodeType.Root) || (nodeType == NodeType.Folder))
{
this.SelectedNode = this.AddFolder();
}
else if (nodeType == NodeType.Page)
{
this.SelectedNode = tn.Parent;
this.SelectedNode = this.AddFolder();
}
}
else
{
if ((nodeType == NodeType.Root) || (nodeType == NodeType.Folder))
{
this.SelectedNode = this.AddNode();
}
else if (nodeType == NodeType.Page)
{
this.SelectedNode = tn.Parent;
this.SelectedNode = this.AddNode();
}
}
break;
case Keys.Delete:
if (!tn.IsEditing)
{
if (MessageBox.Show("Delete?", "Delete?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
{
this.DeleteNode();
this.HasChanged = true;
}
}
break;
case Keys.F2:
this.EditNode();
break;
case Keys.F3:
switch (nodeType)
{
case NodeType.Root:
case NodeType.Folder:
try
{
Clipboard.SetText(tn.Text ?? string.Empty);
}
catch
{
}
break;
case NodeType.Page:
var viewModel = UIControl.GetTag<App6Options.Item>(this.SelectedNode);
if (viewModel != null)
{
try
{
Clipboard.SetText(viewModel.Address ?? string.Empty);
}
catch
{
}
}
break;
default:
break;
}
break;
case Keys.Up:
if (e.Modifiers == Keys.Control)
{
this.MoveUp();
this.HasChanged = true;
}
break;
case Keys.Down:
if (e.Modifiers == Keys.Control)
{
this.MoveDown();
this.HasChanged = true;
}
break;
case Keys.Apps:
this.OnNodeMouseClick(new TreeNodeMouseClickEventArgs(tn, MouseButtons.Right, 1, tn.Bounds.X, tn.Bounds.Y));
break;
default: break;
}
base.OnPreviewKeyDown(e);
}
#endregion
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ContextMenuStrip RootContextMenu { get; set; } = null;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ContextMenuStrip FolderContextMenu { get; set; } = null;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ContextMenuStrip PageContextMenu { get; set; } = null;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new bool HasChanged
{
get => base.HasChanged;
set
{
base.HasChanged = value;
if (this.OnChanged != null)
{
this.OnChanged(this, value);
}
}
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public EditNodeDelegate OnEditNode { get; set; }
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new OnChangedDelegate OnChanged { get; set; }
public TreeNode AddFolder(TreeNode node = null, string name = "")
{
if (node == null)
{
node = this.SelectedNode;
}
var nodeType = GetNodeType(node);
if ((nodeType != NodeType.Root) && (nodeType != NodeType.Folder))
{
return null;
}
if (string.IsNullOrWhiteSpace(name))
{
name = DEFAULT_NEW_FOLDER_NAME;
}
var key = EncodeNodeName(name);
var treeNode = node.Nodes.Add(key, name, (int)NodeIcon.Folder1, (int)NodeIcon.Folder2);
node.Expand();
this.SelectedNode = treeNode;
this.EditNode(treeNode);
this.HasChanged = true;
return treeNode;
}
public TreeNode AddNode(TreeNode node = null)
{
if (node == null)
{
node = this.SelectedNode;
}
if (this.OnEditNode == null)
{
return null;
}
var nodeType = GetNodeType(node);
if ((nodeType != NodeType.Root) && (nodeType != NodeType.Folder))
{
return null;
}
var newItem = new App6Options.Item()
{
Id = Guid.NewGuid(),
Title = DEFAULT_NEW_PAGE_NAME,
Path = GetNodePath(node)
};
var newNode = this.AddNode(newItem);
var result = this.OnEditNode(this, newNode, newItem);
if (result)
{
newNode.EnsureVisible();
this.SelectedNode = newNode;
this.HasChanged = true;
return newNode;
}
else
{
this.DeleteNode(newNode);
return null;
}
}
public TreeNode AddNode(App6Options.Item model)
{
if (model.Id == Guid.Empty)
{
model.Id = Guid.NewGuid();
}
var parentNode = this.CreateNodePath(model?.Path?.Trim() ?? string.Empty, (int)NodeIcon.Folder1, (int)NodeIcon.Folder2);
// Add custom favicon
var hasIcon = AddImage(model.Id.ToString(), model.Icon);
var newNode = new TreeNode(model?.Title?.Trim() ?? string.Empty, (int)NodeIcon.Default, (int)NodeIcon.Default);
newNode.Tag = model;
newNode.ToolTipText = model.ToString();
if (hasIcon)
{
newNode.ImageKey = newNode.SelectedImageKey = model.Id.ToString();
}
UIControl.Add(parentNode, newNode);
this.HasChanged = true;
return newNode;
}
public void Clear(bool deleteNodes, bool deleteIcons, string rootName = "")
{
if (deleteNodes)
{
UIControl.Invoke(this, (x) =>
{
this.Nodes.Clear();
this.Nodes.Add("", rootName?.Trim() ?? string.Empty, (int)NodeIcon.Root, (int)NodeIcon.Root);
});
}
if (deleteIcons)
{
UIControl.Invoke(this, (x) =>
{
this.ImageList.Images.Clear();
this.ImageList.Images.Add(TreeViewResource.bookmark_root);
this.ImageList.Images.Add(TreeViewResource.folder);
this.ImageList.Images.Add(TreeViewResource.folder_symlink);
this.ImageList.Images.Add("default", TreeViewResource.file_question_mark);
});
}
this.HasChanged = true;
}
public TreeNode EditNode(TreeNode node = null)
{
if (node == null)
{
node = this.SelectedNode;
}
if (node.IsEditing)
{
return null;
}
var nodeType = GetNodeType(node);
if ((nodeType == NodeType.Root) || (nodeType == NodeType.Folder))
{
_allowBeginEdit = true;
node.BeginEdit();
}
else if (nodeType == NodeType.Page)
{
var model = UIControl.GetTag<App6Options.Item>(node);
model.Path = GetNodePath(node);
if (this.OnEditNode != null) this.OnEditNode(this, node, model);
}
return node;
}
public NodeType GetNodeType(TreeNode node = null)
{
if (node == null)
{
node = this.SelectedNode;
}
if (node == null)
{
return NodeType.None;
}
if (node.Tag == null)
{
if (node.Equals(node.TreeView.Nodes[0]))
{
return NodeType.Root;
}
else
{
return NodeType.Folder;
}
}
else
{
if (node.Tag is App6Options.Item)
{
return NodeType.Page;
}
else
{
return NodeType.None;
}
}
}
public void UpdateNode(TreeNode node, App6Options.Item model)
{
if (node == null)
{
return;
}
if (model == null)
{
return;
}
if (model.Id == Guid.Empty)
{
return;
}
// Replace icon
var hasIcon = AddImage(model.Id.ToString(), model.Icon);
UIControl.Invoke(this, (x) =>
{
node.Text = model.Title;
node.ImageIndex = node.SelectedImageIndex = (int)NodeIcon.Default;
node.Tag = model;
node.ToolTipText = model.ToString();
if (hasIcon)
{
node.ImageKey = node.SelectedImageKey = model.Id.ToString();
}
});
this.HasChanged = true;
}
public List<string> GetAllNodePaths()
{
var result = this.GetAllNodes(this.Nodes);
return result.Where(x => this.GetNodeType(x) == NodeType.Folder)
.Select(x => this.GetNodePath(x))
?.ToList()
?? new List<string>();
}
public List<KeyValuePair<TreeNode, App6Options.Item>> GetAllItems()
{
var result = this.ToNodeList<App6Options.Item>() ?? new List<KeyValuePair<TreeNode, App6Options.Item>>();
foreach (var node in result)
{
node.Value.Path = this.GetNodePath(node.Key);
if (node.Value.Path.Contains('\n'))
{
node.Value.Path = node.Value.Path.Substring(0, node.Value.Path.LastIndexOf('\n'));
}
}
return result;
}
private bool AddImage(string key, Image image)
{
UIControl.Invoke(this, (x) =>
{
if (this.ImageList.Images.ContainsKey(key))
{
this.ImageList.Images.RemoveByKey(key);
}
});
if (image == null)
{
return false;
}
try
{
if (image.Width <= 0)
{
return false;
}
}
catch (Exception)
{
return false;
}
UIControl.Invoke(this, (x) =>
{
this.ImageList.Images.Add(key, new Bitmap(image));
});
return true;
}
private List<TreeNode> GetAllNodes(TreeNodeCollection nodes)
{
var result = new List<TreeNode>();
foreach (var node in nodes.OfType<TreeNode>())
{
result.Add(node);
result.AddRange(this.GetAllNodes(node.Nodes));
}
return result;
}
}
}