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(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(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 GetAllNodePaths() { var result = this.GetAllNodes(this.Nodes); return result.Where(x => this.GetNodeType(x) == NodeType.Folder) .Select(x => this.GetNodePath(x)) ?.ToList() ?? new List(); } public List> GetAllItems() { var result = this.ToNodeList() ?? new List>(); 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 GetAllNodes(TreeNodeCollection nodes) { var result = new List(); foreach (var node in nodes.OfType()) { result.Add(node); result.AddRange(this.GetAllNodes(node.Nodes)); } return result; } } }