using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Collections;
using System.Drawing.Design;
using Aga.Controls.Tree.NodeControls;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Threading;
using Aga.Controls.Threading;


namespace Aga.Controls.Tree
{
	public partial class TreeViewAdv : Control
	{
    static public bool isWin8OrAbove; // Added by mikel.

		private const int LeftMargin = 7;
		internal const int ItemDragSensivity = 4;
		private readonly int _columnHeaderHeight;
		private const int DividerWidth = 9;
        private const int DividerCorrectionGap = -2;

		private Pen _linePen;
		private Pen _markPen;
    private Pen _gridPen; // by mikel
		private bool _suspendUpdate;
		private bool _needFullUpdate;
		private bool _fireSelectionEvent;
		private NodePlusMinus _plusMinus;
		private Control _currentEditor;
		private EditableControl _currentEditorOwner;
		private ToolTip _toolTip;
		private System.Windows.Forms.Timer _dragTimer;
		private DrawContext _measureContext;
		private TreeColumn _hotColumn = null;
		private IncrementalSearch _search;
		private List<TreeNodeAdv> _expandingNodes = new List<TreeNodeAdv>();
		private AbortableThreadPool _threadPool = new AbortableThreadPool();

		#region Public Events
    
    // ml: Made all events protected and virtual.

		[Category("Action")]
		public event ItemDragEventHandler ItemDrag;
    protected virtual void OnItemDrag(MouseButtons buttons, object item)
		{
			if (ItemDrag != null)
				ItemDrag(this, new ItemDragEventArgs(buttons, item));
		}

		[Category("Behavior")]
		public event EventHandler<TreeNodeAdvMouseEventArgs> NodeMouseDoubleClick;
    protected virtual void OnNodeMouseDoubleClick(TreeNodeAdvMouseEventArgs args)
		{
			if (NodeMouseDoubleClick != null)
				NodeMouseDoubleClick(this, args);
		}

    // ml: added button for all 3 column events.
    [Category("Behavior")]
		public event EventHandler<TreeColumnEventArgs> ColumnWidthChanged;
		protected internal virtual void OnColumnWidthChanged(TreeColumn column)
		{
			if (ColumnWidthChanged != null)
				ColumnWidthChanged(this, new TreeColumnEventArgs(column, MouseButtons.Left));
		}

		[Category("Behavior")]
		public event EventHandler<TreeColumnEventArgs> ColumnReordered;
		protected internal virtual void OnColumnReordered(TreeColumn column)
		{
			if (ColumnReordered != null)
        ColumnReordered(this, new TreeColumnEventArgs(column, MouseButtons.Left));
		}

		[Category("Behavior")]
		public event EventHandler<TreeColumnEventArgs> ColumnClicked;

		protected internal virtual void OnColumnClicked(TreeColumn column, MouseButtons button)
		{
			if (ColumnClicked != null)
				ColumnClicked(this, new TreeColumnEventArgs(column, button));
		}

