///
/// 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.Windows.Forms;
using JetBrains.DataStructures;
using JetBrains.Omea.Base;
namespace JetBrains.JetListViewLibrary
{
internal interface IPagingProvider
{
IViewNode MoveByPage( IViewNode startNode, MoveDirection direction );
}
///
/// Manages the selection state and selection actions of items in JetListView.
///
public abstract class SelectionModel: IEnumerable
{
private JetListViewNodeCollection _nodeCollection;
private IVisibleNodeCollection _visibleNodeCollection;
private IViewNode _focusNode;
private IViewNode _activeNode;
private IViewNode _selectionStartNode;
private bool _processedMouseDown;
private IPagingProvider _pagingProvider;
private IViewNode _lastRemovedNode;
internal SelectionModel( JetListViewNodeCollection nodeCollection )
{
_nodeCollection = nodeCollection;
_nodeCollection.MultipleNodesChanged += new MultipleNodesChangedEventHandler( HandleMultipleNodesChanged );
VisibleNodeCollection = _nodeCollection;
}
internal IPagingProvider PagingProvider
{
get { return _pagingProvider; }
set { _pagingProvider = value; }
}
internal IVisibleNodeCollection VisibleNodeCollection
{
get { return _visibleNodeCollection; }
set
{
if ( _visibleNodeCollection != value )
{
if ( _visibleNodeCollection != null )
{
_visibleNodeCollection.NodesCollapsed -= new EventHandler( HandleNodesCollapsed );
_visibleNodeCollection.ViewNodeRemoving -= new ViewNodeEventHandler( HandleNodeRemoving );
}
_visibleNodeCollection = value;
if ( _visibleNodeCollection != null )
{
_visibleNodeCollection.NodesCollapsed += new EventHandler( HandleNodesCollapsed );
_visibleNodeCollection.ViewNodeRemoving += new ViewNodeEventHandler( HandleNodeRemoving );
}
}
}
}
internal IViewNode FocusViewNode
{
get { return _focusNode; }
}
internal JetListViewNode FocusNode
{
get { return _focusNode as JetListViewNode; }
}
public JetListViewNode ActiveNode
{
get { return _activeNode as JetListViewNode; }
}
public abstract int Count
{
get;
}
public object this [int index]
{
get
{
lock( SelectionLock )
{
IEnumerator enumerator = GetEnumerator();
for( int i=0; i<=index; i++ )
{
if ( !enumerator.MoveNext() )
{
throw new IndexOutOfRangeException( "The selection index " + index + " is out of range; selection count = " + Count );
}
}
return enumerator.Current;
}
}
}
internal abstract IEnumerable SelectedNodes
{
get;
}
public abstract IEnumerator GetEnumerator();
internal MouseHandleResult HandleMouseDown( IViewNode itemNode, Keys modifiers )
{
MouseHandleResult result = 0;
_processedMouseDown = false;
if ( itemNode == null )
{
return result;
}
if ( (modifiers & Keys.Shift) != 0 )
{
ProcessShiftClick( itemNode, ((modifiers & Keys.Control) != 0) );
}
else if ( (modifiers & Keys.Control) != 0 )
{
ProcessControlClick( itemNode );
}
else
{
if ( IsSingleNodeSelected( itemNode ) && FocusNode == itemNode )
{
result |= MouseHandleResult.MayInPlaceEdit;
}
ProcessClick( itemNode );
}
if ( itemNode != null )
{
SetFocusNode( itemNode );
}
return result;
}
internal void HandleMouseUp( IViewNode itemNode, Keys modifiers )
{
if ( itemNode != null )
{
if ( modifiers == Keys.Control )
{
if ( IsNodeSelected( itemNode ) && !_processedMouseDown )
{
UnselectNode( itemNode );
}
}
else if ( modifiers == Keys.None )
{
ProcessMouseUp( itemNode );
}
}
}
private void ProcessClick( IViewNode itemNode )
{
if ( !IsNodeSelected( itemNode ) )
{
ClearSelection();
SelectNode( itemNode );
}
else
{
SetFocusNode( itemNode );
}
_selectionStartNode = itemNode;
}
private void ProcessMouseUp( IViewNode node )
{
if ( !IsSingleNodeSelected( node ) )
{
IViewNode[] selNodes = SelectionToArray();
for( int i=0; i
/// Adds the specified item to the selection if it is present in the list of items
/// in the control.
///
/// The item to select.
/// true if the item was added to selection, false if it is not present in the control.
public bool AddIfPresent( object item )
{
if ( item == null )
throw new ArgumentNullException( "item" );
JetListViewNode node = _nodeCollection.NodeFromItem( item );
if ( node != null )
{
AddSelectedNode( node );
return true;
}
return false;
}
public void Add( object item )
{
if ( item == null )
throw new ArgumentNullException( "item" );
JetListViewNode node = _nodeCollection.NodeFromItem( item );
if ( node == null )
{
throw new ArgumentException( "Item is not displayed in the control", "item" );
}
AddSelectedNode( node );
}
private void AddSelectedNode( JetListViewNode node )
{
_visibleNodeCollection.EnsureNodeVisible( node );
SelectNode( node );
if ( _focusNode == null )
{
SetFocusNode( node );
}
if ( _selectionStartNode == null )
{
_selectionStartNode = node;
}
}
public void SelectSingleItem( object item )
{
Guard.NullArgument( item, "item" );
JetListViewNode node = _nodeCollection.NodeFromItem( item );
if ( node == null )
{
throw new ArgumentException( "Item is not displayed in the control", "item" );
}
SelectAndFocusNode( node );
}
public void Remove( object item )
{
JetListViewNode[] nodes = _nodeCollection.NodesFromItem( item );
if ( nodes.Length == 0 )
{
throw new ArgumentException( "Item is not displayed in the control", "item" );
}
for( int i=0; i
/// Checks if any of the nodes displaying the specified item is selected.
///
/// The item to check.
/// true if the item is selected, false otherwise.
public bool Contains( object item )
{
JetListViewNode[] nodes = _nodeCollection.NodesFromItem( item );
if ( nodes.Length == 0 )
{
throw new ArgumentException( "Item is not displayed in the control", "item" );
}
for( int i=0; i
/// Moves the selection up by a single item.
///
public void MoveUp()
{
MoveSelection( MoveDirection.Up, Keys.None );
}
///
/// Moves the selection down by a single item.
///
public void MoveDown()
{
MoveSelection( MoveDirection.Down, Keys.None );
}
private void MoveSelection( MoveDirection direction, Keys modifiers )
{
IViewNode destNode;
lock( _nodeCollection )
{
IVisibleNodeEnumerator enumerator;
if ( _focusNode == null )
{
enumerator = _visibleNodeCollection.GetFullEnumerator();
if ( !enumerator.MoveNext() )
return;
}
else
{
enumerator = _visibleNodeCollection.GetDirectionalEnumerator( _focusNode, direction );
enumerator.MoveNext();
if ( !enumerator.MoveNext() )
return;
}
destNode = enumerator.CurrentNode;
// skip group headers
if ( destNode is GroupHeaderNode )
{
if ( enumerator.MoveNext() && enumerator.CurrentNode is JetListViewNode )
{
destNode = enumerator.CurrentNode;
}
}
MoveSelectionTo( destNode, modifiers );
}
}
private void MoveSelectionByPage( MoveDirection direction, Keys modifiers )
{
if ( _focusNode == null || _pagingProvider == null )
{
MoveSelection( direction, Keys.None );
return;
}
IViewNode destNode = _pagingProvider.MoveByPage( _focusNode, direction );
MoveSelectionTo( destNode, modifiers );
}
private void MoveSelectionTo( IViewNode destNode, Keys modifiers )
{
if ( (modifiers & Keys.Shift ) != 0 && _selectionStartNode != null )
{
SelectItemRange( _selectionStartNode, destNode, true );
SetFocusNode( destNode );
}
else if ( (modifiers & Keys.Control) != 0 )
{
SetFocusNode( destNode );
}
else
{
SelectAndFocusNode( destNode );
}
}
internal void SelectAndFocusNode( IViewNode destNode )
{
if ( !IsSingleNodeSelected( destNode ) )
{
ClearSelection();
SelectNode( destNode );
SetFocusNode( destNode );
}
_selectionStartNode = destNode;
}
private bool IsSingleNodeSelected( IViewNode node )
{
return Count == 1 && IsNodeSelected( node ) && _focusNode == node;
}
private void ProcessSpaceKey()
{
if ( _focusNode != null )
{
if ( IsNodeSelected( _focusNode ) )
{
UnselectNode( _focusNode );
}
else
{
SelectNode( _focusNode );
}
}
}
private void HandleNodeRemoving( object sender, ViewNodeEventArgs e )
{
if ( _selectionStartNode == e.ViewNode )
{
_selectionStartNode = null;
}
RemoveNodeFromSelection( e.ViewNode );
}
private void RemoveNodeFromSelection( IViewNode viewNode )
{
if ( ( UnselectNode( viewNode ) && Count == 0 ) || viewNode == _focusNode )
{
IViewNode nextNode = null, prevNode = null;
IVisibleNodeEnumerator enumerator = _visibleNodeCollection.GetDirectionalEnumerator( viewNode, MoveDirection.Down );
enumerator.MoveNext();
// make sure we don't move the selection to the node being deleted if we delete
// the node and its group in one VisibleNodeRemoving operation
if ( enumerator.MoveNext() && enumerator.CurrentNode != _lastRemovedNode )
{
nextNode = enumerator.CurrentNode;
}
enumerator = _visibleNodeCollection.GetDirectionalEnumerator( viewNode, MoveDirection.Up );
enumerator.MoveNext();
if ( enumerator.MoveNext() && enumerator.CurrentNode != _lastRemovedNode )
{
prevNode = enumerator.CurrentNode;
}
if ( nextNode == null )
{
if ( prevNode != null )
{
SelectAndFocusNode( prevNode );
}
else
{
SetFocusNode( null );
}
}
else if ( prevNode != null && viewNode is JetListViewNode )
{
JetListViewNode prevLvNode = prevNode as JetListViewNode;
JetListViewNode nextLvNode = nextNode as JetListViewNode;
int removedNodeLevel = (viewNode as JetListViewNode).Level;
if ( prevLvNode != null && nextLvNode != null &&
prevLvNode.Level == removedNodeLevel && nextLvNode.Level != removedNodeLevel )
{
SelectAndFocusNode( prevNode );
}
else if ( prevLvNode != null && nextLvNode != null &&
prevLvNode.Level != removedNodeLevel && nextLvNode.Level == removedNodeLevel )
{
SelectAndFocusNode( nextNode );
}
else if ( prevNode == (viewNode as JetListViewNode).Parent )
{
SelectAndFocusNode( prevNode );
}
else if ( prevLvNode != null && nextLvNode == null )
{
SelectAndFocusNode( prevNode );
}
else
{
SelectAndFocusNode( nextNode );
}
}
else
{
SelectAndFocusNode( nextNode );
}
}
_lastRemovedNode = viewNode;
}
private void HandleMultipleNodesChanged( object sender, MultipleNodesChangedEventArgs e )
{
// ensure correct lock ordering - lock node collection before selection
lock( _nodeCollection )
{
lock( SelectionLock )
{
if ( Count > 0 )
{
if ( _nodeCollection.IsEmpty )
{
Clear();
}
else
{
IViewNode[] selNodes = SelectionToArray();
foreach( IViewNode selNode in selNodes )
{
if ( !_visibleNodeCollection.IsNodeVisible( selNode ) )
{
UnselectNode( selNode );
}
}
if ( Count == 0 )
{
IVisibleNodeEnumerator enumerator = _visibleNodeCollection.GetFullEnumerator();
if ( enumerator.MoveNext() )
{
SelectAndFocusNode( enumerator.CurrentNode );
}
}
}
}
}
}
}
private void HandleNodesCollapsed( object sender, EventArgs e )
{
if ( Count > 0 )
{
IViewNode[] selNodes = SelectionToArray();
foreach( IViewNode node in selNodes )
{
MoveSelectionToVisibleParent( node, true );
}
}
if ( _focusNode != null )
{
MoveSelectionToVisibleParent( _focusNode, false );
}
if ( _selectionStartNode != null )
{
_selectionStartNode = _visibleNodeCollection.GetVisibleParent( _selectionStartNode );
}
}
private void MoveSelectionToVisibleParent( IViewNode node, bool select )
{
IViewNode visibleParent = _visibleNodeCollection.GetVisibleParent( node );
if ( visibleParent != null )
{
if ( select )
{
UnselectNode( node );
SelectNode( visibleParent );
}
else
{
SetFocusNode( visibleParent );
}
}
}
public void SelectAll()
{
if ( _nodeCollection.VisibleItemCount > 0 )
{
IEnumerator enumerator = _nodeCollection.EnumerateNodesForward( _nodeCollection.Nodes [0] );
while( enumerator.MoveNext() )
{
SelectNode( (IViewNode) enumerator.Current );
}
}
}
}
}