using System; using System.Text; using System.IO; using System.Xml; using System.Collections.Generic; using System.Reflection; using System.Windows.Forms; using CommonLang; using CommonLang.Xml; using System.Collections; using CommonLang.Property; using System.Drawing; using System.Text.RegularExpressions; using CommonLang.Concurrent; using CommonLang.IO; namespace CommonFroms.G2D { public class G2DTreeView : TreeView { private TreeNode last_find_object = null; public G2DTreeView() { this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.G2DTreeView_KeyDown); ; } private void G2DTreeView_KeyDown(object sender, KeyEventArgs e) { if (e.Control && e.KeyCode == Keys.F) { G2DSearchDialog find = new G2DSearchDialog(); find.FindPrevClicked += (string text) => { TreeNode finded = FormUtils.FindLastTreeNodeByText(this.Nodes, text, last_find_object); if (finded != null) { this.SelectedNode = finded; finded.Expand(); last_find_object = finded; } return finded; }; find.FindNextClicked += (string text) => { TreeNode finded = FormUtils.FindTreeNodeByText(this.Nodes, text, last_find_object); if (finded != null) { this.SelectedNode = finded; finded.Expand(); last_find_object = finded; } return finded; }; find.FindClicked += (string text) => { TreeNode finded = FormUtils.FindTreeNodeByText(this.Nodes, text, last_find_object, true); if (finded != null) { this.SelectedNode = finded; finded.Expand(); last_find_object = finded; find.Close(); } return finded; }; find.Show(); } } } public class G2DTreeNodeBase : TreeNode { public void RemoveFromParent() { if (Parent != null) { Parent.Nodes.Remove(this); } } public static T FindNodeByText( TreeNode parent, string text, bool searchAllChildren) where T : TreeNode { foreach (TreeNode tn in parent.Nodes) { if (typeof(T).IsAssignableFrom(tn.GetType()) && tn.Text.Equals(text)) { return (T)tn; } if (searchAllChildren) { T stn = FindNodeByText(tn, text, true); if (stn != null) { return (T)stn; } } } return default(T); } public static bool ContainsChild(TreeNode parent, TreeNode node, bool searchAllChildren) { foreach (TreeNode tn in parent.Nodes) { if (tn == node) { return true; } if (searchAllChildren) { if (ContainsChild(tn, node, true)) { return true; } } } return false; } public static List GetAllNodes(TreeNode tn) { List ret = new List(); GetAllNodes(tn, ret); return ret; } public static List GetAllNodes(TreeNodeCollection tc) { List ret = new List(); foreach (TreeNode tn in tc) { GetAllNodes(tn, ret); } return ret; } public static void GetAllNodes(TreeNode node, List ret) { ret.Add(node); foreach (TreeNode tn in node.Nodes) { GetAllNodes(tn, ret); } } } public interface IDataNode { string DataID { get; } } public class G2DTreeNode : G2DTreeNodeBase, IDataNode { readonly private static Encoding UTF8 = new UTF8Encoding(false); private T mData; public G2DTreeNode(T data) { this.mData = data; this.Text = data.ToString(); this.Tag = mData; this.Name = this.DataID; } public string path; private static Regex r = new Regex("^[^/]*(/|$)"); public G2DTreeNode(Stream input) { using (XmlReader xml = XmlReader.Create(input)) { XmlDocument doc = new XmlDocument(); doc.Load(xml); this.mData = (T)XmlUtil.XmlToObject(typeof(T), doc); this.Text = mData.ToString(); path = doc.SelectSingleNode("*/property.EditorPath").InnerText; if (path.EndsWith("/" + Text)) { path = path.Substring(0, path.Length - 1 - Text.Length); } path = r.Replace(path, ""); this.Tag = mData; this.Name = this.DataID; } } public T Data { get { return mData; } } public G2DTreeNode Clone(string newID) { T newData = XmlUtil.CloneObject(mData); G2DTreeNode ret = new G2DTreeNode(newData); ret.SetDataID(newID); return ret; } public void Refresh() { this.Text = mData.ToString(); } public string DataID { get { Type type = mData.GetType(); TableClassAttribute tca = PropertyUtil.GetAttribute(type); FieldInfo fi = mData.GetType().GetField(tca.PrimaryKey); string id = Parser.ObjectToString(fi.GetValue(mData)); return id; } } public void SetDataID(string id) { Type type = mData.GetType(); TableClassAttribute tca = PropertyUtil.GetAttribute(type); FieldInfo fi = mData.GetType().GetField(tca.PrimaryKey); fi.SetValue(mData, Parser.StringToObject(id, fi.FieldType)); this.Text = mData.ToString(); this.Name = id; } public XmlDocument ToXML(Stream output) { Type type = mData.GetType(); XmlDocument doc = XmlUtil.ObjectToXml(mData); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.Encoding = UTF8; using (XmlWriter xml = XmlWriter.Create(output, settings)) { doc.Save(xml); xml.Flush(); } return doc; } public byte[] ToBin(IExternalizableFactory factory) { if (mData is IExternalizable) { using (MemoryStream ms = new MemoryStream(1024 * 1024)) { OutputStream output = new OutputStream(ms, factory); output.PutExt(mData as IExternalizable); ms.Flush(); byte[] bin = new byte[ms.Position]; Array.Copy(ms.GetBuffer(), bin, bin.Length); return bin; } } return null; } } //------------------------------------------------------------------------------------------------------ public class G2DTreeNodeGroup : G2DTreeNodeBase { public G2DTreeNodeGroup(string name) { this.Text = name; this.Name = name; } public bool ContainsNode(TreeNode node) { return GetAllNodes().Contains(node); } public List GetAllNodes() { List ret = new List(); GetAllNodes(this, ret); return ret; } public G2DTreeNodeGroup AddG2DGroup(string gname) { G2DTreeNodeGroup group = new G2DTreeNodeGroup(gname); group.ContextMenuStrip = this.ContextMenuStrip; group.SelectedImageKey = this.SelectedImageKey; group.ImageKey = this.ImageKey; this.Nodes.Add(group); return group; } public string GetTreeInfo() { var doc = new XmlDocument(); var e = doc.CreateElement("node"); doc.AppendChild(e); GetTreeInfo(e, this); return XmlUtil.ToString(doc); } public static void GetTreeInfo(XmlElement e, TreeNode node) { e.SetAttribute("Name", node.Name); e.SetAttribute("IsExpanded", node.IsExpanded.ToString()); foreach (TreeNode sub in node.Nodes) { var ts = sub.GetType(); var xs = e.OwnerDocument.CreateElement("node"); e.AppendChild(xs); GetTreeInfo(xs, sub); } } public void SetTreeInfo(string xmltext) { var doc = XmlUtil.FromString(xmltext); var e = doc.DocumentElement; SetTreeInfo(e, this); } public static void SetTreeInfo(XmlElement e, TreeNode node) { if (e.GetAttribute("Name") == node.Name) { if (e.HasAttribute("IsExpanded") && bool.Parse(e.GetAttribute("IsExpanded"))) { node.Expand(); } foreach (var xs in e.ChildNodes) { if (xs is XmlElement) { var se = xs as XmlElement; var xname = se.GetAttribute("Name"); if (xname != null) { var sub = node.Nodes[xname]; if (sub != null) { SetTreeInfo(se, sub); } } } } } } public string GetSavePath(TreeNode node, bool include_self = false) { if (node == this) { return ""; } if (node.Parent == this) { if (include_self) { return this.Text; } return ""; } string ret = node.Parent.Text; node = node.Parent; while (node != null) { if (node.Parent == this) { break; } ret = node.Parent.Text + "/" + ret; node = node.Parent; } if (include_self) { return this.Text + "/" + ret; } return ret; } public G2DTreeNodeGroup GetOrCreateGroup(string path) { if (!String.IsNullOrEmpty(path)) { string[] paths = path.Split('/'); G2DTreeNodeGroup node = this; foreach (string sub in paths) { G2DTreeNodeGroup tn = FindNodeByText(node, sub, false); if (tn != null) { node = tn; } else { tn = node.AddG2DGroup(sub); node = tn; } } return node; } return this; } } //------------------------------------------------------------------------------------------------------ public class G2DTreeNodeRoot : G2DTreeNodeGroup where T : new() { readonly private static Encoding UTF8 = new UTF8Encoding(false); readonly private string dir; readonly private string setting_dir; private HashMap savedBin = new HashMap(); private HashMap savedMd5 = new HashMap(); private HashMap savedSize = new HashMap(); public ContextMenuStrip ChildsContextMenuStrip; public string ChildsImageKey; public string Dir { get { return dir; } } public string SettingDir { get { return setting_dir; } } public G2DTreeNodeRoot(string name, string dir, string set_dir) : base(name) { this.dir = dir; this.setting_dir = set_dir; } delegate void Delegate0(); public bool AddG2DNode(G2DTreeNode tn, TreeNode parent) { if (ContainsNode(parent) && !ContainsNode(tn) && !ContainsG2DNodeID(tn.DataID)) { tn.ImageKey = ChildsImageKey; tn.SelectedImageKey = ChildsImageKey; tn.ContextMenuStrip = ChildsContextMenuStrip; parent.Nodes.Add(tn); return true; } return false; } private void AddG2DNode(G2DTreeNode tn, string path) { TreeNode parent = GetOrCreateGroup(path); tn.ImageKey = ChildsImageKey; tn.SelectedImageKey = ChildsImageKey; tn.ContextMenuStrip = ChildsContextMenuStrip; parent.Nodes.Add(tn); } public void Invoke(Action action) { if (this.TreeView != null && this.TreeView.InvokeRequired) { this.TreeView.Invoke(new Delegate0(() => { action.Invoke(); })); } else { action.Invoke(); } } public bool SetG2DNodeID(G2DTreeNode tn, string id) { List> ret = GetG2DList(); if (ret.Contains(tn)) { foreach (G2DTreeNode node in ret) { if (node.DataID.Equals(id)) { return false; } } tn.SetDataID(id); return true; } return false; } public bool ContainsG2DNodeID(string id) { List> ret = GetG2DList(); foreach (G2DTreeNode node in ret) { if (node.DataID.Equals(id)) { return true; } } return false; } public List> GetG2DList() { List> ret = new List>(); GetG2DList(this, ret); return ret; } private void GetG2DList(TreeNode node, List> ret) { foreach (TreeNode tn in node.Nodes) { if (tn is G2DTreeNode) { ret.Add(tn as G2DTreeNode); } else if (tn.Nodes.Count > 0) { GetG2DList(tn, ret); } } } public G2DTreeNode FindNode(string id) { List> ret = GetG2DList(); foreach (G2DTreeNode node in ret) { if (node.DataID.Equals(id)) { return node; } } return null; } public void Refresh() { List> ret = GetG2DList(); foreach (G2DTreeNode node in ret) { node.Refresh(); } } protected virtual XmlDocument AtomicSave(G2DTreeNode sub, IExternalizableFactory factory, bool ignore_bin_equals = false) { string xmlpath = dir + "/" + sub.DataID + ".xml"; string binpath = dir + "/" + sub.DataID + ".xml.bin"; byte[] bin = sub.ToBin(factory); if (bin != null) { if (ignore_bin_equals) { byte[] oldbin = savedBin.Get(sub.DataID); if (oldbin != null && CUtils.ArraysEqual(oldbin, bin)) { return null; } } savedBin.Put(sub.DataID, bin); File.WriteAllBytes(binpath, bin); } using (MemoryStream output = new MemoryStream(1024 * 1024)) { XmlDocument doc = sub.ToXML(output); output.Flush(); byte[] xml = output.ToArray(); File.WriteAllBytes(xmlpath, xml); GenMD5(sub.DataID, xml); return doc; } } protected virtual G2DTreeNode AtomicLoad(string file, IExternalizableFactory factory) { byte[] xml = File.ReadAllBytes(file); using (MemoryStream input = new MemoryStream(xml)) { try { G2DTreeNode node = new G2DTreeNode(input); if (File.Exists(file + ".bin")) { try { byte[] bin = IOUtil.ObjectToBin(factory, node.Data as IExternalizable); byte[] oldbin = File.ReadAllBytes(file + ".bin"); if (oldbin != null && CUtils.ArraysEqual(oldbin, bin)) { savedBin.Put(node.DataID, bin); GenMD5(node.DataID, xml); } } catch (Exception er) { er.ToString(); } } return node; } catch (Exception err) { MessageBox.Show(err.Message); } } return null; } protected virtual void GenMD5(string id, byte[] xml) { string md5 = CMD5.CalculateMD5(xml); savedMd5.Put(id, md5); savedSize.Put(id, xml.Length); } public int GetTryLoadCount() { return Directory.GetFiles(dir).Length; } public void LoadState() { try { if (setting_dir != null) { if (File.Exists(setting_dir + "/.tree")) { this.SetTreeInfo(File.ReadAllText(setting_dir + "/.tree", UTF8)); } } } catch (Exception) { } } public void SaveState() { if (setting_dir != null) { File.WriteAllText(setting_dir + "/.tree", this.GetTreeInfo(), UTF8); } } public void LoadAll(IExternalizableFactory factory, AtomicInteger progress) { { foreach (string sub in Directory.GetFiles(dir)) { if (sub.EndsWith(".xml")) { G2DTreeNode tn = AtomicLoad(sub, factory); if (tn != null) { string id = tn.DataID; this.Invoke(() => { AddG2DNode(tn, tn.path); }); } } progress.IncrementAndGet(); } Invoke(() => { LoadState(); }); } } public List> SaveList() { if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } List> ret = GetG2DList(); StringBuilder savelist = new StringBuilder(); foreach (G2DTreeNode sub in ret) { savelist.AppendLine(GetSavePath(sub) + ";" + sub.DataID); } File.WriteAllText(dir + "/.list", savelist.ToString(), UTF8); Invoke(() => { SaveState(); }); return ret; } public FileInfo GetListFile() { return new FileInfo(dir + "/.list"); } public FileInfo GetMd5File() { return new FileInfo(dir + "/.md5"); } public List ListSavedFiles() { List> list = GetG2DList(); List ret = new List(list.Count); foreach (G2DTreeNode sub in list) { string path = dir + "/" + sub.DataID + ".xml"; ret.Add(new FileInfo(path)); } ret.Add(new FileInfo(dir + "/.list")); return ret; } public XmlDocument SaveOne(T data, IExternalizableFactory factory) { if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } List> ret = GetG2DList(); foreach (G2DTreeNode sub in ret) { try { if (sub.Data.Equals(data)) { AtomicSave(sub, factory, false); } } catch (Exception err) { MessageBox.Show(err.Message + "\n" + err.StackTrace); } } return null; } public void SaveAll(IExternalizableFactory factory, AtomicInteger progress) { List> ret = SaveList(); HashMap> savedfiles = new HashMap>(); foreach (G2DTreeNode sub in ret) { try { savedfiles.Add(sub.DataID + ".xml", sub); savedfiles.Add(sub.DataID + ".xml.bin", sub); AtomicSave(sub, factory, true); } catch (Exception err) { MessageBox.Show(err.Message + "\n" + err.StackTrace); } finally { progress.IncrementAndGet(); } } // clean foreach (string sub in Directory.GetFiles(dir)) { string filename = Path.GetFileName(sub); if (filename.EndsWith(".xml") || filename.EndsWith(".xml.bin")) { if (!savedfiles.ContainsKey(filename)) { FileSystem.DeleteToRecycleBin(sub); } } } // save md5 try { StringBuilder sb = new StringBuilder(); foreach (G2DTreeNode sub in ret) { string md5 = savedMd5.Get(sub.DataID); int size = savedSize.Get(sub.DataID); sb.AppendLine(string.Format(string.Format("{0} : {1,12} : {2}", md5, size, sub.DataID + ".xml"))); } File.WriteAllText(dir + "/.md5", sb.ToString(), UTF8); } catch (Exception err) { MessageBox.Show(err.Message + "\n" + err.StackTrace); } } } //------------------------------------------------------------------------------------------------------ public class G2DTreeNodeComparer : IComparer, IComparer { public int Compare(TreeNode x, TreeNode y) { if (x is IDataNode && y is IDataNode) { IDataNode tx = x as IDataNode; IDataNode ty = y as IDataNode; return tx.DataID.CompareTo(ty.DataID); } return x.Text.CompareTo(y.Text); } public int Compare(object x, object y) { return Compare((TreeNode)x, (TreeNode)y); } } //------------------------------------------------------------------------------------------------------ public class G2DTreeViewAdapter { public delegate void TreeNodeAddedHandler(G2DTreeNode node); public delegate void TreeNodeRemovedHandler(G2DTreeNode node); public delegate void TreeNodeRenamedHandler(G2DTreeNode node); public event TreeNodeAddedHandler OnTreeNodeAdded; public event TreeNodeRemovedHandler OnTreeNodeRemoved; public event TreeNodeRenamedHandler OnTreeNodeRenamed; private TreeView treeView; private G2DTreeNodeGroup treeRoot; public G2DTreeViewAdapter( TreeView treeView, G2DTreeNodeGroup root) { this.treeView = treeView; this.treeRoot = root; this.treeView.ItemDrag += new ItemDragEventHandler(treeView1_ItemDrag); this.treeView.DragDrop += new DragEventHandler(treeView1_DragDrop); this.treeView.DragOver += new DragEventHandler(treeView1_DragOver); this.treeView.DragEnter += new DragEventHandler(treeView1_DragEnter); } public G2DTreeNodeGroup GetSelectedGroup() { if (treeView.SelectedNode is G2DTreeNodeGroup) { return treeView.SelectedNode as G2DTreeNodeGroup; } if (treeView.SelectedNode is G2DTreeNode) { return treeView.SelectedNode.Parent as G2DTreeNodeGroup; } return null; } public G2DTreeNode GetSelectedObject() { if (treeView.SelectedNode is G2DTreeNode) { return treeView.SelectedNode as G2DTreeNode; } return null; } public void RemoveSelectedObject() { G2DTreeNode node = GetSelectedObject(); if (node != null) { if (MessageBox.Show("确定要删除: " + node.DataID, "确认", MessageBoxButtons.OKCancel) == DialogResult.OK) { node.Parent.Nodes.Remove(node); if (OnTreeNodeRemoved != null) { OnTreeNodeRemoved.Invoke(node); } } } } public void DuplicateSelectedObject() { G2DTreeNode node = GetSelectedObject(); if (node != null) { G2DTreeNodeGroup parent = node.Parent as G2DTreeNodeGroup; T copy = XmlUtil.CloneObject(node.Data); G2DTreeNode copy_node = new G2DTreeNode(copy); copy_node.ImageKey = node.ImageKey; copy_node.SelectedImageKey = node.SelectedImageKey; copy_node.ContextMenuStrip = node.ContextMenuStrip; string name = copy_node.DataID; while (!string.IsNullOrEmpty(name)) { name = G2DTextDialog.Show(name, "复制 " + node.Name); if (name != null) { if (ContainsObject(name)) { MessageBox.Show("\"" + name + "\" 已存在!"); } else { copy_node.SetDataID(name); parent.Nodes.Add(copy_node); parent.Expand(); treeView.SelectedNode = copy_node; if (OnTreeNodeAdded != null) { OnTreeNodeAdded.Invoke(copy_node); } return; } } else { return; } } } } public void RenameObject() { G2DTreeNode node = GetSelectedObject(); if (node != null) { string name = node.DataID; while (!string.IsNullOrEmpty(name)) { name = G2DTextDialog.Show(name, "重命名 " + name); if (name != null) { if (ContainsObject(name)) { MessageBox.Show("\"" + name + "\" 已存在!"); } else { string src_name = node.DataID; node.SetDataID(name); if (OnTreeNodeRenamed != null) { OnTreeNodeRenamed.Invoke(node); } return; } } } } } public G2DTreeNodeGroup GetTreeRoot() { return treeRoot; } public bool ContainsObject(string name) { TreeNode tn = G2DTreeNodeBase.FindNodeByText>(GetTreeRoot(), name, true); return tn != null; } public void AddGroup() { G2DTreeNodeGroup parent = GetSelectedGroup(); if (parent != null) { string name = G2DTextDialog.Show("分组", "添加过滤器"); if (name != null) { G2DTreeNodeGroup group = parent.AddG2DGroup(name); parent.Expand(); treeView.SelectedNode = group; } } } public G2DTreeNode NewObject(T data) { G2DTreeNodeGroup parent = GetSelectedGroup(); if (parent != null) { string name = data.GetType().Name + GetTreeRoot().GetAllNodes().Count; while (!string.IsNullOrEmpty(name)) { name = G2DTextDialog.Show(name, "添加:" + name); if (name != null) { if (ContainsObject(name)) { MessageBox.Show("\"" + name + "\" 已存在!"); } else { G2DTreeNode node = new G2DTreeNode(data); node.SetDataID(name); parent.Nodes.Add(node); parent.Expand(); treeView.SelectedNode = node; if (OnTreeNodeAdded != null) { OnTreeNodeAdded.Invoke(node); } return node; } } } } return null; } #region Delegate private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) { treeView.DoDragDrop(e.Item, DragDropEffects.Move); } private void treeView1_DragDrop(object sender, DragEventArgs e) { Point pos = treeView.PointToClient(new Point(e.X, e.Y)); TreeNode dropNode = this.treeView.GetNodeAt(pos); G2DTreeNodeBase child_node = (G2DTreeNodeBase)e.Data.GetData(typeof(G2DTreeNode)); G2DTreeNodeBase group_node = (G2DTreeNodeBase)e.Data.GetData(typeof(G2DTreeNodeGroup)); if (dropNode is G2DTreeNodeGroup) { if (child_node == null && group_node == null) { MessageBox.Show("error"); } else if (child_node != null) { child_node.RemoveFromParent(); dropNode.Nodes.Add(child_node); dropNode.Expand(); //treeView1.SelectedNode = child_node; } else if (group_node != dropNode) { if (!G2DTreeNodeBase.ContainsChild(group_node, dropNode, true)) { group_node.RemoveFromParent(); dropNode.Nodes.Add(group_node); dropNode.Expand(); //treeView1.SelectedNode = group_node; } } } } private void treeView1_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(typeof(G2DTreeNode))) { e.Effect = DragDropEffects.Move; } else if (e.Data.GetDataPresent(typeof(G2DTreeNodeGroup))) { e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } private void treeView1_DragOver(object sender, DragEventArgs e) { Point pos = treeView.PointToClient(new Point(e.X, e.Y)); TreeNode dropNode = this.treeView.GetNodeAt(pos); if (dropNode is G2DTreeNodeBase) { e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } #endregion } }