/* Copyright (c) Cloud Software Group, Inc.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
using System.Collections.Generic;
using System.Windows.Forms;
using System.ComponentModel;
using System;
using System.Drawing;
using System.Diagnostics;
using System.Collections.ObjectModel;
namespace XenAdmin.Controls
{
///
/// A that only adds s when they become visible.
///
public partial class VirtualTreeView : MultiSelectTreeView, IVirtualTreeNodeCollectionOwner, IHaveNodes
{
private readonly VirtualTreeNodeCollection _nodes;
private readonly VirtualTreeSelectedNodeCollection _selectedNodes;
///
/// Initializes a new instance of the class.
///
public VirtualTreeView()
{
_nodes = new VirtualTreeNodeCollection(this);
_selectedNodes = new VirtualTreeSelectedNodeCollection(this);
}
///
/// Gets or sets the tree node that is currently selected in the tree view control.
///
///
///
/// The that is currently selected in the tree view control.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public new VirtualTreeNode SelectedNode
{
get
{
return (VirtualTreeNode)base.SelectedNode;
}
set
{
if (value != null)
{
Devirtualise(value);
}
base.SelectedNode = value;
}
}
///
/// Gets or sets the first fully-visible tree node in the tree view control.
///
///
///
/// A that represents the first fully-visible tree node in the tree view control.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public new VirtualTreeNode TopNode
{
get { return (VirtualTreeNode)base.TopNode; }
set
{
Util.ThrowIfParameterNull(value, "value");
Devirtualise(value);
base.TopNode = value;
}
}
private static List GetParents(VirtualTreeNode node)
{
List parents = new List();
VirtualTreeNode nn = node.Parent;
while (nn != null)
{
parents.Add(nn);
nn = nn.Parent;
}
parents.Reverse();
return parents;
}
///
/// Populates the real nodes for every node from the root down to this node.
///
private void Devirtualise(VirtualTreeNode node)
{
foreach (VirtualTreeNode parent in GetParents(node))
{
Devirtualise(parent.Nodes);
}
}
///
/// Ensures that all real nodes are up-to-date with the virtual ones.
///
private void Devirtualise(VirtualTreeNodeCollection nodes)
{
if (NodeCollectionRequiresUpdate(nodes.Owner))
{
nodes.Owner.RealNodes.Clear();
nodes.Owner.RealNodes.AddRange(new List(nodes).ToArray());
}
}
private static bool NodeCollectionRequiresUpdate(IVirtualTreeNodeCollectionOwner owner)
{
if (owner.RealNodes.Count == 1 && owner.RealNodes[0] is DummyTreeNode)
{
return true;
}
if (owner.RealNodes.Count != owner.Nodes.Count)
{
return true;
}
for (int i = 0; i < owner.Nodes.Count; i++)
{
if (owner.RealNodes[i] != owner.Nodes[i])
{
return true;
}
}
return false;
}
public new VirtualTreeSelectedNodeCollection SelectedNodes
{
get { return _selectedNodes; }
}
#region IVirtualTreeNodeCollectionOwner Members
public new VirtualTreeNodeCollection Nodes
{
get { return _nodes; }
}
MultiSelectTreeNodeCollection IVirtualTreeNodeCollectionOwner.RealNodes
{
get { return base.Nodes; }
}
#endregion
#region IHaveNodes Members
VirtualTreeView.VirtualTreeNodeCollection IHaveNodes.Nodes
{
get { return Nodes; }
}
object IHaveNodes.Tag
{
get { return Tag; }
}
void IHaveNodes.Expand()
{
}
void IHaveNodes.Collapse()
{
}
#endregion
///
/// Retrieves the tree node that is at the specified point.
///
public new VirtualTreeNode GetNodeAt(Point pt)
{
return (VirtualTreeNode)base.GetNodeAt(pt);
}
///
/// Retrieves the tree node that is at the specified point.
///
public new VirtualTreeNode GetNodeAt(int x, int y)
{
return (VirtualTreeNode)base.GetNodeAt(x, y);
}
///
/// Merges the specified node tree with the existing one. Automatically does a BeginUpdate
/// if the nodes change.
///
/// The new root nodes.
public void UpdateRootNodes(IEnumerable newRootNodes)
{
Util.ThrowIfParameterNull(newRootNodes, "newRootNodes");
foreach (VirtualTreeNode node in newRootNodes)
{
if (node.TreeView != null)
{
throw new ArgumentException("newRootNodes should not be attached to a TreeView.", "newRootNodes");
}
}
bool doneBeginUpdate = false;
UpdateNodes(Nodes, new List(newRootNodes), ref doneBeginUpdate);
}
private void UpdateNodes(VirtualTreeNodeCollection oldNodes, IList newNodes, ref bool doneBeginUpdate)
{
for (int i = 0; i < Math.Max(oldNodes.Count, newNodes.Count); )
{
if (oldNodes.Count > i && newNodes.Count > i)
{
if (oldNodes[i].Nodes.Count > 0 && newNodes[i].Nodes.Count == 0 && oldNodes[i].IsExpanded)
{
// fix for issues CA-34486 and CA-36409
// see VirtualTreeViewTests.TestUpdateWhenRemoveAllChildNodesOfExandedParent
// When merging sets of tree-nodes using UpdateRootNodes, if you remove all of
// the child-nodes from a node, the node still remembers its IsExpanded state
// from before the nodes were removed regardless of whether you call Collapse()
// or Expand() after the nodes were removed.
// This causes problems for the Virtual treeview as it
// relies the BeforeExpanded event to convert DummyTreeNodes into VirtualTreeNodes
// on population.
oldNodes[i].Collapse();
}
UpdateNode(newNodes[i], oldNodes[i], ref doneBeginUpdate);
UpdateNodes(oldNodes[i].Nodes, newNodes[i].Nodes, ref doneBeginUpdate);
i++;
}
else if (oldNodes.Count <= i && newNodes.Count > i)
{
LogTreeView("Adding node " + newNodes[i].Text);
DoBeginUpdateIfRequired(ref doneBeginUpdate);
oldNodes.Add(newNodes[i]);
UpdateNodes(oldNodes[i].Nodes, newNodes[i].Nodes, ref doneBeginUpdate);
i++;
}
else
{
LogTreeView("Removing node " + oldNodes[i].Text);
DoBeginUpdateIfRequired(ref doneBeginUpdate);
oldNodes.RemoveAt(i);
}
}
}
private void DoBeginUpdateIfRequired(ref bool doneBeginUpdate)
{
if (!doneBeginUpdate)
{
doneBeginUpdate = true;
BeginUpdate();
}
}
[Conditional("DEBUG")]
private void LogTreeView(string s)
{
//Debug.WriteLine(s);
}
private void UpdateNode(VirtualTreeNode src, VirtualTreeNode dest, ref bool doneBeginUpdate)
{
if (dest.ImageIndex != src.ImageIndex)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.ImageIndex = src.ImageIndex;
}
if (dest.SelectedImageIndex != src.SelectedImageIndex)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.SelectedImageIndex = src.SelectedImageIndex;
}
if (dest.Text != src.Text)
{
LogTreeView("Overwriting " + src.Text + " with " + dest.Text);
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.Text = src.Text;
}
if (dest.Tag != src.Tag)
{
dest.Tag = src.Tag;
}
if (dest.NodeFont != src.NodeFont)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.NodeFont = src.NodeFont;
}
if (dest.BackColor != src.BackColor)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.BackColor = src.BackColor;
}
if (dest.ForeColor != src.ForeColor)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.ForeColor = src.ForeColor;
}
if (dest.Name != src.Name)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.Name = src.Name;
}
if (dest.ShowCheckBox != src.ShowCheckBox)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.ShowCheckBox = src.ShowCheckBox;
}
if (dest.Checked != src.Checked)
{
DoBeginUpdateIfRequired(ref doneBeginUpdate);
dest.Checked = src.Checked;
}
}
///
/// Iterates through every node of this .
///
public new IEnumerable AllNodes
{
get
{
foreach (VirtualTreeNode node in Nodes)
{
yield return node;
foreach (VirtualTreeNode n in node.Descendants)
{
yield return n;
}
}
}
}
#region Virtual overrides
protected sealed override void OnAfterCheck(TreeViewEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnAfterCheck(new VirtualTreeViewEventArgs(node, e.Action));
}
protected sealed override void OnBeforeCheck(TreeViewCancelEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
VirtualTreeViewCancelEventArgs args = new VirtualTreeViewCancelEventArgs(node, e.Cancel, e.Action);
OnBeforeCheck(args);
e.Cancel = args.Cancel;
}
protected sealed override void OnAfterCollapse(TreeViewEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnAfterCollapse(new VirtualTreeViewEventArgs(node, e.Action));
}
protected sealed override void OnBeforeCollapse(TreeViewCancelEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
VirtualTreeViewCancelEventArgs args = new VirtualTreeViewCancelEventArgs(node, e.Cancel, e.Action);
OnBeforeCollapse(args);
e.Cancel = args.Cancel;
}
protected sealed override void OnAfterExpand(TreeViewEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnAfterExpand(new VirtualTreeViewEventArgs(node, e.Action));
}
protected sealed override void OnBeforeExpand(TreeViewCancelEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
Devirtualise(node.Nodes);
VirtualTreeViewCancelEventArgs args = new VirtualTreeViewCancelEventArgs(node, e.Cancel, e.Action);
OnBeforeExpand(args);
e.Cancel = args.Cancel;
}
protected sealed override void OnAfterDeselect(TreeViewEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnAfterDeselect(new VirtualTreeViewEventArgs(node));
}
protected sealed override void OnBeforeDeselect(TreeViewEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnBeforeDeselect(new VirtualTreeViewEventArgs(node));
}
protected sealed override void OnAfterSelect(TreeViewEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnAfterSelect(new VirtualTreeViewEventArgs(node, e.Action));
}
protected sealed override void OnBeforeSelect(MultiSelectTreeViewCancelEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
VirtualTreeViewCancelEventArgs args = new VirtualTreeViewCancelEventArgs(node, e.Cancel, e.Action);
OnBeforeSelect(args);
e.Cancel = args.Cancel;
}
protected sealed override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnNodeMouseClick(new VirtualTreeNodeMouseClickEventArgs(node, e.Button, e.Clicks, e.X, e.Y));
}
protected sealed override void OnNodeMouseDoubleClick(TreeNodeMouseClickEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
OnNodeMouseDoubleClick(new VirtualTreeNodeMouseClickEventArgs(node, e.Button, e.Clicks, e.X, e.Y));
}
protected sealed override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
VirtualNodeLabelEditEventArgs args = new VirtualNodeLabelEditEventArgs(node, e.Label);
OnAfterLabelEdit(args);
e.CancelEdit = args.CancelEdit;
}
protected sealed override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
VirtualTreeNode node = (VirtualTreeNode)e.Node;
VirtualNodeLabelEditEventArgs args = new VirtualNodeLabelEditEventArgs(node, e.Label);
OnBeforeLabelEdit(args);
e.CancelEdit = args.CancelEdit;
}
protected sealed override void OnItemDrag(MultiSelectTreeViewItemDragEventArgs e)
{
List nodes = new List();
foreach (MultiSelectTreeNode n in e.Nodes)
{
nodes.Add((VirtualTreeNode)n);
}
OnItemDrag(new VirtualTreeViewItemDragEventArgs(e.Button, nodes));
}
protected virtual void OnAfterCheck(VirtualTreeViewEventArgs e)
{
EventHandler handler = AfterCheck;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnBeforeCheck(VirtualTreeViewCancelEventArgs e)
{
EventHandler handler = BeforeCheck;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnAfterCollapse(VirtualTreeViewEventArgs e)
{
EventHandler handler = AfterCollapse;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnBeforeCollapse(VirtualTreeViewCancelEventArgs e)
{
EventHandler handler = BeforeCollapse;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnAfterExpand(VirtualTreeViewEventArgs e)
{
EventHandler handler = AfterExpand;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnBeforeExpand(VirtualTreeViewCancelEventArgs e)
{
EventHandler handler = BeforeExpand;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnAfterDeselect(VirtualTreeViewEventArgs e)
{
EventHandler handler = AfterDeselect;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnBeforeDeselect(VirtualTreeViewEventArgs e)
{
EventHandler handler = BeforeDeselect;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnAfterSelect(VirtualTreeViewEventArgs e)
{
EventHandler handler = AfterSelect;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnBeforeSelect(VirtualTreeViewCancelEventArgs e)
{
EventHandler handler = BeforeSelect;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnBeforeLabelEdit(VirtualNodeLabelEditEventArgs e)
{
EventHandler handler = BeforeLabelEdit;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnAfterLabelEdit(VirtualNodeLabelEditEventArgs e)
{
EventHandler handler = AfterLabelEdit;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnNodeMouseClick(VirtualTreeNodeMouseClickEventArgs e)
{
EventHandler handler = NodeMouseClick;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnNodeMouseDoubleClick(VirtualTreeNodeMouseClickEventArgs e)
{
EventHandler handler = NodeMouseDoubleClick;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnItemDrag(VirtualTreeViewItemDragEventArgs e)
{
EventHandler handler = ItemDrag;
if (handler != null)
{
handler(this, e);
}
}
#endregion
#region Events
///
/// Occurs after the tree node check box is checked.
///
[Category("Behavior"), Description("Occurs after the tree node check box is checked.")]
public new event EventHandler AfterCheck;
///
/// Occurs before the tree node check box is checked.
///
[Category("Behavior"), Description("Occurs before the tree node check box is checked.")]
public new event EventHandler BeforeCheck;
///
/// Occurs after the tree node is collapsed.
///
[Category("Behavior"), Description("Occurs after the tree node is collapsed.")]
public new event EventHandler AfterCollapse;
///
/// Occurs before the tree node is collapsed.
///
[Category("Behavior"), Description("Occurs before the tree node is collapsed.")]
public new event EventHandler BeforeCollapse;
///
/// Occurs after the tree node is expanded.
///
[Category("Behavior"), Description("Occurs after the tree node is expanded.")]
public new event EventHandler AfterExpand;
///
/// Occurs before the tree node is expanded.
///
[Category("Behavior"), Description("Occurs after the tree node is expanded.")]
public new event EventHandler BeforeExpand;
///
/// Occurs after the tree node is deselected.
///
[Category("Behavior"), Description("Occurs after the tree node is deselected.")]
public new event EventHandler AfterDeselect;
///
/// Occurs before the tree node is deselected.
///
[Category("Behavior"), Description("Occurs after the tree node is deselected.")]
public new event EventHandler BeforeDeselect;
///
/// Occurs after the tree node is selected.
///
[Category("Behavior"), Description("Occurs after the tree node is selected.")]
public new event EventHandler AfterSelect;
///
/// Occurs before the tree node is selected.
///
[Category("Behavior"), Description("Occurs before the tree node is selected.")]
public new event EventHandler BeforeSelect;
///
/// Occurs after the node is clicked.
///
[Category("Behavior"), Description("Occurs after the node is clicked.")]
public new event EventHandler NodeMouseClick;
///
/// Occurs after the node is double clicked.
///
[Category("Behavior"), Description("Occurs after the node is double clicked.")]
public new event EventHandler NodeMouseDoubleClick;
///
/// Occurs after the tree node label is edited.
///
[Category("Behavior"), Description("Occurs after the tree node label is edited.")]
public new event EventHandler AfterLabelEdit;
///
/// Occurs before the tree node label is edited.
///
[Category("Behavior"), Description("Occurs before the tree node label is edited.")]
public new event EventHandler BeforeLabelEdit;
///
/// Occurs when a node starts being dragged.
///
[Category("Behavior"), Description("Occurs when a node starts being dragged.")]
public new event EventHandler ItemDrag;
#endregion
///
/// Used to ensure that the '+' expander thingy is visible when a node has children.
///
private class DummyTreeNode : MultiSelectTreeNode
{
}
}
internal interface IVirtualTreeNodeCollectionOwner
{
///
/// Gets a complete collection of nodes owned.
///
VirtualTreeView.VirtualTreeNodeCollection Nodes { get;}
///
/// Gets a collection of the nodes which are actually added to the base .
///
MultiSelectTreeNodeCollection RealNodes { get;}
}
internal interface IHaveNodes
{
VirtualTreeView.VirtualTreeNodeCollection Nodes { get; }
object Tag { get; }
void Expand();
void Collapse();
}
#region VirtualTreeView EventArgs classes
public class VirtualTreeViewEventArgs : EventArgs
{
private readonly TreeViewAction _action;
private readonly VirtualTreeNode _node;
public VirtualTreeViewEventArgs(VirtualTreeNode node)
{
Util.ThrowIfParameterNull(node, "node");
_node = node;
}
public VirtualTreeViewEventArgs(VirtualTreeNode node, TreeViewAction action)
: this(node)
{
_action = action;
}
public TreeViewAction Action
{
get
{
return _action;
}
}
public VirtualTreeNode Node
{
get
{
return _node;
}
}
}
public class VirtualTreeViewCancelEventArgs : CancelEventArgs
{
private readonly TreeViewAction _action;
private readonly VirtualTreeNode _node;
public VirtualTreeViewCancelEventArgs(VirtualTreeNode node, bool cancel, TreeViewAction action)
: base(cancel)
{
_node = node;
_action = action;
}
public TreeViewAction Action
{
get
{
return _action;
}
}
public VirtualTreeNode Node
{
get
{
return _node;
}
}
}
public class VirtualTreeNodeMouseClickEventArgs : MouseEventArgs
{
private readonly VirtualTreeNode _node;
public VirtualTreeNodeMouseClickEventArgs(VirtualTreeNode node, MouseButtons button, int clicks, int x, int y)
: base(button , clicks , x , y, 0)
{
_node = node;
}
public VirtualTreeNode Node
{
get
{
return _node;
}
}
}
public class VirtualNodeLabelEditEventArgs : EventArgs
{
private bool _cancelEdit;
private readonly string _label;
private readonly VirtualTreeNode _node;
public VirtualNodeLabelEditEventArgs(VirtualTreeNode node)
{
Util.ThrowIfParameterNull(node, "node");
_node = node;
_label = null;
}
public VirtualNodeLabelEditEventArgs(VirtualTreeNode node, string label)
: this(node)
{
_label = label;
}
public bool CancelEdit
{
get
{
return _cancelEdit;
}
set
{
_cancelEdit = value;
}
}
public string Label
{
get
{
return _label;
}
}
public VirtualTreeNode Node
{
get
{
return _node;
}
}
}
public class VirtualTreeViewItemDragEventArgs : EventArgs
{
private readonly ReadOnlyCollection _nodes;
private readonly MouseButtons _button;
public VirtualTreeViewItemDragEventArgs(MouseButtons button)
: this(button, new VirtualTreeNode[0])
{
}
public VirtualTreeViewItemDragEventArgs(MouseButtons button, IEnumerable nodes)
{
_nodes = new ReadOnlyCollection(new List(nodes));
_button = button;
}
public MouseButtons Button
{
get
{
return _button;
}
}
public ReadOnlyCollection Nodes
{
get
{
return _nodes;
}
}
}
#endregion
}