/// /// 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 ); } } } } }