/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Diagnostics; using JetBrains.Omea.Base; using JetBrains.Omea.Containers; namespace JetBrains.JetListViewLibrary { public enum CollapseState { NoChildren, Expanded, Collapsed }; public enum RequestChildrenReason { Expand, Enumerate }; /// /// Marker interface for a row displayed in JetListView (either a node or a group header). /// public interface IViewNode { } /// /// A single node in JetListView. /// public class JetListViewNode: IViewNode { private JetListViewNodeCollection _owner; private object _data; private JetListViewNode _parent; private ArrayList _children; private int _unacceptedChildCount; private byte _flags; internal JetListViewNode( JetListViewNodeCollection owner, object data ) { _owner = owner; _data = data; SetFiltersAccept( true ); } public object Data { get { return _data; } } public JetListViewNode Parent { get { return _parent; } } public int Level { get { int result = 0; JetListViewNode theParent = _parent; while( theParent.Parent != null ) { result++; theParent = theParent.Parent; } return result; } } internal JetListViewNodeCollection Owner { get { return _owner; } } public bool Expanded { get { return ( _flags & 2 ) != 0; } set { if ( Expanded != value ) { _owner.OnExpandChanging( this ); if ( value ) { RequestChildren( RequestChildrenReason.Expand ); _flags |= 2; } else { _flags &= 0xfd; } _owner.OnExpandChanged( this ); } } } internal void RequestChildren( RequestChildrenReason reason ) { if ( ChildCount == 0 && ( _flags & 8 ) == 0 ) { _flags |= 8; if ( _owner.OnChildrenRequested( this, reason ) ) { _flags &= 0xfb; } else { _flags &= 0xf7; } } } /// /// Gets the node which precedes the current node in the list. /// public JetListViewNode PrevNode { get { int index = _parent._children.IndexOf( this ); if ( index > 0 ) { return (JetListViewNode) _parent._children [index-1]; } return null; } } public JetListViewNode PrevFilteredNode { get { int index = _parent._children.IndexOf( this ); while ( index > 0 ) { index--; JetListViewNode child = (JetListViewNode) _parent._children [index]; if ( child.FiltersAccept ) { return child; } } return null; } } public JetListViewNode NextNode { get { int index = _parent._children.IndexOf( this ); if ( index < _parent._children.Count-1 ) { return (JetListViewNode) _parent._children [index+1]; } return null; } } public JetListViewNode NextFilteredNode { get { int index = _parent._children.IndexOf( this ); while ( index < _parent._children.Count-1 ) { index++; JetListViewNode child = (JetListViewNode) _parent._children [index]; if ( child.FiltersAccept ) { return child; } } return null; } } public bool FiltersAccept { get { return ( _flags & 1 ) != 0; } } internal void SetFiltersAccept( bool value ) { if( value ) { _flags |= 1; } else { _flags &= 0xfe; } } internal void AddChild( JetListViewNode itemNode ) { itemNode._parent = this; if ( _children == null ) { _children = new ArrayList(); } IComparer comparer = _owner.GetNodeComparer( this ); lock( _children ) { if ( comparer == null ) { _children.Add( itemNode ); } else { int index = _children.BinarySearch( itemNode, comparer ); if ( index < 0 ) index = ~index; _children.Insert( index, itemNode ); } } _flags &= 0xfb; if ( !itemNode.FiltersAccept ) { _unacceptedChildCount++; } } internal void RemoveChild( JetListViewNode node ) { if ( _children != null ) { lock( _children ) { int index = _children.IndexOf( node ); if ( index >= 0 ) { _children.RemoveAt( index ); if ( !node.FiltersAccept ) { _unacceptedChildCount--; } } } } } public int IndexOf( JetListViewNode itemNode ) { if ( _children == null ) { return -1; } return _children.IndexOf( itemNode ); } internal void ClearChildren() { _children = null; _unacceptedChildCount = 0; } public ChildNodeCollection Nodes { get { return new ChildNodeCollection( this ); } } internal int ChildCount { get { if ( _children == null ) { return 0; } return _children.Count; } } public bool HasChildren { get { return ( ChildCount > 0 && _unacceptedChildCount < ChildCount) || ( _flags & 4 ) != 0 ; } set { if ( ChildCount == 0 ) { if( value ) { _flags |= 4; } else { _flags &= 0xfb; } if ( value ) { _flags &= 0xf7; } } } } private static ArrayList emptyList = new ArrayList(); internal IEnumerator GetChildEnumerator() { if ( _children == null ) { return emptyList.GetEnumerator(); } return _children.GetEnumerator(); } public CollapseState CollapseState { get { if ( !HasChildren ) return CollapseState.NoChildren; return Expanded ? CollapseState.Expanded : CollapseState.Collapsed; } } public JetListViewNode this [int index ] { get { if ( index < 0 || index >= _children.Count ) { throw new ArgumentOutOfRangeException( "index", index, "Index was out of range (count=" + _children.Count + ")" ); } return (JetListViewNode) _children [index]; } } internal int CountVisibleItems() { int result = 0; if ( _children != null ) { lock( _children ) { foreach( JetListViewNode child in _children ) { if ( child.FiltersAccept ) { result++; if ( child.CollapseState == CollapseState.Expanded ) { result += child.CountVisibleItems(); } } } } } return result; } public JetListViewNode GetLastChild( bool needExpanded ) { if ( ChildCount == 0 ) { return null; } JetListViewNode node = null; for( int i=_children.Count-1; i >= 0; i-- ) { if ( ((JetListViewNode) _children [i]).FiltersAccept ) { node = (JetListViewNode) _children [i]; break; } } if ( node != null && node.ChildCount > 0 ) { if ( needExpanded && node.CollapseState == CollapseState.Collapsed ) { return node; } JetListViewNode nodeChild = node.GetLastChild( needExpanded ); if ( nodeChild != null ) { return nodeChild; } } return node; } internal JetListViewNode GetChildNode( int index ) { return (JetListViewNode) _children [index]; } internal bool IsChildOutOfSortedPosition( JetListViewNode node, IComparer comparer ) { int index = _children.IndexOf( node ); Debug.Assert( index >= 0 ); int prevCmpResult = 0, nextCmpResult = 0; if ( index > 0 ) { prevCmpResult = comparer.Compare( _children [index-1], node ); } if ( index < _children.Count-1 ) { nextCmpResult = comparer.Compare( node, _children [index+1] ); } return prevCmpResult > 0 || nextCmpResult > 0; } internal void UpdateChildPosition( JetListViewNode node, IComparer comparer ) { int index = _children.IndexOf( node ); Debug.Assert( index >= 0 ); _children.RemoveAt( index ); index = _children.BinarySearch( node, comparer ); if ( index < 0 ) { index = ~index; } _children.Insert( index, node ); } internal void MoveChild( JetListViewNode nodeToMove, JetListViewNode afterNode ) { _children.Remove( nodeToMove ); if ( afterNode == null ) { _children.Insert( 0, nodeToMove ); } else { int index = _children.IndexOf( afterNode ); Debug.Assert( index >= 0 ); _children.Insert( index+1, nodeToMove ); } } internal IEnumerator EnumerateChildrenRecursive() { return new NodeEnumerator( this, Level+1 ); } public void ExpandAll() { if ( HasChildren ) { Expanded = true; if ( _children != null ) { lock( _children ) { foreach( JetListViewNode node in _children ) { node.ExpandAll(); } } } } } /// /// Performs stable sort of children using the specified comparer. /// /// The comparer used for sorting. internal void SortChildren( IComparer comparer ) { if ( _children != null && _children.Count > 0 ) { lock( _children ) { RedBlackTree tree = new RedBlackTree( comparer ); for( int i=0; i<_children.Count; i++ ) { tree.RB_Insert( _children [i] ); } RBNodeBase node = tree.GetMinimumNode(); for( int i=0; i<_children.Count; i++ ) { _children [i] = node.Key; node = tree.GetNext( node ); } } } } public void SetParent( JetListViewNode newParent ) { _owner.SetNodeParent( this, newParent ); } public override string ToString() { return "JetListViewNode(Data=" + _data.ToString() + ")"; } public void SetExpandedRecursive( bool expanded ) { if ( HasChildren ) { Expanded = expanded; if ( _children != null ) { foreach( JetListViewNode child in _children ) { child.SetExpandedRecursive( expanded ); } } } } internal void UpdateUnacceptedChildCount() { _unacceptedChildCount = 0; if ( _children != null ) { lock( _children ) { foreach( JetListViewNode child in _children ) { if ( !child.FiltersAccept ) { _unacceptedChildCount++; } } } } } } internal enum MoveDirection { Up, Down }; public class ChildNodeCollection: IEnumerable { private JetListViewNode _baseNode; public ChildNodeCollection( JetListViewNode baseNode ) { _baseNode = baseNode; } public JetListViewNode this [int index] { get { return _baseNode.GetChildNode( index ); } } public int Count { get { return _baseNode.ChildCount; } } public JetListViewNode Add( object data ) { if ( data == null ) { throw new ArgumentNullException( "data" ); } return _baseNode.Owner.Add( data, _baseNode ); } public void Remove( object data ) { Guard.NullArgument( data, "data" ); _baseNode.Owner.Remove( data, _baseNode ); } /// /// Moves the specified node in the collection of children of the specified node. /// /// The node to move. /// The node after which the moved node is inserted, or null if /// the moved node is inserted at the beginning of the list. public void Move( JetListViewNode nodeToMove, JetListViewNode afterNode ) { Guard.NullArgument( nodeToMove, "nodeToMove" ); if ( nodeToMove.Parent != _baseNode ) { throw new InvalidOperationException( "Node to move is a child of a different node" ); } if ( afterNode != null && afterNode.Parent != _baseNode) { throw new InvalidOperationException( "Node to insert after is a child of a different node" ); } _baseNode.Owner.MoveChild( _baseNode, nodeToMove, afterNode ); } public void AddRange( object[] items ) { for( int i=0; i