/// /// 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 System.Drawing; using System.Windows.Forms; using JetBrains.DataStructures; using JetBrains.Omea.Base; using SP.Windows; namespace JetBrains.JetListViewLibrary { [Flags] public enum RowState { None = 0, ActiveSelected = 1, InactiveSelected = 2, Focused = 4, Disabled = 8, DropTarget = 16, InPlaceEdit = 32, IncSearchMatch = 64 }; [Flags] public enum MouseHandleResult { Handled = 1, MayInPlaceEdit = 2, SuppressFocus = 4, FocusOnMouseDown = 8 } /// /// Defines how the visual cues for a drop target are rendered during the drag'n'drop operation over the list view. /// [Flags] public enum DropTargetRenderMode { /// /// The drop over the current item is prohibited. /// Currently, the drop target cues for this mode are not painted at all. /// Restricted = 1, /// /// A box drop target over the item. /// Over = 2, /// /// An insertion mark above the hovered item. /// InsertAbove = 4, /// /// An insertion mark below the hovered item. /// InsertBelow = 8, /// /// A synthetic value meaning any insertion drop target. Do not assign to this value, just use it to test for any kind of insertion. /// InsertAny = InsertAbove | InsertBelow } internal interface IRowRenderer { int GetRowHeight( JetListViewNode node ); void UpdateItem( object item ); void DrawRow( Graphics g, Rectangle rc, JetListViewNode itemNode, RowState rowState ); MouseHandleResult HandleMouseDown( JetListViewNode node, int x, int y, MouseButtons button, Keys modifiers ); bool HandleMouseUp( JetListViewNode node, int x, int y, MouseButtons button, Keys modifiers ); bool HandleKeyDown( JetListViewNode node, KeyEventArgs e ); bool AcceptDoubleClick( JetListViewNode node,int x, int y ); void HandleDoubleClick( JetListViewNode node ); void SizeColumnsToContent( HashSet addedNodes, HashSet removedNodes, HashSet changedNodes ); void ProcessNodeExpanded( JetListViewNode node ); void ProcessNodeCollapsed( JetListViewNode node ); Rectangle GetColumnBounds( JetListViewColumn col, JetListViewNode node ); JetListViewColumn GetInPlaceEditColumn( JetListViewNode node ); bool MatchIncrementalSearch( JetListViewNode node, string text ); int ScrollOffset { get; set; } int ScrollRange { get; } int VisibleWidth { get; set; } int BorderSize { get; set; } string SearchHighlightText { get; set; } Header HeaderControl { get; set; } Control OwnerControl { get; set; } IControlMethodInvoker MethodInvoker { get; set; } IControlPainter ControlPainter { get; set; } bool FullRowSelect { get; set; } int AllRowsHeight { get; } JetListViewColumn GetColumnAt( JetListViewNode node, int x, int y ); event EventHandler ScrollRangeChanged; event EventHandler Invalidate; event RowHeightChangedEventHandler RowHeightChanged; event EventHandler AllRowsHeightChanged; event RequestScrollEventHandler RequestScroll; } /// /// Draws and handles events for group headers. /// internal interface IGroupRenderer { void DrawGroupHeader( Graphics g, Rectangle rectangle, GroupHeaderNode node, RowState rowState ); bool HandleMouseDown( GroupHeaderNode node, int x, int y, MouseButtons button, Keys modifiers ); bool HandleGroupKeyDown( GroupHeaderNode node, KeyEventArgs e ); bool HandleNodeKeyDown( JetListViewNode viewNode, KeyEventArgs e ); Color GroupHeaderColor { get; set; } int GroupHeaderHeight { get; } int VisibleWidth { get; set; } } /// /// Manages drawing and scrolling of a list of variable-height rows. The specific content /// of each row is defined by the IRowRenderer implementation used. /// internal class RowListRenderer: IPagingProvider { private JetListViewNodeCollection _nodeCollection; private NodeGroupCollection _groupCollection; private IVisibleNodeCollection _visibleNodeCollection; private IRowRenderer _rowRenderer; private IGroupRenderer _groupRenderer; private int _scrollOffset; private int _scrollRange; private int _borderSize; private int _lastRemovedNodeTop; private int _lastMovingNodeTop; private int _visibleHeight = Int32.MaxValue; private SelectionModel _selection; private bool _activeSelection = false; private bool _controlEnabled = true; private JetListViewNode _dropTargetRow; private JetListViewNode _inPlaceEditedNode; private IViewNode _topNode; private int _topNodeOffset; private bool _hideSelection; private bool _rowDelimiters = false; private bool _calculatingScrollRange = false; private DropTargetRenderMode _dropTargetRenderMode; public RowListRenderer( JetListViewNodeCollection collection, SelectionModel selectionModel ) { _nodeCollection = collection; _nodeCollection.VisibleNodeAdded += new JetListViewNodeEventHandler( HandleVisibleNodeAdded ); _nodeCollection.VisibleNodeRemoving += new JetListViewNodeEventHandler( HandleVisibleNodeRemoving ); _nodeCollection.VisibleNodeRemoved += new JetListViewNodeEventHandler( HandleVisibleNodeRemoved ); _nodeCollection.NodeMoving += new JetListViewNodeEventHandler( HandleNodeMoving ); _nodeCollection.NodeMoved += new JetListViewNodeEventHandler( HandleNodeMoved ); _nodeCollection.MultipleNodesChanged += new MultipleNodesChangedEventHandler( HandleMultipleNodesChanged ); _nodeCollection.FilterListChanged += new EventHandler( HandleFilterListChanged ); _nodeCollection.NodeExpandChanged += new JetListViewNodeEventHandler( HandleNodeExpandChanged ); _nodeCollection.NodeChanged += new JetListViewNodeEventHandler( HandleNodeChanged ); _nodeCollection.Sorted += new EventHandler( HandleNodesSorted ); _visibleNodeCollection = _nodeCollection; SelectionModel = selectionModel; _topNode = null; } public event InvalidateEventHandler Invalidate; public event EventHandler ScrollRangeChanged; public event RequestScrollEventHandler RequestVerticalScroll; internal IRowRenderer RowRenderer { get { return _rowRenderer; } set { if ( _rowRenderer != value ) { bool hadRenderer = (_rowRenderer != null); if ( _rowRenderer != null ) { _rowRenderer.RowHeightChanged -= new RowHeightChangedEventHandler( HandleRowHeightChanged ); _rowRenderer.AllRowsHeightChanged -= new EventHandler( HandleAllRowsHeightChanged ); } _rowRenderer = value; if ( _rowRenderer != null ) { _rowRenderer.RowHeightChanged += new RowHeightChangedEventHandler( HandleRowHeightChanged ); _rowRenderer.AllRowsHeightChanged += new EventHandler( HandleAllRowsHeightChanged ); } if ( hadRenderer ) { UpdateScrollRange(); } } } } internal IGroupRenderer GroupRenderer { get { return _groupRenderer; } set { _groupRenderer = value; UpdateScrollRange(); } } internal NodeGroupCollection NodeGroupCollection { get { return _groupCollection; } set { if ( _groupCollection != value) { if ( _groupCollection != null ) { _groupCollection.GroupAdded -= new GroupEventHandler( HandleGroupsChanged ); _groupCollection.GroupRemoved -= new GroupEventHandler( HandleGroupsChanged ); _groupCollection.GroupExpandChanged -= new GroupEventHandler( HandleGroupExpandChanged ); } _groupCollection = value; if ( _groupCollection != null ) { _groupCollection.GroupAdded += new GroupEventHandler( HandleGroupsChanged ); _groupCollection.GroupRemoved += new GroupEventHandler( HandleGroupsChanged ); _groupCollection.GroupExpandChanged += new GroupEventHandler( HandleGroupExpandChanged ); _visibleNodeCollection = _groupCollection; } else { _visibleNodeCollection = _nodeCollection; } UpdateTopNode(); } } } internal SelectionModel SelectionModel { get { return _selection; } set { if ( _selection != value ) { if ( _selection != null ) { _selection.SelectionStateChanged -= new ViewNodeStateChangeEventHandler( HandleSelectionStateChanged ); _selection.FocusStateChanged -= new ViewNodeStateChangeEventHandler( HandleFocusStateChanged ); _selection.PagingProvider = null; } _selection = value; _selection.PagingProvider = this; _selection.SelectionStateChanged += new ViewNodeStateChangeEventHandler( HandleSelectionStateChanged ); _selection.FocusStateChanged += new ViewNodeStateChangeEventHandler( HandleFocusStateChanged ); } } } public int ScrollOffset { get { return _scrollOffset; } set { if ( _scrollOffset != value ) { int delta = value - _scrollOffset; _scrollOffset = value; AdjustTopNode( delta ); /* IViewNode adjustedNode = _topNode; int adjustedOffset = _topNodeOffset; UpdateTopNode(); if ( adjustedNode != _topNode || adjustedOffset != _topNodeOffset ) { throw new Exception( "AdjustTopNode/UpdateTopNode mismatch" ); } */ } } } private int GetRowHeight( IViewNode node ) { GroupHeaderNode headerNode = node as GroupHeaderNode; if ( headerNode != null ) { return _groupRenderer.GroupHeaderHeight; } else { int height = _rowRenderer.GetRowHeight( (JetListViewNode) node ); if ( _rowDelimiters ) { height++; } return height; } } private bool inUpdateTopNode = false; private void UpdateTopNode() { if( !inUpdateTopNode ) { inUpdateTopNode = true; try { lock( _nodeCollection ) { _topNode = null; int delta = _scrollOffset; IVisibleNodeEnumerator enumerator = _visibleNodeCollection.GetFullEnumerator(); while( enumerator.MoveNext() ) { int height = GetRowHeight( enumerator.CurrentNode ); if ( delta < height ) { _topNode = enumerator.CurrentNode; _topNodeOffset = delta; break; } delta -= height; } } } finally { inUpdateTopNode = false; } } } private void AdjustTopNode( int delta ) { lock( _nodeCollection ) { IViewNode topNode = _topNode; // avoid race conditions (OM-12038) if ( topNode == null || !_visibleNodeCollection.IsNodeVisible( topNode ) ) { UpdateTopNode(); return; } try { IVisibleNodeEnumerator enumerator = _visibleNodeCollection.GetDirectionalEnumerator( topNode, (delta > 0) ? MoveDirection.Down : MoveDirection.Up ); if ( _topNodeOffset != 0 ) { if ( delta > 0 ) { int curNodeDelta = GetRowHeight( topNode ) - _topNodeOffset; if ( delta < curNodeDelta ) { _topNodeOffset += delta; return; } delta -= curNodeDelta; enumerator.MoveNext(); } else { if ( _topNodeOffset >= -delta ) { _topNodeOffset += delta; return; } delta += _topNodeOffset; } } if ( delta < 0 ) { enumerator.MoveNext(); } int absDelta = Math.Abs( delta ); _topNodeOffset = 0; while( enumerator.MoveNext() ) { int curRowHeight = GetRowHeight( enumerator.CurrentNode ); if ( absDelta < curRowHeight ) { if ( delta > 0 || absDelta == 0 ) { _topNodeOffset = absDelta; } else { _topNodeOffset = curRowHeight - absDelta; } break; } absDelta -= curRowHeight; if ( delta < 0 && absDelta == 0 ) { break; } } _topNode = enumerator.CurrentNode; } catch( ArgumentOutOfRangeException ex ) // mega-hacky crappy fix for OM-11090 { Trace.WriteLine( "Exception in UpdateTopNode: " + ex.ToString() ); UpdateTopNode(); } } } public int ScrollRange { get { return _scrollRange; } } public int VisibleHeight { get { return _visibleHeight; } set { _visibleHeight = value; } } public int BorderSize { get { return _borderSize; } set { _borderSize = value; } } internal bool ActiveSelection { get { return _activeSelection; } set { if ( _activeSelection != value ) { _activeSelection = value; InvalidateSelectedNodes(); } } } public bool HideSelection { get { return _hideSelection; } set { if ( _hideSelection != value ) { _hideSelection = value; if ( !_activeSelection ) { InvalidateSelectedNodes(); } } } } public bool RowDelimiters { get { return _rowDelimiters; } set { if ( _rowDelimiters != value ) { _rowDelimiters = value; UpdateScrollRange(); UpdateTopNode(); } } } private void InvalidateSelectedNodes() { IViewNode[] nodes = _selection.SelectionToArray(); foreach( IViewNode node in nodes ) { InvalidateRow( node ); } } public bool ControlEnabled { get { return _controlEnabled; } set { _controlEnabled = value; } } internal JetListViewNode DropTargetRow { get { return _dropTargetRow; } } protected void OnInvalidate( int startY, int endY ) { if ( Invalidate != null ) { Invalidate( this, new InvalidateEventArgs( startY, endY ) ); } } private void HandleGroupsChanged( object sender, GroupEventArgs e ) { UpdateTopNode(); UpdateScrollRange(); InvalidateBelow( 0 ); } private void HandleGroupExpandChanged( object sender, GroupEventArgs e ) { UpdateScrollRange(); InvalidateBelow( e.GroupHeader ); } private void HandleVisibleNodeAdded( object sender, JetListViewNodeEventArgs e ) { Guard.NullArgument( e.Node, "e.Node" ); if ( _topNode == null ) // first node added to the view { _topNode = e.Node; } UpdateScrollRange(); IViewNode topNode = _topNode; if ( topNode == null ) { topNode = e.Node; } if ( _visibleNodeCollection.GetMoveDirection( e.Node, topNode ) == MoveDirection.Down ) { // If we were scrolled so that the top item in the list was visible, // add the new item to the visible area. If the top item in the list was // not visible, scroll down so that the visible area remains the same and // the new item does not appear in it. if ( _scrollOffset == 0 ) { UpdateTopNode(); } else { OnRequestVerticalScroll( _scrollOffset + GetRowHeight( e.Node ) ); } } InvalidateBelow( e.Node ); } private void HandleNodeExpandChanged( object sender, JetListViewNodeEventArgs e ) { if ( e.Node.Expanded ) { _rowRenderer.ProcessNodeExpanded( e.Node ); } else { _rowRenderer.ProcessNodeCollapsed( e.Node ); } UpdateTopNode(); UpdateScrollRange(); InvalidateBelow( e.Node ); } private void InvalidateBelow( IViewNode node ) { int startY, endY; if ( GetRowBounds( node, out startY, out endY ) ) { InvalidateBelow( startY ); } } private void InvalidateBelow( int y ) { OnInvalidate( y, VisibleHeight + _borderSize ); } private void HandleVisibleNodeRemoving( object sender, JetListViewNodeEventArgs e ) { _lastRemovedNodeTop = GetNodeTop( e.Node ); } private void HandleVisibleNodeRemoved( object sender, JetListViewNodeEventArgs e ) { UpdateTopNode(); UpdateScrollRange(); if ( _lastRemovedNodeTop >= 0 ) { InvalidateBelow( _lastRemovedNodeTop ); } } private void HandleNodeMoving( object sender, JetListViewNodeEventArgs e ) { _lastMovingNodeTop = GetNodeTop( e.Node ); } private void HandleNodeMoved( object sender, JetListViewNodeEventArgs e ) { UpdateTopNode(); int movedNodeTop = GetNodeTop( e.Node ); if ( _lastMovingNodeTop >= 0 || movedNodeTop >= 0 ) { InvalidateBelow( Math.Min( _lastMovingNodeTop, movedNodeTop ) ); } } private void HandleMultipleNodesChanged( object sender, MultipleNodesChangedEventArgs e ) { _rowRenderer.SizeColumnsToContent( e.AddedNodes, e.RemovedNodes, e.ChangedNodes ); UpdateTopNode(); UpdateScrollRange(); InvalidateBelow( 0 ); } private void HandleFilterListChanged( object sender, EventArgs e ) { ScrollSelectionInView(); } private void ScrollSelectionInView() { if ( _selection.FocusNode != null ) { ScrollInView( _selection.FocusNode ); } } private void HandleNodeChanged( object sender, JetListViewNodeEventArgs e ) { InvalidateRow( e.Node ); } private void HandleRowHeightChanged( object sender, RowHeightChangedEventArgs e ) { if ( JetListViewNodeCollection.IsNodeVisible( e.Row ) ) { SetScrollRange( _scrollRange - e.OldHeight + e.NewHeight ); InvalidateBelow( 0 ); } } private void HandleAllRowsHeightChanged( object sender, EventArgs e ) { UpdateScrollRange(); UpdateTopNode(); InvalidateBelow( 0 ); ScrollSelectionInView(); } internal void UpdateScrollRange() { if ( _rowRenderer == null || _calculatingScrollRange ) { return; } int newScrollRange; _calculatingScrollRange = true; try { if ( _rowRenderer.AllRowsHeight >= 0 && _groupCollection == null) { int rowHeight = _rowRenderer.AllRowsHeight; if ( _rowDelimiters ) { rowHeight++; } newScrollRange = rowHeight * _visibleNodeCollection.VisibleNodeCount; } else { newScrollRange = 0; lock( _nodeCollection ) { if ( _nodeCollection.Nodes.Count > 0 ) { IEnumerator rowEnumerator = _visibleNodeCollection.GetFullEnumerator(); while( rowEnumerator.MoveNext() ) { newScrollRange += GetRowHeight( (IViewNode) rowEnumerator.Current ); } } } } } finally { _calculatingScrollRange = false; } SetScrollRange( newScrollRange ); } private void SetScrollRange( int newScrollRange ) { if ( newScrollRange != _scrollRange ) { _scrollRange = newScrollRange; if ( ScrollRangeChanged != null ) { ScrollRangeChanged( this, EventArgs.Empty ); } } } private IEnumerator GetRowEnumerator() { return GetRowEnumerator( _topNode ); } private IEnumerator GetRowEnumerator( IViewNode startNode ) { return _visibleNodeCollection.GetDirectionalEnumerator( startNode, MoveDirection.Down ); } public void Draw( Graphics g, Rectangle rectangle ) { lock( _nodeCollection ) { IViewNode topNode = _topNode; if ( topNode != null ) { int curY = -_topNodeOffset + _borderSize; IEnumerator enumerator = GetRowEnumerator( topNode ); while( enumerator.MoveNext() ) { int oldVer = _nodeCollection.Version; GroupHeaderNode groupNode = enumerator.Current as GroupHeaderNode; if ( groupNode != null ) { DrawGroupRow( g, rectangle, groupNode, ref curY ); } else { JetListViewNode itemNode = (JetListViewNode) enumerator.Current; DrawNodeRow( g, rectangle, itemNode, ref curY ); } // some column draw methods may have caused events to be pumped or modified // the collection - if it has changed, abort the draw and start again if ( oldVer != _nodeCollection.Version ) { InvalidateBelow( 0 ); break; } if ( curY >= rectangle.Bottom ) { break; } } } } } private void DrawNodeRow( Graphics g, Rectangle rectangle, JetListViewNode itemNode, ref int curY ) { int itemBaseHeight = _rowRenderer.GetRowHeight( itemNode ); int itemHeight = itemBaseHeight; if ( _rowDelimiters ) { itemHeight++; } if ( curY + itemHeight >= rectangle.Top ) { Rectangle rcRow = new Rectangle( rectangle.Left, curY, rectangle.Width, itemBaseHeight ); if ( _rowRenderer != null ) { _rowRenderer.DrawRow( g, rcRow, itemNode, GetRowState( itemNode ) ); } if ( _rowDelimiters ) { g.DrawLine( SystemPens.Control, rectangle.Left, curY + itemBaseHeight, rectangle.Width, curY + itemBaseHeight ); } // Draw an insertion mark if the current row is the insertion drop target (for insertion either above or below) if((itemNode == _dropTargetRow) && ((_dropTargetRenderMode & DropTargetRenderMode.InsertAny ) != 0)) DrawInsertMark( g, rcRow ); } curY += itemHeight; } private void DrawInsertMark( Graphics g, Rectangle rcRow ) { int markY; if ( _dropTargetRenderMode == DropTargetRenderMode.InsertAbove ) { markY = rcRow.Top-1; } else { markY = rcRow.Bottom-1; } g.DrawLine( Pens.DarkGray, rcRow.Left+2, markY+1, rcRow.Right-1, markY+1 ); g.DrawLine( Pens.DarkGray, rcRow.Left+2, markY-1, rcRow.Left+2, markY+3 ); g.DrawLine( Pens.DarkGray, rcRow.Right-1, markY-1, rcRow.Right-1, markY+3 ); g.DrawLine( Pens.BlueViolet, rcRow.Left+1, markY, rcRow.Right-2, markY ); g.DrawLine( Pens.BlueViolet, rcRow.Left+1, markY-2, rcRow.Left+1, markY+2 ); g.DrawLine( Pens.BlueViolet, rcRow.Right-2, markY-2, rcRow.Right-2, markY+2 ); } private void DrawGroupRow( Graphics g, Rectangle rectangle, GroupHeaderNode node, ref int curY ) { Rectangle rcHeader = new Rectangle( rectangle.Left, curY, rectangle.Width, _groupRenderer.GroupHeaderHeight ); _groupRenderer.DrawGroupHeader( g, rcHeader, node, GetRowState( node ) ); curY += rcHeader.Height; } private RowState GetRowState( IViewNode node ) { RowState rowState = RowState.None; if ( !_controlEnabled ) { rowState |= RowState.Disabled; } else { if ( _selection.IsNodeSelected( node ) ) { if ( _activeSelection ) { rowState |= RowState.ActiveSelected; } else if ( !_hideSelection ) { rowState |= RowState.InactiveSelected; } } if ( _activeSelection && _selection.IsNodeFocused( node ) ) { rowState |= RowState.Focused; } } if ( node == _dropTargetRow && _dropTargetRenderMode == DropTargetRenderMode.Over ) { rowState |= RowState.DropTarget; } if ( node == _inPlaceEditedNode ) { rowState |= RowState.InPlaceEdit; } return rowState; } public JetListViewNode GetRowAt( int y ) { int deltaY; return GetRowAndDelta( y, out deltaY ) as JetListViewNode; } public IViewNode GetRowAndDelta( int y, out int deltaY ) { lock( _nodeCollection ) { if ( _topNode != null ) { int curY = -_topNodeOffset + _borderSize; IEnumerator enumerator = GetRowEnumerator(); while( enumerator.MoveNext() ) { IViewNode viewNode = (IViewNode) enumerator.Current; int itemHeight = GetRowHeight( viewNode ); if ( y >= curY && y < curY + itemHeight ) { deltaY = y - curY; return viewNode; } curY += itemHeight; if ( curY >= _visibleHeight + _borderSize ) { break; } } } } deltaY = 0; return null; } public bool GetRowBounds( IViewNode node, out int startY, out int endY ) { if ( _rowRenderer != null && _topNode != null ) { lock( _nodeCollection ) { if ( _topNode != null ) { int curY = -_topNodeOffset + _borderSize; IEnumerator enumerator = GetRowEnumerator(); while( enumerator.MoveNext() ) { IViewNode itemNode = (IViewNode) enumerator.Current; int itemHeight = GetRowHeight( itemNode ); if ( itemNode == node ) { startY = curY; endY = curY + itemHeight; return endY > 0; } curY += itemHeight; if ( curY > _visibleHeight + _borderSize ) break; } } } } startY = -1; endY = -1; return false; } private int GetNodeTop( JetListViewNode node ) { int startY, endY; if ( GetRowBounds( node, out startY, out endY ) ) { return startY; } return -1; } public MouseHandleResult HandleMouseDown( int x, int y, MouseButtons button, Keys modifiers ) { MouseHandleResult rrResult = 0; int deltaY; IViewNode row = GetRowAndDelta( y, out deltaY ); if ( row is JetListViewNode ) { rrResult = _rowRenderer.HandleMouseDown( row as JetListViewNode, x, deltaY, button, modifiers ); if ( ( rrResult & MouseHandleResult.Handled ) != 0 ) { return rrResult; } } else if ( row is GroupHeaderNode ) { if ( _groupRenderer.HandleMouseDown( row as GroupHeaderNode, x, deltaY, button, modifiers ) ) { return MouseHandleResult.Handled; } } if ( button == MouseButtons.Left && row != null ) { MouseHandleResult selResult = _selection.HandleMouseDown( row, modifiers ); if ( (rrResult & MouseHandleResult.MayInPlaceEdit ) == 0 ) { selResult = rrResult & ~MouseHandleResult.MayInPlaceEdit; } return selResult; } return 0; } public void HandleMouseUp( int x, int y, MouseButtons button, Keys modifiers ) { int deltaY; JetListViewNode row = GetRowAndDelta( y, out deltaY ) as JetListViewNode; if ( row != null ) { if ( _rowRenderer.HandleMouseUp( row, x, deltaY, button, modifiers ) ) return; } if ( button == MouseButtons.Left ) { _selection.HandleMouseUp( row, modifiers ); } } public int GetWheelScrollDistance( int lines ) { // the wheel scroll can be requested between BeginUpdate() and EndUpdate(), // when the node which was _topNode has already been removed from the collection // but UpdateTopNode() was not yet called (OM-10633) int result = 0; lock ( _nodeCollection ) { IViewNode topNode = _topNode; // avoid race condition (OM-11963) if ( topNode != null && _visibleNodeCollection.IsNodeVisible( topNode ) ) { IVisibleNodeEnumerator enumerator = _visibleNodeCollection.GetDirectionalEnumerator( topNode, (lines < 0) ? MoveDirection.Up : MoveDirection.Down ); while( enumerator.MoveNext() && lines != 0 ) { int rowHeight = GetRowHeight( enumerator.CurrentNode ); if ( lines > 0 ) { result += rowHeight; lines--; } else { result -= rowHeight; lines++; } } } } return result; } public bool HandleKeyDown( KeyEventArgs e ) { IViewNode viewNode = _selection.FocusViewNode; if ( viewNode is JetListViewNode ) { if ( _rowRenderer.HandleKeyDown( viewNode as JetListViewNode, e ) ) return true; } if ( viewNode is GroupHeaderNode ) { if ( _groupRenderer.HandleGroupKeyDown( viewNode as GroupHeaderNode, e ) ) return true; } if ( viewNode is JetListViewNode && _groupRenderer != null ) { if ( _groupRenderer.HandleNodeKeyDown( viewNode as JetListViewNode, e ) ) return true; } return _selection.HandleKeyDown( e.KeyData ); } public bool AcceptDoubleClick( int x, int y ) { int deltaY; JetListViewNode node = GetRowAndDelta( y, out deltaY ) as JetListViewNode; if ( node == null ) { return false; } return _rowRenderer.AcceptDoubleClick( node, x, deltaY ); } public void HandleDoubleClick( int x, int y ) { JetListViewNode node = GetRowAt( y ); if ( node != null ) { _rowRenderer.HandleDoubleClick( node ); } } private void HandleSelectionStateChanged( object sender, ViewNodeStateChangeEventArgs e ) { InvalidateRow( e.ViewNode ); } private void HandleFocusStateChanged( object sender, ViewNodeStateChangeEventArgs e ) { InvalidateRow( e.ViewNode ); if ( e.State ) { ScrollInView( e.ViewNode ); } } private void HandleNodesSorted( object sender, EventArgs e ) { UpdateTopNode(); InvalidateBelow( 0 ); ScrollSelectionInView(); } internal void InvalidateDropTargetRow( IViewNode node ) { int startY, endY; if ( GetRowBounds( node, out startY, out endY ) ) { // include possible space for insertion mark startY -= 3; if ( startY < 0 ) { startY = 0; } endY += 3; if ( endY > _scrollRange ) { endY = _scrollRange; } OnInvalidate( startY, endY ); } } internal void InvalidateRow( IViewNode node ) { int startY, endY; if ( GetRowBounds( node, out startY, out endY ) ) { OnInvalidate( startY, endY ); } } public void ScrollInView( IViewNode node ) { if ( _rowRenderer == null ) { return; } int curY = 0; IEnumerator enumerator = _visibleNodeCollection.GetFullEnumerator(); while( enumerator.MoveNext() ) { IViewNode itemNode = (IViewNode) enumerator.Current; int itemHeight = GetRowHeight( itemNode ); if ( itemNode == node ) { if ( curY < _scrollOffset ) { OnRequestVerticalScroll( curY ); } else if ( itemHeight > _visibleHeight && curY >= _scrollOffset + _visibleHeight ) { OnRequestVerticalScroll( curY ); } else if ( itemHeight < _visibleHeight && curY + itemHeight > _scrollOffset + _visibleHeight ) { OnRequestVerticalScroll( curY + itemHeight - _visibleHeight ); } return; } curY += itemHeight; } } private void OnRequestVerticalScroll( int y ) { if ( RequestVerticalScroll != null ) { RequestVerticalScroll( this, new RequestScrollEventArgs( y ) ); } } public IViewNode MoveByPage( IViewNode startNode, MoveDirection direction ) { lock( _nodeCollection ) { int movedHeight = 0; IEnumerator enumerator = _visibleNodeCollection.GetDirectionalEnumerator( startNode, direction ); IViewNode lastNode = null; while( enumerator.MoveNext() ) { int nextHeight = GetRowHeight( (IViewNode) enumerator.Current ); if ( movedHeight + nextHeight > VisibleHeight ) { break; } movedHeight += nextHeight; lastNode = (IViewNode) enumerator.Current; } if ( lastNode != null ) { return lastNode; } return startNode; } } internal void SetDropTarget(JetListViewNode row, DropTargetRenderMode mode) { bool bRepaint = ((row == _dropTargetRow) && (mode != _dropTargetRenderMode)); // If the row changes, it will be repainted automatically; if not, repaint if mode changes _dropTargetRenderMode = mode; SetDropTargetRow( row ); if(bRepaint) InvalidateDropTargetRow( row ); } internal void ClearDropTarget() { SetDropTargetRow( null ); } private void SetDropTargetRow( JetListViewNode node ) { if ( node != _dropTargetRow ) { if ( _dropTargetRow != null ) { InvalidateDropTargetRow( _dropTargetRow ); } _dropTargetRow = node; if ( _dropTargetRow != null ) { InvalidateDropTargetRow( _dropTargetRow ); } } } internal JetListViewNode EditedNode { get { return _inPlaceEditedNode; } set { if (_inPlaceEditedNode != null) { InvalidateRow( _inPlaceEditedNode ); } _inPlaceEditedNode = value; if (_inPlaceEditedNode != null) { InvalidateRow( _inPlaceEditedNode ); } } } } }