		[Category("Behavior")]
		public event EventHandler SelectionChanged;
		protected internal virtual void OnSelectionChanged()
		{
			if (SuspendSelectionEvent)
				_fireSelectionEvent = true;
			else
			{
				_fireSelectionEvent = false;
				if (SelectionChanged != null)
					SelectionChanged(this, EventArgs.Empty);
			}
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Collapsing;
    protected virtual void OnCollapsing(TreeNodeAdv node)
		{
			if (Collapsing != null)
				Collapsing(this, new TreeViewAdvEventArgs(node));
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Collapsed;
    protected virtual void OnCollapsed(TreeNodeAdv node)
		{
			if (Collapsed != null)
				Collapsed(this, new TreeViewAdvEventArgs(node));
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Expanding;
    protected virtual void OnExpanding(TreeNodeAdv node)
		{
			if (Expanding != null)
				Expanding(this, new TreeViewAdvEventArgs(node));
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Expanded;
    protected virtual void OnExpanded(TreeNodeAdv node)
		{
			if (Expanded != null)
				Expanded(this, new TreeViewAdvEventArgs(node));
		}

    [Category("Behavior")]
    public event EventHandler GridLineStyleChanged;
    protected virtual void OnGridLineStyleChanged()
    {
      if (GridLineStyleChanged != null)
        GridLineStyleChanged(this, EventArgs.Empty);
    }

    [Category("Drawing")]
    public event EventHandler<TreeViewAdvDrawRowEventArgs> BeforeDrawNode;
    protected virtual void OnBeforeNodeDrawing(TreeNodeAdv node, DrawContext context)
    {
      if (BeforeDrawNode != null)
        BeforeDrawNode(this, new TreeViewAdvDrawRowEventArgs(node, context));
    }

    public event EventHandler<TreeViewAdvDrawRowEventArgs> AfterDrawNode;
    protected virtual void OnAfterNodeDrawing(TreeNodeAdv node, DrawContext context)
    {
      if (AfterDrawNode != null)
        AfterDrawNode(this, new TreeViewAdvDrawRowEventArgs(node, context));
    }

    #endregion

    static TreeViewAdv() // Added by mikel.
    {
      isWin8OrAbove = false;
      OperatingSystem system = Environment.OSVersion;
      if (system.Platform == PlatformID.Win32NT)
        isWin8OrAbove = system.Version.Major > 6 || (system.Version.Major == 6 && system.Version.Minor >= 2);
    }

		public TreeViewAdv()
		{
			InitializeComponent();
			SetStyle(ControlStyles.AllPaintingInWmPaint
				| ControlStyles.UserPaint
				| ControlStyles.OptimizedDoubleBuffer
				| ControlStyles.ResizeRedraw
				| ControlStyles.Selectable
				, true);


			if (Application.RenderWithVisualStyles)
				_columnHeaderHeight = 20;
			else
				_columnHeaderHeight = 17;

			//BorderStyle = BorderStyle.Fixed3D;
			_hScrollBar.Height = SystemInformation.HorizontalScrollBarHeight;
			_vScrollBar.Width = SystemInformation.VerticalScrollBarWidth;
			_rowLayout = new FixedRowHeightLayout(this, RowHeight);
			_rowMap = new List<TreeNodeAdv>();
			_selection = new List<TreeNodeAdv>();
			_readonlySelection = new ReadOnlyCollection<TreeNodeAdv>(_selection);
			_columns = new TreeColumnCollection(this);
			_toolTip = new ToolTip();
			_dragTimer = new System.Windows.Forms.Timer();
			_dragTimer.Interval = 100;
			_dragTimer.Tick += new EventHandler(DragTimerTick);
			
			_measureContext = new DrawContext();
			_measureContext.Font = Font;
			_measureContext.Graphics = Graphics.FromImage(new Bitmap(1, 1));

			Input = new NormalInputState(this);
			_search = new IncrementalSearch(this);
			CreateNodes();
			CreatePens();

			ArrangeControls();

			_plusMinus = new NodePlusMinus();
			_controls = new NodeControlsCollection(this);

      Font = _font;
			// !!!Modified by mzinner@mysql.com
			//ExpandingIcon.IconChanged += new EventHandler(ExpandingIconChanged);
		}

		void ExpandingIconChanged(object sender, EventArgs e)
		{
			if (IsHandleCreated)
				Invoke(new MethodInvoker(DrawIcons));
		}

		private void DrawIcons()
		{
			Graphics gr = Graphics.FromHwnd(this.Handle);
			int firstRowY = _rowLayout.GetRowBounds(FirstVisibleRow).Y;
			DrawContext context = new DrawContext();
			context.Graphics = gr;
			for (int i = 0; i < _expandingNodes.Count; i++)
			{
				foreach (NodeControlInfo info in GetNodeControls(_expandingNodes[i]))
					if (info.Control is ExpandingIcon)
					{
						Rectangle rect = info.Bounds;
						rect.X -= OffsetX;
						rect.Y -= firstRowY;
						context.Bounds = rect;
						info.Control.Draw(info.Node, context);
					}
			}
			gr.Dispose();
		}

		#region Public Methods

		public TreePath GetPath(TreeNodeAdv node)
		{
			if (node == _root)
				return TreePath.Empty;
			else
			{
				Stack<object> stack = new Stack<object>();
				while (node != _root && node != null)
				{
					stack.Push(node.Tag);
					node = node.Parent;
				}
				return new TreePath(stack.ToArray());
			}
		}

		public TreeNodeAdv GetNodeAt(Point point)
		{
			NodeControlInfo info = GetNodeControlInfoAt(point);
			return info.Node;
		}

		public NodeControlInfo GetNodeControlInfoAt(Point point)
		{
			if (point.X < 0 || point.Y < 0)
				return NodeControlInfo.Empty;

			int row = _rowLayout.GetRowAt(point);
			if (row < RowCount && row >= 0)
				return GetNodeControlInfoAt(RowMap[row], point);
			else
				return NodeControlInfo.Empty;
		}

		private NodeControlInfo GetNodeControlInfoAt(TreeNodeAdv node, Point point)
		{
			Rectangle rect = _rowLayout.GetRowBounds(FirstVisibleRow);
			point.Y += (rect.Y - ColumnHeaderHeight);
			point.X += OffsetX;
			foreach (NodeControlInfo info in GetNodeControls(node))
				if (info.Bounds.Contains(point))
					return info;

			if (FullRowSelect)
				return new NodeControlInfo(null, Rectangle.Empty, node);
			else
				return NodeControlInfo.Empty;
		}

		public void BeginUpdate()
		{
			_suspendUpdate = true;
			SuspendSelectionEvent = true;
		}

		public void EndUpdate()
		{
			_suspendUpdate = false;
			if (_needFullUpdate)
				FullUpdate();
			else
				UpdateView();
			SuspendSelectionEvent = false;
		}

		public void ExpandAll()
		{
			_root.ExpandAll();
		}

		public void CollapseAll()
		{
			_root.CollapseAll();
		}

		/// <summary>
		/// Expand all parent nodes, andd scroll to the specified node
		/// </summary>
		public void EnsureVisible(TreeNodeAdv node)
		{
			if (node == null)
				throw new ArgumentNullException("node");

			if (!IsMyNode(node))
				throw new ArgumentException();

			TreeNodeAdv parent = node.Parent;
			while (parent != _root)
			{
				parent.IsExpanded = true;
				parent = parent.Parent;
			}
			ScrollTo(node);
		}

		/// <summary>
		/// Make node visible, scroll if needed. All parent nodes of the specified node must be expanded
		/// </summary>
		/// <param name="node"></param>
		public void ScrollTo(TreeNodeAdv node)
		{
			if (node == null)
				throw new ArgumentNullException("node");

			if (!IsMyNode(node))
				throw new ArgumentException();

			if (node.Row < 0)
				CreateRowMap();

			int row = -1;
 
			if (node.Row < FirstVisibleRow)
				row = node.Row;
			else
			{
				int pageStart = _rowLayout.GetRowBounds(FirstVisibleRow).Top;
				int rowBottom = _rowLayout.GetRowBounds(node.Row).Bottom;
				if (rowBottom > pageStart + DisplayRectangle.Height - ColumnHeaderHeight)
					row = _rowLayout.GetFirstRow(node.Row);
			}

			if (row >= _vScrollBar.Minimum && row <= _vScrollBar.Maximum)
				_vScrollBar.Value = row;
		}

		public void ClearSelection()
		{
			BeginUpdate();
			try
			{
				ClearSelectionInternal();
			}
			finally
			{
				EndUpdate();
			}
		}

		internal void ClearSelectionInternal()
		{
			while (Selection.Count > 0)
				Selection[0].IsSelected = false;
		}

		#endregion

		protected override void OnSizeChanged(EventArgs e)
		{
			ArrangeControls();
			SafeUpdateScrollBars();
			base.OnSizeChanged(e);
		}

		private void ArrangeControls()
		{
			int hBarSize = _hScrollBar.Height;
			int vBarSize = _vScrollBar.Width;
			Rectangle clientRect = ClientRectangle;
			
			_hScrollBar.SetBounds(clientRect.X, clientRect.Bottom - hBarSize,
				clientRect.Width - vBarSize, hBarSize);

			_vScrollBar.SetBounds(clientRect.Right - vBarSize, clientRect.Y,
				vBarSize, clientRect.Height - hBarSize);
		}

		private void SafeUpdateScrollBars()
		{
      if (!IsHandleCreated)
        return;

			if (InvokeRequired)
				Invoke(new MethodInvoker(UpdateScrollBars));
			else
				UpdateScrollBars();
		}

		private void UpdateScrollBars()
		{
			UpdateVScrollBar();
			UpdateHScrollBar();
			UpdateVScrollBar();
			UpdateHScrollBar();
			_hScrollBar.Width = DisplayRectangle.Width;
			_vScrollBar.Height = DisplayRectangle.Height;
		}

		private void UpdateHScrollBar()
		{
			_hScrollBar.Maximum = ContentWidth;
			_hScrollBar.LargeChange = Math.Max(DisplayRectangle.Width, 0);
			_hScrollBar.SmallChange = 5;
			_hScrollBar.Visible = _hScrollBar.LargeChange < _hScrollBar.Maximum;
			_hScrollBar.Value = Math.Min(_hScrollBar.Value, _hScrollBar.Maximum - _hScrollBar.LargeChange + 1);
		}

		private void UpdateVScrollBar()
		{
			_vScrollBar.Maximum = Math.Max(RowCount - 1, 0);
			_vScrollBar.LargeChange = _rowLayout.PageRowCount;
			_vScrollBar.Visible = (RowCount > 0) && (_vScrollBar.LargeChange <= _vScrollBar.Maximum);
			_vScrollBar.Value = Math.Min(_vScrollBar.Value, _vScrollBar.Maximum - _vScrollBar.LargeChange + 1);
		}

		protected override CreateParams CreateParams
		{
			[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
			get
			{
				CreateParams res = base.CreateParams;
				switch (BorderStyle)
				{
					case BorderStyle.FixedSingle:
							res.Style |= 0x800000;
							break;
					case BorderStyle.Fixed3D:
							res.ExStyle |= 0x200;
						break;
				}
				return res;
			}
		}

		protected override void OnGotFocus(EventArgs e)
		{
			HideEditor();
			UpdateView();
			ChangeInput();
			base.OnGotFocus(e);
		}

		protected override void OnLeave(EventArgs e)
		{
			if (_currentEditorOwner != null)
				_currentEditorOwner.ApplyChanges();
			HideEditor();
			UpdateView();
			base.OnLeave(e);
		}

		protected override void OnFontChanged(EventArgs e)
		{
			base.OnFontChanged(e);
			_measureContext.Font = Font;
			FullUpdate();
		}

		internal IEnumerable<NodeControlInfo> GetNodeControls(TreeNodeAdv node)
		{
			if (node == null)
				yield break;
			Rectangle rowRect = _rowLayout.GetRowBounds(node.Row);
			foreach (NodeControlInfo n in GetNodeControls(node, rowRect))
				yield return n;
		}

		internal IEnumerable<NodeControlInfo> GetNodeControls(TreeNodeAdv node, Rectangle rowRect)
		{
			if (node == null)
				yield break;

			int y = rowRect.Y;
			int x = (node.Level - 1) * _indent + LeftMargin;
			int width = 0;
			Rectangle rect = Rectangle.Empty;

			if (ShowPlusMinus)
			{
				width = _plusMinus.GetActualSize(node, _measureContext).Width;
				rect = new Rectangle(x, y, width, rowRect.Height);
				if (UseColumns && Columns.Count > 0 && Columns[0].Width < rect.Right)
					rect.Width = Columns[0].Width - x;

				yield return new NodeControlInfo(_plusMinus, rect, node);
				x += width;
			}

			if (!UseColumns)
			{
				foreach (NodeControl c in NodeControls)
				{
					Size s = c.GetActualSize(node, _measureContext);
					if (!s.IsEmpty)
					{
						width = s.Width;
						rect = new Rectangle(x, y, width, rowRect.Height);
						x += rect.Width;
						yield return new NodeControlInfo(c, rect, node);
					}
				}
			}
			else
			{
				int right = 0;
				foreach (TreeColumn col in Columns)
				{
					if (col.IsVisible && col.Width > 0)
					{
						right += col.Width;
						for (int i = 0; i < NodeControls.Count; i++)
						{
							NodeControl nc = NodeControls[i];
							if (nc.ParentColumn == col)
							{
								Size s = nc.GetActualSize(node, _measureContext);
								if (!s.IsEmpty)
								{
									bool isLastControl = true;
									for (int k = i + 1; k < NodeControls.Count; k++)
										if (NodeControls[k].ParentColumn == col)
										{
											isLastControl = false;
											break;
										}

									width = right - x;
									if (!isLastControl)
										width = s.Width;
									int maxWidth = Math.Max(0, right - x);
									rect = new Rectangle(x, y, Math.Min(maxWidth, width), rowRect.Height);
									x += width;
									yield return new NodeControlInfo(nc, rect, node);
								}
							}
						}
						x = right;
					}
				}
			}
		}

		internal static double Dist(Point p1, Point p2)
		{
			return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
		}

		public void FullUpdate()
		{
      // ml: gets called from the background thread at times when the tree is already gone mostly.
      if (IsDisposed || Disposing)
        return;

      if (IsHandleCreated && InvokeRequired)
        Invoke(new MethodInvoker(UnsafeFullUpdate));
      else
        UnsafeFullUpdate();
		}

		private void UnsafeFullUpdate()
		{
			_rowLayout.ClearCache();
			CreateRowMap();
			SafeUpdateScrollBars();
			UpdateView();
			_needFullUpdate = false;
		}

		internal void UpdateView()
		{
			if (!_suspendUpdate)
				Invalidate(false);
		}

		internal void UpdateHeaders()
		{
			Invalidate(new Rectangle(0,0, Width, ColumnHeaderHeight));
		}

		internal void UpdateColumns()
		{
			FullUpdate();
		}

		private void CreateNodes()
		{
			Selection.Clear();
			SelectionStart = null;
			_root = new TreeNodeAdv(this, null);
			_root.IsExpanded = true;
			if (_root.Nodes.Count > 0)
				CurrentNode = _root.Nodes[0];
			else
				CurrentNode = null;
		}

		internal void ReadChilds(TreeNodeAdv parentNode)
		{
			ReadChilds(parentNode, false);
		}

		internal void ReadChilds(TreeNodeAdv parentNode, bool performFullUpdate)
		{
			if (!parentNode.IsLeaf)
			{
				parentNode.IsExpandedOnce = true;
				List<TreeNodeAdv> oldNodes = new List<TreeNodeAdv>(parentNode.Nodes);
				parentNode.Nodes.Clear();

				if (Model != null)
				{
					IEnumerable items = Model.GetChildren(GetPath(parentNode));
					if (items != null)
						foreach (object obj in items)
						{
							bool found = false;
							if (obj != null)
							{
								for (int i = 0; i < oldNodes.Count; i++)
                  // ml: what a nonsense! Identifying a node by its text? There are nodes having no text at all...
									if (obj.ToString() == oldNodes[i].Tag.ToString())
									{
										oldNodes[i].RightBounds = oldNodes[i].Height = null;
                    oldNodes[i].Tag = obj;
										AddNode(parentNode, -1, oldNodes[i]);
										oldNodes.RemoveAt(i);
										found = true;
										break;
									}
							}
							if (!found)
								AddNewNode(parentNode, obj, -1);
						}
          if (performFullUpdate)
            FullUpdate();
        }

			}
		}

		private void AddNewNode(TreeNodeAdv parent, object tag, int index)
		{
			TreeNodeAdv node = new TreeNodeAdv(this, tag);
			AddNode(parent, index, node);
		}

		private void AddNode(TreeNodeAdv parent, int index, TreeNodeAdv node)
		{
			if (index >= 0 && index < parent.Nodes.Count)
				parent.Nodes.Insert(index, node);
			else
				parent.Nodes.Add(node);

			node.IsLeaf = Model.IsLeaf(GetPath(node));
			if (node.IsLeaf)
				node.Nodes.Clear();
			if (!LoadOnDemand || node.IsExpandedOnce)
				ReadChilds(node);
		}

		private struct ExpandArgs
		{
			public TreeNodeAdv Node;
			public bool Value;
			public bool IgnoreChildren;
		}

		public void AbortBackgroundExpandingThreads()
		{
			_threadPool.CancelAll(true);
			for (int i = 0; i < _expandingNodes.Count; i++)
				_expandingNodes[i].IsExpandingNow = false;
			_expandingNodes.Clear();
			Invalidate();
		}

		internal void SetIsExpanded(TreeNodeAdv node, bool value, bool ignoreChildren)
		{
			ExpandArgs eargs = new ExpandArgs();
			eargs.Node = node;
			eargs.Value = value;
			eargs.IgnoreChildren = ignoreChildren;

			if (AsyncExpanding && LoadOnDemand && !_threadPool.IsMyThread(Thread.CurrentThread))
			{
				WaitCallback wc = delegate(object argument) { SetIsExpanded((ExpandArgs)argument); };
				_threadPool.QueueUserWorkItem(wc, eargs);
			}
			else
				SetIsExpanded(eargs);
		}

		private void SetIsExpanded(ExpandArgs eargs)
		{
			bool update = !eargs.IgnoreChildren && !AsyncExpanding;
			if (update)
				BeginUpdate();
			try
			{
				if (IsMyNode(eargs.Node) && eargs.Node.IsExpanded != eargs.Value)
					SetIsExpanded(eargs.Node, eargs.Value);

				if (!eargs.IgnoreChildren)
					SetIsExpandedRecursive(eargs.Node, eargs.Value);
			}
			finally
			{
				if (update)
					EndUpdate();
			}
		}

		internal void SetIsExpanded(TreeNodeAdv node, bool value)
		{
			if (Root == node && !value)
				return; //Can't collapse root node

			if (value)
				OnExpanding(node);
			else
				OnCollapsing(node);

			if (value && !node.IsExpandedOnce)
			{
				if (AsyncExpanding && LoadOnDemand)
				{
					AddExpandingNode(node);
					node.AssignIsExpanded(true);
					Invalidate();
				}
				ReadChilds(node, AsyncExpanding);
				RemoveExpandingNode(node);
			}
			node.AssignIsExpanded(value);
			SmartFullUpdate();

			if (value)
				OnExpanded(node);
			else
				OnCollapsed(node);
		}

		private void RemoveExpandingNode(TreeNodeAdv node)
		{
			node.IsExpandingNow = false;
			_expandingNodes.Remove(node);
		}

		private void AddExpandingNode(TreeNodeAdv node)
		{
			node.IsExpandingNow = true;
			_expandingNodes.Add(node);
			ExpandingIcon.Start();
		}

		internal void SetIsExpandedRecursive(TreeNodeAdv root, bool value)
		{
			for (int i = 0; i < root.Nodes.Count; i++)
			{
				TreeNodeAdv node = root.Nodes[i];
				node.IsExpanded = value;
				SetIsExpandedRecursive(node, value);
			}
		}

		private void CreateRowMap()
		{
			RowMap.Clear();
			int row = 0;
			_contentWidth = 0;
			foreach (TreeNodeAdv node in VisibleNodes)
			{
				node.Row = row;
				RowMap.Add(node);
				if (!UseColumns)
				{
					_contentWidth = Math.Max(_contentWidth, GetNodeWidth(node));
				}
				row++;
			}
			if (UseColumns)
			{
				_contentWidth = 0;
				foreach (TreeColumn col in _columns)
					if (col.IsVisible)
						_contentWidth += col.Width;
			}
		}

		private int GetNodeWidth(TreeNodeAdv node)
		{
			if (node.RightBounds == null)
			{
				Rectangle res = GetNodeBounds(GetNodeControls(node, Rectangle.Empty));
				node.RightBounds = res.Right;
			}
			return node.RightBounds.Value;
		}

    /// <summary>
    /// ml: added for hit tests.
    /// Returns a node's bounds in client coordinates, taking the current scroll position into account.
    /// </summary>
    public Rectangle GetRealNodeBounds(TreeNodeAdv node)
    {
      Rectangle bounds = GetNodeBounds(node);
      Point p = ScrollPosition;
      int colHeaderY = node.Tree.UseColumns ? node.Tree.ColumnHeaderHeight : 0;
      bounds.Offset(-p.X, -p.Y * RowHeight + colHeaderY);
      return bounds;
    }

		protected Rectangle GetNodeBounds(TreeNodeAdv node)
		{
			return GetNodeBounds(GetNodeControls(node));
		}

		internal Rectangle GetNodeBounds(IEnumerable<NodeControlInfo> nodeControls)
		{
			Rectangle res = Rectangle.Empty;
			foreach (NodeControlInfo info in nodeControls)
			{
				if (res == Rectangle.Empty)
					res = info.Bounds;
				else
					res = Rectangle.Union(res, info.Bounds);
			}
			return res;
		}

		private void _vScrollBar_ValueChanged(object sender, EventArgs e)
		{
			FirstVisibleRow = _vScrollBar.Value;
		}

		private void _hScrollBar_ValueChanged(object sender, EventArgs e)
		{
			OffsetX = _hScrollBar.Value;
		}

		internal void SmartFullUpdate()
		{
			if (_suspendUpdate)
				_needFullUpdate = true;
			else
				FullUpdate();
		}

		internal bool IsMyNode(TreeNodeAdv node)
		{
			if (node == null)
				return false;

			if (node.Tree != this)
				return false;

			while (node.Parent != null)
				node = node.Parent;

			return node == _root;
		}

		private void UpdateSelection()
		{
			bool flag = false;

			if (!IsMyNode(CurrentNode))
				CurrentNode = null;
			if (!IsMyNode(_selectionStart))
				_selectionStart = null;

			for (int i = Selection.Count - 1; i >= 0; i--)
				if (!IsMyNode(Selection[i]))
				{
					flag = true;
					Selection.RemoveAt(i);
				}

			if (flag)
				OnSelectionChanged();
		}

		internal void ChangeColumnWidth(TreeColumn column)
		{
			if (!(_input is ResizeColumnState))
			{
				FullUpdate();
				OnColumnWidthChanged(column);
			}
		}

		public TreeNodeAdv FindNode(TreePath path)
		{
			return FindNode(path, false);
		}

		public TreeNodeAdv FindNode(TreePath path, bool readChilds)
		{
			if (path.IsEmpty())
				return _root;
			else
				return FindNode(_root, path, 0, readChilds);
		}

		private TreeNodeAdv FindNode(TreeNodeAdv root, TreePath path, int level, bool readChilds)
		{
			if (!root.IsExpandedOnce && readChilds)
				ReadChilds(root);

			for (int i = 0; i < root.Nodes.Count; i++)
			{
				TreeNodeAdv node = root.Nodes[i];
				if (node.Tag == path.FullPath[level])
				{
					if (level == path.FullPath.Length - 1)
						return node;
					else
						return FindNode(node, path, level + 1, readChilds);
				}
			}
			return null;
		}

		public TreeNodeAdv FindNodeByTag(object tag)
		{
			return FindNodeByTag(_root, tag);
		}

		private TreeNodeAdv FindNodeByTag(TreeNodeAdv root, object tag)
		{
			foreach (TreeNodeAdv node in root.Nodes)
			{
				if (node.Tag == tag)
					return node;
				TreeNodeAdv res = FindNodeByTag(node, tag);
				if (res != null)
					return res;
			}
			return null;
		}

		#region Editor

		public void DisplayEditor(Control control, EditableControl owner)
		{
			if (control == null || owner == null)
				throw new ArgumentNullException();

			if (CurrentNode != null)
			{
				HideEditor();
				_currentEditor = control;
				_currentEditorOwner = owner;
				UpdateEditorBounds();

				UpdateView();
				control.Parent = this;
				control.Focus();
				owner.UpdateEditor(control);
			}
		}

		public void UpdateEditorBounds()
		{
			if (_currentEditor != null)
			{
				EditorContext context = new EditorContext();
				context.Owner = _currentEditorOwner;
				context.CurrentNode = CurrentNode;
				context.Editor = _currentEditor;
				context.DrawContext = _measureContext;

				SetEditorBounds(context);
			}
		}

		public void HideEditor()
		{
			if (_currentEditorOwner != null)
			{
				_currentEditorOwner.HideEditor(_currentEditor);
				_currentEditor = null;
				_currentEditorOwner = null;
			}
		}

		private void SetEditorBounds(EditorContext context)
		{
			foreach (NodeControlInfo info in GetNodeControls(context.CurrentNode))
			{
				if (context.Owner == info.Control && info.Control is EditableControl)
				{
					Point p = info.Bounds.Location;
					p.X += info.Control.LeftMargin;
					p.X -= OffsetX;
					p.Y -= (_rowLayout.GetRowBounds(FirstVisibleRow).Y - ColumnHeaderHeight);
					int width = DisplayRectangle.Width - p.X;
					if (UseColumns && info.Control.ParentColumn != null && Columns.Contains(info.Control.ParentColumn))
					{
						Rectangle rect = GetColumnBounds(info.Control.ParentColumn.Index);
						width = rect.Right - OffsetX - p.X;
					}
					context.Bounds = new Rectangle(p.X, p.Y, width, info.Bounds.Height);
					((EditableControl)info.Control).SetEditorBounds(context);
					return;
				}
			}
		}

		private Rectangle GetColumnBounds(int column)
		{
			int x = 0;
			for (int i = 0; i < Columns.Count; i++)
			{
				if (Columns[i].IsVisible)
				{
					if (i < column)
						x += Columns[i].Width;
					else
						return new Rectangle(x, 0, Columns[i].Width, 0);
				}
			}
			return Rectangle.Empty;
		}

		#endregion

		#region ModelEvents
		private void BindModelEvents()
		{
			_model.NodesChanged += new EventHandler<TreeModelEventArgs>(_model_NodesChanged);
			_model.NodesInserted += new EventHandler<TreeModelEventArgs>(_model_NodesInserted);
			_model.NodesRemoved += new EventHandler<TreeModelEventArgs>(_model_NodesRemoved);
			_model.StructureChanged += new EventHandler<TreePathEventArgs>(_model_StructureChanged);
		}

		private void UnbindModelEvents()
		{
			_model.NodesChanged -= new EventHandler<TreeModelEventArgs>(_model_NodesChanged);
			_model.NodesInserted -= new EventHandler<TreeModelEventArgs>(_model_NodesInserted);
			_model.NodesRemoved -= new EventHandler<TreeModelEventArgs>(_model_NodesRemoved);
			_model.StructureChanged -= new EventHandler<TreePathEventArgs>(_model_StructureChanged);
		}

		private void _model_StructureChanged(object sender, TreePathEventArgs e)
		{
			if (e.Path == null)
				throw new ArgumentNullException();

			TreeNodeAdv node = FindNode(e.Path);
			if (node != null)
			{
				ReadChilds(node);
				UpdateSelection();
				SmartFullUpdate();
			}
			//else 
			//	throw new ArgumentException("Path not found");
		}

		private void _model_NodesRemoved(object sender, TreeModelEventArgs e)
		{
			TreeNodeAdv parent = FindNode(e.Path);
			if (parent != null)
			{
				if (e.Indices != null)
				{
					List<int> list = new List<int>(e.Indices);
					list.Sort();
					for (int n = list.Count - 1; n >= 0; n--)
					{
						int index = list[n];
						if (index >= 0 && index < parent.Nodes.Count) // ml: bug fix
							parent.Nodes.RemoveAt(index);
						else
							throw new ArgumentOutOfRangeException("Index out of range");
					}
				}
				else
				{
					for (int i = parent.Nodes.Count - 1; i >= 0; i--)
					{
						for (int n = 0; n < e.Children.Length; n++)
							if (parent.Nodes[i].Tag == e.Children[n])
							{
								parent.Nodes.RemoveAt(i);
								break;
							}
					}
				}
			}
			UpdateSelection();
			SmartFullUpdate();
		}

		private void _model_NodesInserted(object sender, TreeModelEventArgs e)
		{
			if (e.Indices == null)
				throw new ArgumentNullException("Indices");

			TreeNodeAdv parent = FindNode(e.Path);
			if (parent != null)
			{
				for (int i = 0; i < e.Children.Length; i++)
					AddNewNode(parent, e.Children[i], e.Indices[i]);
			}
			SmartFullUpdate();
		}

		private void _model_NodesChanged(object sender, TreeModelEventArgs e)
		{
			TreeNodeAdv parent = FindNode(e.Path);
			if (parent != null && parent.IsVisible  && parent.IsExpanded)
			{
				if (InvokeRequired)
					Invoke(new UpdateContentWidthDelegate(ClearNodesSize), e, parent);
				else
					ClearNodesSize(e, parent);
				SmartFullUpdate();
			}
		}

		private delegate void UpdateContentWidthDelegate(TreeModelEventArgs e, TreeNodeAdv parent);
		private void ClearNodesSize(TreeModelEventArgs e, TreeNodeAdv parent)
		{
			if (e.Indices != null)
			{
				foreach (int index in e.Indices)
				{
					if (index >= 0 && index < parent.Nodes.Count)
					{
						TreeNodeAdv node = parent.Nodes[index];
						node.Height = node.RightBounds = null;
					}
					else
						throw new ArgumentOutOfRangeException("Index out of range");
				}
			}
			else
			{
				foreach (TreeNodeAdv node in parent.Nodes)
				{
					foreach (object obj in e.Children)
						if (node.Tag == obj)
						{
							node.Height = node.RightBounds = null;
						}
				}
			}
		}
		#endregion
	}
}