533 lines
16 KiB
C#
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;
|
|
}
|
|
|
|
}
|
|
} |