/// /// 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.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace JetBrains.JetListViewLibrary { /// /// The column which draws item lines and expand/collapse signs in JetListView. /// public class TreeStructureColumn: JetListViewColumn { // the width of the indent column is used as an indent step private const int _cColumnWidth = 17; private bool _showLines = true; private Pen _linePen; private int _indent; // Width is the width of the last indent step, while Indent is the width of all steps before the last one public TreeStructureColumn() { Width = _cColumnWidth; _linePen = new Pen( SystemColors.GrayText ); _linePen.DashStyle = DashStyle.Dot; _showHeader = false; } public override void Dispose() { if ( Owner.OwnerControl != null ) { Owner.OwnerControl.NodeCollection.NodeAdded -= HandleStructureChanged; Owner.OwnerControl.NodeCollection.NodeRemoved -= HandleStructureChanged; } base.Dispose(); } protected override void OnWidthChanged() { base.OnWidthChanged(); _indent = Width; } public int Indent { get { return _indent; } set { _indent = value; } } protected override void SetOwner( JetListViewColumnCollection value ) { base.SetOwner( value ); if ( value != null && value.OwnerControl != null ) { value.OwnerControl.NodeCollection.NodeAdded += HandleStructureChanged; value.OwnerControl.NodeCollection.NodeRemoved += HandleStructureChanged; } } /// /// Gets or sets a value indicating whether lines are drawn between tree nodes. /// public bool ShowLines { get { return _showLines; } set { _showLines = value; } } public override bool IsIndentColumn() { return true; } public override int GetIndent( JetListViewNode node ) { return node.Level * _indent + Width; } protected internal override void DrawNode( Graphics g, Rectangle rc, JetListViewNode node, RowState state, string highlightText ) { if ( Owner == null || OwnerControl == null ) return; Rectangle lastRect = new Rectangle( rc.Right - Width, rc.Top, Width, rc.Height ); Rectangle iconRect = lastRect; int midX = (lastRect.Left + lastRect.Right) / 2; int midY = (lastRect.Top + lastRect.Bottom) / 2; if ( _showLines && ( midY + OwnerControl.VScrollbar.Value ) % 2 == 1 ) { midY--; iconRect.Offset( 0, -1 ); } Size iconSize = OwnerControl.ControlPainter.GetTreeIconSize( g, lastRect ); if ( node.CollapseState != CollapseState.NoChildren ) { int left = midX - iconSize.Width/2; int top = midY - iconSize.Height/2; Rectangle rcIcon = new Rectangle( left, top, iconSize.Width, iconSize.Height ); DrawStructureIcon( g, iconRect, rcIcon, node ); } if ( _showLines ) { int lineTop = rc.Top; if ( ( lineTop + OwnerControl.VScrollbar.Value ) % 2 == 1 ) { lineTop++; } if ( node.CollapseState != CollapseState.NoChildren ) { if ( node.Level > 0 || node.PrevFilteredNode != null ) { g.DrawLine( _linePen, midX, lineTop, midX, midY - iconSize.Height/2 ); } g.DrawLine( _linePen, midX + iconSize.Width/2, midY, lastRect.Right, midY ); if ( node.NextFilteredNode != null ) { g.DrawLine( _linePen, midX, midY + iconSize.Height/2, midX, lastRect.Bottom ); } } else { if ( node.Level > 0 || node.PrevFilteredNode != null ) { g.DrawLine( _linePen, midX, lineTop, midX, midY ); } if ( node.NextFilteredNode != null ) { g.DrawLine( _linePen, midX, midY, midX, lastRect.Bottom ); } g.DrawLine( _linePen, midX, midY, lastRect.Right, midY ); } JetListViewNode curParent = node; for( int i=node.Level-1; i >= 0; i-- ) { curParent = curParent.Parent; midX -= Width; if ( curParent.NextFilteredNode != null ) { g.DrawLine( _linePen, midX, lineTop, midX, lastRect.Bottom ); } } } } protected virtual void DrawStructureIcon( Graphics g, Rectangle rc, Rectangle rcIcon, JetListViewNode node ) { OwnerControl.ControlPainter.DrawTreeIcon( g, rcIcon, ( node.CollapseState == CollapseState.Expanded ) ); } protected internal override MouseHandleResult HandleMouseDown( JetListViewNode node, int x, int y ) { int itemIndent = _indent * node.Level; if ( node.HasChildren && x >= itemIndent && x < itemIndent + Width ) { node.Expanded = !node.Expanded; return MouseHandleResult.Handled | MouseHandleResult.SuppressFocus; } return 0; } protected internal override bool HandleMouseUp( JetListViewNode node, int x, int y ) { // selection can be changed not only in mouse down handler, but also in mouse up (OM-8807) int itemIndent = _indent * node.Level; if ( node.HasChildren && x >= itemIndent && x < itemIndent + Width ) { return true; } return false; } public override bool AcceptColumnDoubleClick { get { return false; } } public override bool HandleDoubleClick( JetListViewNode node ) { if ( node.HasChildren ) { node.Expanded = !node.Expanded; return true; } return false; } protected internal override bool HandleKeyDown( JetListViewNode node, KeyEventArgs e ) { if ( node.HasChildren ) { if ( e.KeyData == Keys.Add ) { node.Expanded = true; return true; } if ( e.KeyData == Keys.Subtract ) { node.Expanded = false; return true; } if ( e.KeyData == Keys.Multiply ) { node.ExpandAll(); return true; } } if ( e.KeyData == Keys.Left ) { if ( node.HasChildren && node.Expanded ) { node.Expanded = false; return true; } else if ( node.Parent != null && node.Parent.Data != null ) { OwnerControl.Selection.Clear(); OwnerControl.Selection.Add( node.Parent.Data ); return true; } return false; } if ( e.KeyData == Keys.Right ) { if ( node.HasChildren ) { if ( !node.Expanded ) { node.Expanded = true; } else if ( node.ChildCount > 0 ) { OwnerControl.Selection.Clear(); OwnerControl.Selection.Add( node.GetChildNode( 0 ).Data ); } } return true; } return false; } public override string GetToolTip( JetListViewNode node, Rectangle rc, ref bool needPlace ) { return null; } private void HandleStructureChanged( object sender, JetListViewNodeEventArgs e ) { Owner.OwnerControl.Invalidate(); } } }