/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Security.Permissions;
using System.Threading;
using System.Windows.Forms;
using System.Collections;
using Aga.Controls.Tree.NodeControls;
using Aga.Controls.Threading;
namespace Aga.Controls.Tree
{
///
/// Extensible advanced implemented in 100% managed C# code.
/// Features: Model/View architecture. Multiple column per node. Ability to select
/// multiple tree nodes. Different types of controls for each node column:
/// , Icon, Label... Drag and Drop highlighting. Load on
/// demand of nodes. Incremental search of nodes.
///
public partial class TreeViewAdv : Control
{
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 bool _suspendUpdate;
private bool _needFullUpdate;
private bool _fireSelectionEvent;
private NodePlusMinus _plusMinus;
private ToolTip _toolTip;
private DrawContext _measureContext;
private TreeColumn _hotColumn;
private IncrementalSearch _search;
private List _expandingNodes = new List();
private AbortableThreadPool _threadPool = new AbortableThreadPool();
public VScrollBar VScrollBar { get { return _vScrollBar; } }
#region Public Events
[Category("Action")]
public event ItemDragEventHandler ItemDrag;
private void OnItemDrag(MouseButtons buttons, object item)
{
if (ItemDrag != null)
ItemDrag(this, new ItemDragEventArgs(buttons, item));
}
[Category("Behavior")]
public event EventHandler NodeMouseClick;
private void OnNodeMouseClick(TreeNodeAdvMouseEventArgs args)
{
if (NodeMouseClick != null)
NodeMouseClick(this, args);
}
[Category("Behavior")]
public event EventHandler NodeMouseDoubleClick;
private void OnNodeMouseDoubleClick(TreeNodeAdvMouseEventArgs args)
{
if (NodeMouseDoubleClick != null)
NodeMouseDoubleClick(this, args);
}
[Category("Behavior")]
public event EventHandler ColumnWidthChanged;
internal void OnColumnWidthChanged(TreeColumn column)
{
if (ColumnWidthChanged != null)
ColumnWidthChanged(this, new TreeColumnEventArgs(column));
}
[Category("Behavior")]
public event EventHandler ColumnReordered;
internal void OnColumnReordered(TreeColumn column)
{
if (ColumnReordered != null)
ColumnReordered(this, new TreeColumnEventArgs(column));
}
[Category("Behavior")]
public event EventHandler ColumnClicked;
internal void OnColumnClicked(TreeColumn column)
{
if (ColumnClicked != null)
ColumnClicked(this, new TreeColumnEventArgs(column));
}
[Category("Behavior")]
public event EventHandler SelectionChanged;
internal void OnSelectionChanged()
{
if (SuspendSelectionEvent)
_fireSelectionEvent = true;
else
{
_fireSelectionEvent = false;
if (SelectionChanged != null)
SelectionChanged(this, EventArgs.Empty);
}
}
[Category("Behavior")]
public event EventHandler Collapsing;
private void OnCollapsing(TreeNodeAdv node)
{
if (Collapsing != null)
Collapsing(this, new TreeViewAdvEventArgs(node));
}
[Category("Behavior")]
public event EventHandler Collapsed;
private void OnCollapsed(TreeNodeAdv node)
{
if (Collapsed != null)
Collapsed(this, new TreeViewAdvEventArgs(node));
}
[Category("Behavior")]
public event EventHandler Expanding;
private void OnExpanding(TreeNodeAdv node)
{
if (Expanding != null)
Expanding(this, new TreeViewAdvEventArgs(node));
}
[Category("Behavior")]
public event EventHandler Expanded;
private void OnExpanded(TreeNodeAdv node)
{
if (Expanded != null)
Expanded(this, new TreeViewAdvEventArgs(node));
}
[Category("Behavior")]
public event EventHandler GridLineStyleChanged;
private void OnGridLineStyleChanged()
{
if (GridLineStyleChanged != null)
GridLineStyleChanged(this, EventArgs.Empty);
}
[Category("Behavior")]
public event ScrollEventHandler Scroll;
protected virtual void OnScroll(ScrollEventArgs e)
{
if (Scroll != null)
Scroll(this, e);
}
[Category("Behavior")]
public event EventHandler RowDraw;
protected virtual void OnRowDraw(PaintEventArgs e, TreeNodeAdv node, DrawContext context, int row, Rectangle rowRect)
{
if (RowDraw != null)
{
TreeViewRowDrawEventArgs args = new TreeViewRowDrawEventArgs(e.Graphics, e.ClipRectangle, node, context, row, rowRect);
RowDraw(this, args);
}
}
///
/// Fires when control is going to draw. Can be used to change text or back color
///
[Category("Behavior")]
public event EventHandler DrawControl;
internal bool DrawControlMustBeFired()
{
return DrawControl != null;
}
internal void FireDrawControl(DrawEventArgs args)
{
OnDrawControl(args);
}
protected virtual void OnDrawControl(DrawEventArgs args)
{
if (DrawControl != null)
DrawControl(this, args);
}
[Category("Drag Drop")]
public event EventHandler DropNodeValidating;
protected virtual void OnDropNodeValidating(Point point, ref TreeNodeAdv node)
{
if (DropNodeValidating != null)
{
DropNodeValidatingEventArgs args = new DropNodeValidatingEventArgs(point, node);
DropNodeValidating(this, args);
node = args.Node;
}
}
#endregion
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();
_selection = new List();
_readonlySelection = new ReadOnlyCollection(_selection);
_columns = new TreeColumnCollection(this);
_toolTip = new ToolTip();
_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;
ExpandingIcon.IconChanged += ExpandingIconChanged;
}
void ExpandingIconChanged(object sender, EventArgs e)
{
if (IsHandleCreated && !IsDisposed)
BeginInvoke(new MethodInvoker(DrawIcons));
}
private void DrawIcons()
{
using (Graphics gr = Graphics.FromHwnd(this.Handle))
{
//Apply the same Graphics Transform logic as used in OnPaint.
int y = 0;
if (UseColumns)
{
y += ColumnHeaderHeight;
if (Columns.Count == 0)
return;
}
int firstRowY = _rowLayout.GetRowBounds(FirstVisibleRow).Y;
y -= firstRowY;
gr.ResetTransform();
gr.TranslateTransform(-OffsetX, y);
DrawContext context = new DrawContext();
context.Graphics = gr;
for (int i = 0; i < _expandingNodes.Count; i++)
{
foreach (NodeControlInfo item in GetNodeControls(_expandingNodes[i]))
{
if (item.Control is ExpandingIcon)
{
Rectangle bounds = item.Bounds;
if (item.Node.Parent == null && UseColumns)
bounds.Location = Point.Empty; // display root expanding icon at 0,0
context.Bounds = bounds;
item.Control.Draw(item.Node, context);
}
}
}
}
}
#region Public Methods
public TreePath GetPath(TreeNodeAdv node)
{
if (node == _root)
return TreePath.Empty;
else
{
Stack