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/Windows/Forms/BookmarkTreeView.cs

867 lines
24 KiB
C#

using bzit.bomg;
using bzit.bomg.Data;
using bzit.bomg.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Resources = bzit.bomg.Properties.Resources;
namespace RyzStudio.Windows.Forms
{
public class BookmarkTreeView : System.Windows.Forms.TreeView
{
public enum IconSet
{
Root = 0,
Folder1,
Folder2,
Default
}
public enum NodeType
{
None = 0,
Root,
Folder,
Page
}
public BookmarkTreeViewSNode SNode { get; set; }
public event EventHandler OnNodeChanged = null;
protected IconDatabase iconDatabase = null;
protected TreeNode draggingNode = null;
protected bool hasChanged = false;
public BookmarkTreeView() : base()
{
this.SNode = new BookmarkTreeViewSNode(this);
if (this.ImageList == null)
{
this.ImageList = new ImageList();
}
this.ImageList.ColorDepth = ColorDepth.Depth16Bit;
this.ImageList.ImageSize = new Size(16, 16);
this.ImageList.TransparentColor = Color.Transparent;
this.ImageList.Images.Clear();
this.ImageList.Images.Add(Resources.hexagon);
this.ImageList.Images.Add(Resources.folder);
this.ImageList.Images.Add(Resources.folder_explore);
this.ImageList.Images.Add(Resources.file_text);
this.PathSeparator = "\n";
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool AllowBeginEdit { get; set; } = false;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new ImageList ImageList { get => base.ImageList; set => base.ImageList = value; }
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool HasChanged
{
get => hasChanged;
set
{
hasChanged = value;
OnNodeChanged?.Invoke(null, null);
}
}
#region public methods
public bool InitialiseIconDatabase(out string message, string filename)
{
message = string.Empty;
if (string.IsNullOrWhiteSpace(filename))
{
return false;
}
if (iconDatabase == null)
{
iconDatabase = new IconDatabase();
}
bool rv = false;
if (File.Exists(filename))
{
rv = iconDatabase.LoadFile(filename);
if (!rv)
{
rv = iconDatabase.Create(filename, true, null, true);
if (!rv)
{
message = iconDatabase.LastError;
return false;
}
}
}
else
{
rv = iconDatabase.Create(filename, true, null, true);
if (!rv)
{
message = iconDatabase.LastError;
return false;
}
}
return true;
}
public TreeNode AddFolder(TreeNode node) => this.AddFolder(node, "New Folder (" + DateTime.Now.ToString("yyyy-MM-dd-HHmmss") + ")");
public TreeNode AddFolder(TreeNode node, string name)
{
if (node == null)
{
return null;
}
if (node.Tag != null)
{
return null;
}
return node.Nodes.Add(encodePath(name), name, (int)IconSet.Folder1, (int)IconSet.Folder2);
}
public void AddItem(List<BookmarkItemViewModel> viewModelList)
{
this.Clear();
if (viewModelList == null)
{
return;
}
foreach (BookmarkItemViewModel item in viewModelList)
{
AddItem(item);
}
this.HasChanged = false;
}
public void AddItem(BookmarkItemViewModel viewModel)
{
int iconIndex = addIcon(viewModel);
TreeNode tn = new TreeNode(viewModel.SiteName, iconIndex, iconIndex);
tn.Tag = viewModel;
tn.ToolTipText = viewModel.ToString();
TreeNode tn2 = addFolderPath(viewModel.TreeviewPath);
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() =>
{
tn2.Nodes.Add(tn);
}));
}
else
{
tn2.Nodes.Add(tn);
}
this.HasChanged = true;
}
public void AddItem(TreeNode treeNode, BookmarkItemViewModel viewModel)
{
if (treeNode == null)
{
return;
}
int iconIndex = addIcon(viewModel);
TreeNode tn = new TreeNode(viewModel.SiteName, iconIndex, iconIndex);
tn.Tag = viewModel;
tn.ToolTipText = viewModel.ToString();
treeNode.Nodes.Add(tn);
this.HasChanged = true;
}
public void AddOrUpdateItem(TreeNode treeNode, BookmarkItemViewModel viewModel)
{
switch (this.GetNodeType(treeNode))
{
case NodeType.Root:
case NodeType.Folder:
AddItem(treeNode, viewModel);
break;
case NodeType.Page:
UpdateItem(treeNode, viewModel);
break;
default:
break;
}
}
public void Clear()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() =>
{
this.Nodes.Clear();
}));
}
else
{
this.Nodes.Clear();
}
this.HasChanged = true;
}
public void Clear(string name)
{
this.Nodes.Clear();
this.Nodes.Add("", name?.Trim(), (int)IconSet.Root, (int)IconSet.Root);
this.HasChanged = true;
}
public void CloseIconDatabase()
{
iconDatabase.Close();
}
public int CountBookmarkItem()
{
int itemCount = 0;
if (this.Nodes.Count <= 0)
{
return itemCount;
}
foreach (TreeNode item in this.Nodes)
{
traverseBookmarkList(ref itemCount, item);
}
return itemCount;
}
public NodeType GetNodeType(TreeNode node)
{
if (node.Tag == null)
{
if (node.Equals(this.Nodes[0]))
{
return NodeType.Root;
}
else
{
return NodeType.Folder;
}
}
else
{
if (node.Tag is BookmarkItemViewModel)
{
return NodeType.Page;
}
else
{
return NodeType.None;
}
}
}
public string GetNodePath(TreeNode node)
{
string[] folderList = node.FullPath.Split('\n');
if (folderList.Length < 2)
{
return null;
}
//if (folderList.Length < 2)
//{
// return "/";
//}
StringBuilder sb = new StringBuilder();
for (int i=0; i<(folderList.Length - 1); i++)
{
sb.Append("\\");
sb.Append(encodePath(folderList[i] ?? string.Empty));
}
sb.Append("\\");
return sb.ToString();
}
public BookmarkItemViewModel GetViewModel(TreeNode node) => (BookmarkItemViewModel)node.Tag;
public List<BookmarkItemViewModel> GetBookmarkList()
{
List<BookmarkItemViewModel> rs = new List<BookmarkItemViewModel>();
if (this.Nodes.Count <= 0)
{
return rs;
}
foreach (TreeNode item in this.Nodes)
{
traverseBookmarkList(rs, item);
}
return rs;
}
public List<TreeNode> GetBookmarkNodeList()
{
List<TreeNode> rs = new List<TreeNode>();
if (this.Nodes.Count <= 0)
{
return rs;
}
foreach (TreeNode item in this.Nodes)
{
traverseBookmarkList(rs, item);
}
return rs;
}
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 UpdateItem(TreeNode treeNode, BookmarkItemViewModel viewModel)
{
if (treeNode == null)
{
return;
}
int iconIndex = addIcon(viewModel);
treeNode.Text = viewModel.SiteName;
treeNode.ImageIndex = iconIndex;
treeNode.SelectedImageIndex = iconIndex;
treeNode.Tag = viewModel;
treeNode.ToolTipText = viewModel.ToString();
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)
{
AllowBeginEdit = false;
e.CancelEdit = true;
return;
}
this.HasChanged = true;
base.OnBeforeLabelEdit(e);
}
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;
}
NodeType nodeType = this.SNode.GetNodeType();
switch (e.KeyCode)
{
case Keys.Insert:
if (e.Modifiers == Keys.Shift)
{
if ((nodeType == NodeType.Root) || (nodeType == NodeType.Folder))
{
this.SelectedNode = this.SNode.AddFolder();
}
else if (nodeType == NodeType.Page)
{
this.SelectedNode = tn.Parent;
this.SelectedNode = this.SNode.AddFolder();
}
}
break;
case Keys.Delete:
if (!tn.IsEditing)
{
this.SNode.Delete();
}
break;
case Keys.F2:
if ((nodeType == NodeType.Root) || (nodeType == NodeType.Folder))
{
this.AllowBeginEdit = true;
this.SNode.Edit();
}
break;
case Keys.F3:
switch (nodeType)
{
case NodeType.Root:
case NodeType.Folder:
try
{
Clipboard.SetText(tn.Text ?? string.Empty);
}
catch
{
}
break;
case NodeType.Page:
BookmarkItemViewModel viewModel = this.SNode.GetViewModel();
if (viewModel != null)
{
try
{
Clipboard.SetText(viewModel.SiteAddress ?? string.Empty);
}
catch
{
}
}
break;
default:
break;
}
break;
case Keys.Up:
if (e.Modifiers == Keys.Control)
{
this.SNode.MoveUp();
this.HasChanged = true;
}
break;
case Keys.Down:
if (e.Modifiers == Keys.Control)
{
this.SNode.MoveDown();
this.HasChanged = true;
}
break;
default: break;
}
base.OnPreviewKeyDown(e);
}
#endregion
protected int addIcon(BookmarkItemViewModel viewModel) => addIcon(viewModel.ToModel());
protected int addIcon(BookmarkItemModel model)
{
if (this.ImageList.Images.ContainsKey(model.SiteAddress))
{
return this.ImageList.Images.IndexOfKey(model.SiteAddress);
}
if (iconDatabase.HasIcon(model.SiteAddress))
{
Image rs = iconDatabase.GetIcon(model.SiteAddress);
if (rs == null)
{
return (int)IconSet.Default;
}
else
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() =>
{
this.ImageList.Images.Add(model.SiteAddress, rs);
}));
}
else
{
this.ImageList.Images.Add(model.SiteAddress, rs);
}
return this.ImageList.Images.IndexOfKey(model.SiteAddress);
}
}
byte[] rawData;
Bitmap bmp = model.RetrieveFavicon(out rawData);
if (bmp == null)
{
return (int)IconSet.Default;
}
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() =>
{
this.ImageList.Images.Add(model.SiteAddress, bmp);
}));
}
else
{
this.ImageList.Images.Add(model.SiteAddress, bmp);
}
iconDatabase.AddIcon(model.SiteAddress, rawData);
return this.ImageList.Images.IndexOfKey(model.SiteAddress);
}
protected TreeNode addFolderPath(string path)
{
TreeNode tn = null;
//TreeNode tn = this.Nodes[0];
//if (tn == null)
//{
// return tn;
//}
if (string.IsNullOrWhiteSpace(path))
{
return tn;
}
if (string.IsNullOrWhiteSpace(path.Trim('\\')))
{
return tn;
}
string[] folderList = path.Trim('\\').Split('\\');
if (folderList.Length <= 0)
{
return tn;
}
for (int i=0; i<folderList.Length; i++)
{
string item = folderList[i];
if (i <= 0)
{
if (!this.Nodes.ContainsKey(item))
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() =>
{
this.Nodes.Add(item, decodePath(item), (int)IconSet.Root, (int)IconSet.Root);
}));
}
else
{
this.Nodes.Add(item, decodePath(item), (int)IconSet.Root, (int)IconSet.Root);
}
}
tn = this.Nodes[item];
}
else
{
if (!tn.Nodes.ContainsKey(item))
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() =>
{
tn.Nodes.Add(item, decodePath(item), (int)IconSet.Folder1, (int)IconSet.Folder2);
}));
}
else
{
tn.Nodes.Add(item, decodePath(item), (int)IconSet.Folder1, (int)IconSet.Folder2);
}
}
tn = tn.Nodes[item];
}
}
return tn;
}
protected string decodePath(string value) => System.Web.HttpUtility.UrlDecode(value);
protected string encodePath(string value) => System.Web.HttpUtility.UrlEncode(value);
protected void traverseBookmarkList(List<BookmarkItemViewModel> rs, TreeNode node)
{
foreach (TreeNode tn in node.Nodes)
{
NodeType nodeType = this.GetNodeType(tn);
if (nodeType == NodeType.Folder)
{
traverseBookmarkList(rs, tn);
}
else if (nodeType == NodeType.Page)
{
BookmarkItemViewModel nodeTag = this.GetViewModel(tn);
nodeTag.TreeviewPath = this.GetNodePath(tn);
if (nodeTag != null)
{
rs.Add(nodeTag);
}
}
}
}
protected void traverseBookmarkList(List<TreeNode> rs, TreeNode node)
{
foreach (TreeNode tn in node.Nodes)
{
NodeType nodeType = this.GetNodeType(tn);
if (nodeType == NodeType.Folder)
{
traverseBookmarkList(rs, tn);
}
else if (nodeType == NodeType.Page)
{
BookmarkItemViewModel nodeTag = this.GetViewModel(tn);
nodeTag.TreeviewPath = this.GetNodePath(tn);
if (nodeTag != null)
{
rs.Add(tn);
}
}
}
}
protected void traverseBookmarkList(ref int itemCount, TreeNode node)
{
foreach (TreeNode tn in node.Nodes)
{
NodeType nodeType = this.GetNodeType(tn);
if (nodeType == NodeType.Folder)
{
traverseBookmarkList(ref itemCount, tn);
}
else if (nodeType == NodeType.Page)
{
itemCount++;
}
}
}
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;
}
}
}