///
/// 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.Threading;
using JetBrains.DataStructures;
using JetBrains.Omea.Base;
namespace JetBrains.JetListViewLibrary
{
public interface INodeCollection
{
void SetItemComparer( object parentItem, IComparer comparer );
event JetListViewNodeEventHandler NodeAdded;
event JetListViewNodeEventHandler NodeRemoving;
event JetListViewNodeEventHandler NodeRemoved;
event JetListViewNodeEventHandler NodeExpandChanged;
///
/// Occurs before a node is expanded or collapsed.
///
event JetListViewNodeEventHandler NodeExpandChanging;
///
/// Occurs when the children of the specified node are requested, either during
/// enumeration or when the user actually expands the node to see them.
///
event RequestChildrenEventHandler ChildrenRequested;
bool Contains( object item );
///
/// Returns true if the collection does not contain any nodes.
///
bool IsEmpty { get; }
///
/// Returns a node which contains the specified data item.
///
/// The data item to find the node for.
/// The tree node, or null if no such node is found.
/// If the specified data item is present in multiple places of the tree,
/// it is not defined which of the occurences is returned.
JetListViewNode NodeFromItem( object item );
///
/// Returns the child node of the specified node which contains the specified data item.
///
/// The parent node, or null if the top-level node should be returned.
/// The data item to find the node for.
/// The tree node, or null if no such node is found.
JetListViewNode NodeFromItem( JetListViewNode parent, object item );
///
/// Returns the array of all nodes which contain the specified data item.
///
/// The data item to find the nodes for.
/// The array of nodes, or an empty array if the item was not found.
JetListViewNode[] NodesFromItem( object item );
IEnumerator EnumerateNodesForward( JetListViewNode startNode );
IEnumerator EnumerateNodesBackward( JetListViewNode startNode );
///
/// Returns the total number of nodes which have not been collapsed or filtered away.
///
int VisibleItemCount { get; }
///
/// Returns the topmost node visible in the list.
///
JetListViewNode FirstVisibleNode { get; }
///
/// Performs recursive stable sort of all nodes in the collection according to the
/// comparer specified for each node.
///
void Sort();
///
/// Begins a batch update of the node collection.
///
void BeginUpdate();
///
/// Ends the batch update of the node collection.
///
void EndUpdate();
}
internal interface IVisibleNodeEnumerator: IEnumerator
{
IViewNode CurrentNode { get; }
}
internal class EmptyVisibleNodeEnumerator: IVisibleNodeEnumerator
{
public IViewNode CurrentNode
{
get { return null; }
}
public bool MoveNext()
{
return false;
}
public void Reset()
{
}
public object Current
{
get { return null; }
}
}
internal interface IVisibleNodeCollection
{
IVisibleNodeEnumerator GetFullEnumerator();
MoveDirection GetMoveDirection( IViewNode startNode, IViewNode endNode );
IVisibleNodeEnumerator GetDirectionalEnumerator( IViewNode startNode, MoveDirection direction );
IViewNode GetVisibleParent( IViewNode node );
bool IsNodeVisible( IViewNode node );
///
/// Returns the total number of nodes currently visible.
///
int VisibleNodeCount { get; }
///
/// Returns the first node visible in the view.
///
IViewNode FirstVisibleViewNode { get; }
///
/// Returns the last node visible in the view.
///
IViewNode LastVisibleViewNode { get; }
///
/// Ensures that the specified node is visible (expands its parents and the group in which
/// it may be contained).
///
/// The node to show.
void EnsureNodeVisible( IViewNode node );
event EventHandler NodesCollapsed;
event ViewNodeEventHandler ViewNodeRemoving;
}
///
/// Manages the structure of items visible in a JetListView.
///
internal class JetListViewNodeCollection: INodeCollection, IVisibleNodeCollection
{
private JetListViewNode _rootNode;
private JetListViewFilterCollection _filters;
private JetListViewNodeMap _nodeMap = new JetListViewNodeMap();
private HashMap _comparerMap = new HashMap();
private VisibleItemsEnumerable _visibleItemsEnumerable;
private int _batchUpdateCount = 0;
private bool _havePendingUpdates = false;
private JetListViewNode _lastUpdatedNode;
private bool _flatList = true;
private int _version = 0;
private HashSet _addedNodes = null;
private HashSet _removedNodes = null;
private HashSet _changedNodes = null;
private bool _fullUpdate = false;
private bool filtersAccept;
internal JetListViewNodeCollection( JetListViewFilterCollection filters )
{
_rootNode = new JetListViewNode( this, null );
_rootNode.Expanded = true;
_visibleItemsEnumerable = new VisibleItemsEnumerable( _rootNode );
_filters = filters;
if ( _filters != null )
{
_filters.FilterListChanged += new EventHandler( HandleFilterListChanged );
}
}
public event JetListViewNodeEventHandler NodeAdded;
public event JetListViewNodeEventHandler NodeRemoving;
public event JetListViewNodeEventHandler NodeRemoved;
public event JetListViewNodeEventHandler VisibleNodeAdded;
public event JetListViewNodeEventHandler VisibleNodeRemoving;
public event JetListViewNodeEventHandler VisibleNodeRemoved;
public event JetListViewNodeEventHandler NodeMoving;
public event JetListViewNodeEventHandler NodeMoved;
public event MultipleNodesChangedEventHandler MultipleNodesChanged;
public event JetListViewNodeEventHandler NodeExpandChanging;
public event JetListViewNodeEventHandler NodeExpandChanged;
public event JetListViewNodeEventHandler NodeChanged;
public event RequestChildrenEventHandler ChildrenRequested;
public event EventHandler Sorted;
public event EventHandler NodesCollapsed;
public event ViewNodeEventHandler ViewNodeRemoving;
public event EventHandler FilterListChanged;
#region Event Caller Methods
private void OnNodeAdded( JetListViewNode node )
{
if ( NodeAdded != null )
{
NodeAdded( this, new JetListViewNodeEventArgs( node ) );
}
}
private void OnVisibleNodeAdded( JetListViewNode node )
{
_version++;
if ( _batchUpdateCount > 0 )
{
_havePendingUpdates = true;
if ( !_fullUpdate )
{
if ( _addedNodes == null )
{
_addedNodes = new HashSet();
}
_addedNodes.Add( node );
}
}
else if ( VisibleNodeAdded != null )
{
VisibleNodeAdded( this, new JetListViewNodeEventArgs( node ) );
}
}
private void OnNodeChanged( JetListViewNode node )
{
if ( _batchUpdateCount > 0 )
{
_havePendingUpdates = true;
if ( !_fullUpdate )
{
if ( _changedNodes == null )
{
_changedNodes = new HashSet();
}
_changedNodes.Add( node );
}
}
else if ( NodeChanged != null )
{
NodeChanged( this, new JetListViewNodeEventArgs( node ) );
}
}
private void OnNodeRemoving( JetListViewNode childNode )
{
if ( NodeRemoving != null )
{
NodeRemoving( this, new JetListViewNodeEventArgs( childNode ) );
}
}
private void OnNodeRemoved( JetListViewNode childNode )
{
if ( NodeRemoved != null )
{
NodeRemoved( this, new JetListViewNodeEventArgs( childNode ) );
}
}
private void OnVisibleNodeRemoving( JetListViewNode childNode )
{
_version++;
if ( _batchUpdateCount > 0 )
{
_havePendingUpdates = true;
}
else if ( VisibleNodeRemoving != null )
{
VisibleNodeRemoving( this, new JetListViewNodeEventArgs( childNode ) );
}
if ( ViewNodeRemoving != null )
{
ViewNodeRemoving( this, new ViewNodeEventArgs( childNode ) );
}
}
private void OnVisibleNodeRemoved( JetListViewNode childNode )
{
if ( _batchUpdateCount > 0 )
{
_havePendingUpdates = true;
if ( !_fullUpdate )
{
if ( _removedNodes == null )
{
_removedNodes = new HashSet();
}
_removedNodes.Add( childNode );
}
}
else if ( VisibleNodeRemoved != null )
{
VisibleNodeRemoved( this, new JetListViewNodeEventArgs( childNode ) );
}
}
private void OnMultipleNodesChanged()
{
_version++;
if ( _batchUpdateCount > 0 )
{
_havePendingUpdates = true;
_fullUpdate = true;
_addedNodes = null;
_removedNodes = null;
_changedNodes = null;
}
else if ( MultipleNodesChanged != null )
{
MultipleNodesChanged( this,
new MultipleNodesChangedEventArgs( _addedNodes, _removedNodes, _changedNodes ) );
}
}
protected internal void OnExpandChanging( JetListViewNode node )
{
if ( NodeExpandChanging != null )
{
NodeExpandChanging( this, new JetListViewNodeEventArgs( node ) );
}
}
protected internal void OnExpandChanged( JetListViewNode node )
{
_version++;
if ( NodeExpandChanged != null )
{
NodeExpandChanged( this, new JetListViewNodeEventArgs( node ) );
}
if ( !node.Expanded )
{
if ( NodesCollapsed != null )
{
NodesCollapsed( this, EventArgs.Empty );
}
}
}
internal bool OnChildrenRequested( JetListViewNode node, RequestChildrenReason reason )
{
if ( ChildrenRequested != null )
{
RequestChildrenEventArgs args = new RequestChildrenEventArgs( node, reason );
ChildrenRequested( this, args );
return args.Handled;
}
return true;
}
internal void OnSorted()
{
_version++;
if ( Sorted != null )
{
Sorted( this, EventArgs.Empty );
}
}
internal void OnNodeMoving( JetListViewNode node )
{
_version++;
JetListViewNodeEventArgs args = new JetListViewNodeEventArgs( node );
if ( NodeMoving != null )
{
NodeMoving( this, args );
}
}
internal void OnNodeMoved( JetListViewNode node )
{
_version++;
JetListViewNodeEventArgs args = new JetListViewNodeEventArgs( node );
if ( NodeMoved != null )
{
NodeMoved( this, args );
}
}
#endregion
public JetListViewNode Root
{
get { return _rootNode; }
}
internal int Version { get { return _version; } }
public JetListViewNode Add( object item )
{
return Add( item, null );
}
internal JetListViewNode Add( object item, JetListViewNode parentNode )
{
lock( this )
{
_lastUpdatedNode = null;
if ( parentNode == null )
parentNode = _rootNode;
if ( parentNode != _rootNode )
{
_flatList = false;
}
CollapseState oldCollapseState = parentNode.CollapseState;
JetListViewNode node = new JetListViewNode( this, item );
parentNode.AddChild( node );
_nodeMap.Add( item, node );
if ( _filters != null )
{
filtersAccept = _filters.AcceptNode( node );
node.SetFiltersAccept( filtersAccept );
if ( !filtersAccept )
{
parentNode.UpdateUnacceptedChildCount();
}
}
OnNodeAdded( node );
if ( IsNodeVisible( node ) )
{
OnVisibleNodeAdded( node );
}
if ( parentNode != _rootNode && IsNodeVisible( parentNode ) &&
oldCollapseState != parentNode.CollapseState )
{
OnNodeChanged( parentNode );
}
return node;
}
}
public void Remove( object item, JetListViewNode parentNode )
{
lock( this )
{
_lastUpdatedNode = null;
if ( parentNode == null )
parentNode = _rootNode;
JetListViewNode[] childNodes = _nodeMap.NodesFromItem( item );
if ( childNodes.Length == 0 )
{
throw new ArgumentException( "Trying to remove item which was not added", "item" );
}
int removedNodes = 0;
for( int i=0; i= 0; i-- )
{
RemoveNode( node, node.GetChildNode( i ) );
}
}
}
}
private void ClearAll()
{
_flatList = true;
_rootNode.ClearChildren();
_nodeMap.Clear();
lock( _comparerMap )
{
_comparerMap.Clear();
}
OnMultipleNodesChanged();
}
public JetListViewNode NodeFromItem( JetListViewNode parent, object item )
{
if ( item == null )
{
throw new ArgumentNullException( "item" );
}
lock( this )
{
if ( parent == null )
{
parent = _rootNode;
}
return _nodeMap.NodeFromItem( item, parent );
}
}
private JetListViewNode ParentNodeFromItem( object parent )
{
JetListViewNode parentNode = _rootNode;
if ( parent != null )
{
parentNode = NodeFromItem( parent );
if ( parentNode == null )
throw new ArgumentException( "Unknown parent " + parent );
}
return parentNode;
}
public ChildNodeCollection Nodes
{
get { return new ChildNodeCollection( _rootNode ); }
}
public IEnumerable VisibleItems
{
get { return _visibleItemsEnumerable; }
}
private class VisibleItemsEnumerable: IEnumerable
{
private JetListViewNode _rootNode;
public VisibleItemsEnumerable( JetListViewNode rootNode )
{
_rootNode = rootNode;
}
public IEnumerator GetEnumerator()
{
return new NodeEnumerator( _rootNode );
}
}
public IEnumerator EnumerateNodesForward( JetListViewNode startNode )
{
Guard.NullArgument( startNode, "startNode" );
return new NodeEnumerator( startNode.Parent, startNode.Parent.IndexOf( startNode ), false );
}
public IVisibleNodeEnumerator EnumerateVisibleNodesForward( JetListViewNode startNode )
{
Guard.NullArgument( startNode, "startNode" );
return new NodeEnumerator( startNode.Parent, startNode.Parent.IndexOf( startNode ), true );
}
public IEnumerator EnumerateNodesBackward( JetListViewNode startNode )
{
Guard.NullArgument( startNode, "startNode" );
return new ReverseNodeEnumerator( startNode.Parent, startNode.Parent.IndexOf( startNode ), false );
}
public IVisibleNodeEnumerator EnumerateVisibleNodesBackward( JetListViewNode startNode )
{
Guard.NullArgument( startNode, "startNode" );
return new ReverseNodeEnumerator( startNode.Parent, startNode.Parent.IndexOf( startNode ), true );
}
internal IVisibleNodeEnumerator GetDirectionalEnumerator( JetListViewNode startNode, MoveDirection direction )
{
switch( direction )
{
case MoveDirection.Down:
return EnumerateVisibleNodesForward( startNode );
case MoveDirection.Up:
return EnumerateVisibleNodesBackward( startNode );
default:
throw new ArgumentException( "Invalid move direction" );
}
}
public int VisibleItemCount
{
get
{
if ( _flatList && (_filters == null || _filters.Count == 0 ) )
{
return _rootNode.ChildCount;
}
return _rootNode.CountVisibleItems();
}
}
public JetListViewNode FirstVisibleNode
{
get
{
IEnumerator enumerator = VisibleItems.GetEnumerator();
if ( !enumerator.MoveNext() )
{
return null;
}
return (JetListViewNode) enumerator.Current;
}
}
public JetListViewNode LastNode
{
get
{
lock( this )
{
return _rootNode.GetLastChild( false );
}
}
}
public JetListViewNode LastVisibleNode
{
get
{
lock( this )
{
return _rootNode.GetLastChild( true );
}
}
}
public JetListViewNode NodeFromItem( object item )
{
lock( this )
{
return _nodeMap.NodeFromItem( item );
}
}
public JetListViewNode[] NodesFromItem( object item )
{
lock( this )
{
return _nodeMap.NodesFromItem( item );
}
}
public void SetItemComparer( object parentItem, IComparer comparer )
{
JetListViewNode node = ParentNodeFromItem( parentItem );
lock( _comparerMap )
{
_comparerMap [node] = new NodeComparer( comparer );
}
}
internal IComparer GetNodeComparer( JetListViewNode node )
{
while( node != null )
{
IComparer itemComparer;
lock( _comparerMap )
{
itemComparer = (IComparer) _comparerMap [node];
}
if ( itemComparer != null )
{
return itemComparer;
}
node = node.Parent;
}
return null;
}
public void Update( object item )
{
lock( this )
{
JetListViewNode[] nodes = NodesFromItem( item );
if ( nodes.Length == 0 )
throw new ArgumentException( "Item not found in list", "item" );
for( int i=0; i rhs.Level )
{
lhs = lhs.Parent;
}
while( rhs.Level > lhs.Level )
{
rhs = rhs.Parent;
}
while( lhs.Parent != rhs.Parent )
{
lhs = lhs.Parent;
rhs = rhs.Parent;
}
if ( lhs == rhs )
{
return origLhs.Level - origRhs.Level;
}
int lhsIndex = lhs.Parent.IndexOf( lhs );
int rhsIndex = rhs.Parent.IndexOf( rhs );
return lhsIndex - rhsIndex;
}
public MoveDirection GetMoveDirection( IViewNode startNode, IViewNode endNode )
{
JetListViewNode startLvNode = (JetListViewNode) startNode;
JetListViewNode endLvNode = (JetListViewNode) endNode;
int rc = CompareVisibleOrder( startLvNode, endLvNode );
if ( rc < 0 )
{
return MoveDirection.Down;
}
return MoveDirection.Up;
}
public void SetExpandedRecursive( bool expanded )
{
for( int i=0; i<_rootNode.ChildCount; i++ )
{
_rootNode.GetChildNode( i ).SetExpandedRecursive( expanded );
}
}
IVisibleNodeEnumerator IVisibleNodeCollection.GetDirectionalEnumerator( IViewNode startNode, MoveDirection direction )
{
if ( startNode is JetListViewNode )
{
return GetDirectionalEnumerator( (JetListViewNode) startNode, direction );
}
return new EmptyVisibleNodeEnumerator();
}
IViewNode IVisibleNodeCollection.GetVisibleParent( IViewNode node )
{
JetListViewNode lvNode = node as JetListViewNode;
if ( lvNode == null )
{
return null;
}
return GetVisibleParent( lvNode );
}
IVisibleNodeEnumerator IVisibleNodeCollection.GetFullEnumerator()
{
return (IVisibleNodeEnumerator) VisibleItems.GetEnumerator();
}
bool IVisibleNodeCollection.IsNodeVisible( IViewNode node )
{
JetListViewNode lvNode = node as JetListViewNode;
if ( lvNode == null || !Contains( lvNode.Data ) )
{
return false;
}
while( lvNode != null )
{
if ( !lvNode.FiltersAccept )
{
return false;
}
lvNode = lvNode.Parent;
}
return true;
}
void IVisibleNodeCollection.EnsureNodeVisible( IViewNode node )
{
ExpandParents( (JetListViewNode) node );
}
int IVisibleNodeCollection.VisibleNodeCount
{
get { return VisibleItemCount; }
}
IViewNode IVisibleNodeCollection.LastVisibleViewNode
{
get { return LastVisibleNode; }
}
IViewNode IVisibleNodeCollection.FirstVisibleViewNode
{
get { return FirstVisibleNode; }
}
}
}