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<T>(
            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<T>(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<TreeNode> GetAllNodes(TreeNode tn)
        {
            List<TreeNode> ret = new List<TreeNode>();
            GetAllNodes(tn, ret);
            return ret;
        }
        public static List<TreeNode> GetAllNodes(TreeNodeCollection tc)
        {
            List<TreeNode> ret = new List<TreeNode>();
            foreach (TreeNode tn in tc)
            {
                GetAllNodes(tn, ret);
            }
            return ret;
        }
        public static void GetAllNodes(TreeNode node, List<TreeNode> ret)
        {
            ret.Add(node);
            foreach (TreeNode tn in node.Nodes)
            {
                GetAllNodes(tn, ret);
            }
        }
    }

    public interface IDataNode
    {
        string DataID { get; }
    }

    public class G2DTreeNode<T> : 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<T> Clone(string newID)
        {
            T newData = XmlUtil.CloneObject<T>(mData);
            G2DTreeNode<T> ret = new G2DTreeNode<T>(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<TableClassAttribute>(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<TableClassAttribute>(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<TreeNode> GetAllNodes()
        {
            List<TreeNode> ret = new List<TreeNode>();
            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<G2DTreeNodeGroup>(node, sub, false);
                    if (tn != null)
                    {
                        node = tn;
                    }
                    else
                    {
                        tn = node.AddG2DGroup(sub);
                        node = tn;
                    }
                }
                return node;
            }
            return this;
        }
    }

    //------------------------------------------------------------------------------------------------------

    public class G2DTreeNodeRoot<T> : G2DTreeNodeGroup where T : new()
    {
        readonly private static Encoding UTF8 = new UTF8Encoding(false);
        readonly private string dir;
        readonly private string setting_dir;
        private HashMap<string, byte[]> savedBin = new HashMap<string, byte[]>();
        private HashMap<string, string> savedMd5 = new HashMap<string, string>();
        private HashMap<string, int> savedSize = new HashMap<string, int>();

        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<T> 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<T> 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<T> tn, string id)
        {
            List<G2DTreeNode<T>> ret = GetG2DList();
            if (ret.Contains(tn))
            {
                foreach (G2DTreeNode<T> node in ret)
                {
                    if (node.DataID.Equals(id))
                    {
                        return false;
                    }
                }
                tn.SetDataID(id);
                return true;
            }
            return false;
        }

        public bool ContainsG2DNodeID(string id)
        {
            List<G2DTreeNode<T>> ret = GetG2DList();
            foreach (G2DTreeNode<T> node in ret)
            {
                if (node.DataID.Equals(id))
                {
                    return true;
                }
            }
            return false;
        }

        public List<G2DTreeNode<T>> GetG2DList()
        {
            List<G2DTreeNode<T>> ret = new List<G2DTreeNode<T>>();
            GetG2DList(this, ret);
            return ret;
        }

        private void GetG2DList(TreeNode node, List<G2DTreeNode<T>> ret)
        {
            foreach (TreeNode tn in node.Nodes)
            {
                if (tn is G2DTreeNode<T>)
                {
                    ret.Add(tn as G2DTreeNode<T>);
                }
                else if (tn.Nodes.Count > 0)
                {
                    GetG2DList(tn, ret);
                }
            }
        }


        public G2DTreeNode<T> FindNode(string id)
        {
            List<G2DTreeNode<T>> ret = GetG2DList();
            foreach (G2DTreeNode<T> node in ret)
            {
                if (node.DataID.Equals(id))
                {
                    return node;
                }
            }
            return null;
        }

        public void Refresh()
        {
            List<G2DTreeNode<T>> ret = GetG2DList();
            foreach (G2DTreeNode<T> node in ret)
            {
                node.Refresh();
            }
        }

        protected virtual XmlDocument AtomicSave(G2DTreeNode<T> 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<T> AtomicLoad(string file, IExternalizableFactory factory)
        {
            byte[] xml = File.ReadAllBytes(file);
            using (MemoryStream input = new MemoryStream(xml))
            {
                try
                {
                    G2DTreeNode<T> node = new G2DTreeNode<T>(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<T> tn = AtomicLoad(sub, factory);
                        if (tn != null)
                        {
                            string id = tn.DataID;
                            this.Invoke(() =>
                            {
                                AddG2DNode(tn, tn.path);
                            });
                        }
                    }
                    progress.IncrementAndGet();
                }
                Invoke(() =>
                {
                    LoadState();
                });
            }
        }

        public List<G2DTreeNode<T>> SaveList()
        {
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            List<G2DTreeNode<T>> ret = GetG2DList();
            StringBuilder savelist = new StringBuilder();
            foreach (G2DTreeNode<T> 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<FileInfo> ListSavedFiles()
        {
            List<G2DTreeNode<T>> list = GetG2DList();
            List<FileInfo> ret = new List<FileInfo>(list.Count);
            foreach (G2DTreeNode<T> 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<G2DTreeNode<T>> ret = GetG2DList();
            foreach (G2DTreeNode<T> 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<G2DTreeNode<T>> ret = SaveList();
            HashMap<string, G2DTreeNode<T>> savedfiles = new HashMap<string, G2DTreeNode<T>>();
            foreach (G2DTreeNode<T> 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<T> 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<TreeNode>, 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<T>
    {
        public delegate void TreeNodeAddedHandler(G2DTreeNode<T> node);
        public delegate void TreeNodeRemovedHandler(G2DTreeNode<T> node);
        public delegate void TreeNodeRenamedHandler(G2DTreeNode<T> 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<T>)
            {
                return treeView.SelectedNode.Parent as G2DTreeNodeGroup;
            }
            return null;
        }

        public G2DTreeNode<T> GetSelectedObject()
        {
            if (treeView.SelectedNode is G2DTreeNode<T>)
            {
                return treeView.SelectedNode as G2DTreeNode<T>;
            }
            return null;
        }

        public void RemoveSelectedObject()
        {
            G2DTreeNode<T> 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<T> node = GetSelectedObject();
            if (node != null)
            {
                G2DTreeNodeGroup parent = node.Parent as G2DTreeNodeGroup;

                T copy = XmlUtil.CloneObject(node.Data);
                G2DTreeNode<T> copy_node = new G2DTreeNode<T>(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<T> 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<G2DTreeNode<T>>(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<T> 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<T> node = new G2DTreeNode<T>(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<T>));
            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<T>)))
            {
                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
    }

}