/// /// 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.Drawing; using System.Windows.Forms; using JetBrains.DataStructures; using SP.Windows; namespace JetBrains.JetListViewLibrary { /// /// Draws JetListView rows with a different multi-line column layout in each row. /// internal class MultiLineRowRenderer: RowRendererBase { private IColumnSchemeProvider _columnSchemeProvider; private int _visibleWidth; private HeaderSection _arrangeByHeaderSection; private HeaderSection _sortOrderHeaderSection; private JetListViewColumn _sortColumn; private Color _lastTextColor; private ItemColorCallback _lastItemColorCallback; private ItemFontCallback _lastItemFontCallback; private int _topMargin = 0; public MultiLineRowRenderer( JetListViewColumnCollection columnCollection ) : base( columnCollection ) { } public IColumnSchemeProvider ColumnSchemeProvider { get { return _columnSchemeProvider; } set { _columnSchemeProvider = value; } } public int TopMargin { get { return _topMargin; } set { _topMargin = value; } } public override int VisibleWidth { get { return _visibleWidth; } set { if ( _visibleWidth != value ) { _visibleWidth = value; if ( _arrangeByHeaderSection != null ) { _arrangeByHeaderSection.Width = Math.Max( 50, _visibleWidth - 100 ); } OnInvalidate(); } } } public override void DrawRow( Graphics g, Rectangle rc, JetListViewNode itemNode, RowState state ) { MultiLineColumnScheme scheme = _columnSchemeProvider.GetColumnScheme( itemNode.Data ); int indent = GetRowIndent( itemNode, scheme ); Rectangle rcFocus = GetFocusRect( itemNode, rc ); bool focusRow = false, dropTargetRow = false; if ( ( (state & RowState.Focused) != 0 && _searchHighlightText != null && _searchHighlightText.Length > 0) ) { state |= RowState.IncSearchMatch; } if ( _fullRowSelect ) { FillFullRowSelectBar( g, rcFocus, state ); } ClearRowSelectState( ref state, ref focusRow, ref dropTargetRow ); foreach( MultiLineColumnSetting setting in scheme.ColumnSettings ) { if ( _fullRowSelect ) { if ( (state & RowState.IncSearchMatch) != 0 && (state & RowState.ActiveSelected) != 0 ) { state &= ~RowState.ActiveSelected; state |= RowState.InactiveSelected; } } _lastTextColor = setting.TextColor; _lastItemColorCallback = setting.Column.ForeColorCallback; _lastItemFontCallback = setting.Column.FontCallback; if ( ( state & (RowState.ActiveSelected | RowState.InactiveSelected) ) == 0 ) { setting.Column.ForeColorCallback = new ItemColorCallback( GetColumnForeColor ); } HorizontalAlignment lastAlignment = setting.Column.Alignment; int oldMargin = setting.Column.RightMargin; setting.Column.Alignment = setting.TextAlign; if ( setting.TextAlign == HorizontalAlignment.Right ) { setting.Column.RightMargin = 5; } Rectangle rcCol = GetRectangleFromSetting( scheme, setting, indent ); rcCol.Offset( _borderSize, rc.Top ); DrawColumnWithHighlight( g, rcCol, itemNode, setting.Column, state ); setting.Column.ForeColorCallback = _lastItemColorCallback; setting.Column.Alignment = lastAlignment; setting.Column.RightMargin = oldMargin; } DrawRowSelectRect( g, rcFocus, dropTargetRow, focusRow ); } protected internal override Rectangle GetFocusRect( JetListViewNode itemNode, Rectangle rc ) { MultiLineColumnScheme scheme = _columnSchemeProvider.GetColumnScheme( itemNode.Data ); int indent = GetRowIndent( itemNode, scheme ); return new Rectangle( _borderSize + indent, rc.Top, _visibleWidth - _borderSize - indent, rc.Height ); } private Color GetColumnForeColor( object item ) { if ( _lastItemColorCallback != null && _lastItemFontCallback != null ) { Color callbackColor = _lastItemColorCallback( item ); FontStyle callbackFont = _lastItemFontCallback( item ); if ( callbackColor.ToArgb() != SystemColors.ControlText.ToArgb() || callbackFont != FontStyle.Regular ) { return callbackColor; } } return _lastTextColor; } private Rectangle GetRectangleFromSetting( MultiLineColumnScheme scheme, MultiLineColumnSetting setting, int indent ) { if ( setting.Column.IsIndentColumn() ) { return new Rectangle( 0, _topMargin, indent, _rowHeight ); } int baseWidth = scheme.BaseWidth; int deltaWidth = _visibleWidth - baseWidth - indent; int startX = setting.StartX + indent; int width = setting.Width; if ( ( setting.Anchor & ColumnAnchor.Left ) == 0 ) { startX += deltaWidth; } if ( (setting.Anchor & (ColumnAnchor.Left | ColumnAnchor.Right) ) == (ColumnAnchor.Left | ColumnAnchor.Right) ) { width += deltaWidth; } return new Rectangle( startX, _topMargin + setting.StartRow * _rowHeight, width, ( setting.EndRow - setting.StartRow + 1 ) * _rowHeight); } private int GetRowIndent( JetListViewNode node, MultiLineColumnScheme scheme ) { if ( !node.HasChildren && node.Level == 0 && !scheme.AlignTopLevelItems ) { return 0; } foreach( JetListViewColumn col in scheme.Columns ) { if ( col.IsIndentColumn() ) { return col.GetIndent( node ); } } return 0; } public override int GetRowHeight( JetListViewNode node ) { MultiLineColumnScheme scheme = _columnSchemeProvider.GetColumnScheme( node.Data ); return scheme.RowCount * _rowHeight + _topMargin; } public override int AllRowsHeight { get { return -1; } } public override void UpdateItem( object item ) { MultiLineColumnScheme scheme = _columnSchemeProvider.GetColumnScheme( item ); foreach( MultiLineColumnSetting setting in scheme.ColumnSettings ) { setting.Column.UpdateItem( item ); } } public override Rectangle GetColumnBounds( JetListViewColumn col, JetListViewNode node ) { MultiLineColumnScheme scheme = _columnSchemeProvider.GetColumnScheme( node.Data ); foreach( MultiLineColumnSetting setting in scheme.ColumnSettings ) { if ( setting.Column == col ) { int indent = GetRowIndent( node, scheme ); return GetRectangleFromSetting( scheme, setting, indent ); } } throw new Exception( "The specified column is not found in the column scheme for the specified node" ); } protected override JetListViewColumn GetColumnAndDelta( JetListViewNode node, int x, int y, out int deltaX, out int deltaY ) { MultiLineColumnScheme scheme = _columnSchemeProvider.GetColumnScheme( node.Data ); foreach( MultiLineColumnSetting setting in scheme.ColumnSettings ) { int indent = GetRowIndent( node, scheme ); Rectangle rcCol = GetRectangleFromSetting( scheme, setting, indent ); if ( rcCol.Contains( x, y ) ) { deltaX = x - rcCol.Left; deltaY = y - rcCol.Top; return setting.Column; } } deltaX = 0; deltaY = 0; return null; } protected override IEnumerable GetColumnEnumerable( JetListViewNode node ) { MultiLineColumnScheme scheme = _columnSchemeProvider.GetColumnScheme( node.Data ); return scheme.Columns; } public override void SizeColumnsToContent( HashSet addedNodes, HashSet removedNodes, HashSet changedNodes ) { } public override void ProcessNodeExpanded( JetListViewNode node ) { } public override void ProcessNodeCollapsed( JetListViewNode node ) { } protected override void HookHeaderControl() { _headerControl.Sections.Clear(); _arrangeByHeaderSection = new HeaderSection( "Arranged By: ", Math.Max( 50, _visibleWidth - 100 ) ); _headerControl.Sections.Add( _arrangeByHeaderSection ); _sortOrderHeaderSection = new HeaderSection( "Sort", 100 ); _headerControl.Sections.Add( _sortOrderHeaderSection ); UpdateSortColumn( null ); _headerControl.SectionClick += new HeaderSectionEventHandler( HandleSectionClick ); _headerControl.BeforeSectionTrack += new HeaderSectionWidthConformableEventHandler( HandleBeforeSectionTrack ); _headerControl.BeforeSectionDrag += new HeaderSectionOrderConformableEventHandler( HandleBeforeSectionDrag ); } protected override void UnhookHeaderControl() { _headerControl.Sections.Clear(); _arrangeByHeaderSection = null; _sortOrderHeaderSection = null; _headerControl.SectionClick -= new HeaderSectionEventHandler( HandleSectionClick ); _headerControl.BeforeSectionTrack -= new HeaderSectionWidthConformableEventHandler( HandleBeforeSectionTrack ); _headerControl.BeforeSectionDrag -= new HeaderSectionOrderConformableEventHandler( HandleBeforeSectionDrag ); } protected override void HandleSortIconChanged( object sender, EventArgs e ) { UpdateSortColumn( null ); } protected override void HandleColumnRemoved( object sender, ColumnEventArgs e ) { base.HandleColumnRemoved( sender, e ); if ( e.Column.SortIcon != SortIcon.None ) { UpdateSortColumn( e.Column ); } } private void UpdateSortColumn( JetListViewColumn exceptColumn ) { bool foundSortColumn = false; foreach( JetListViewColumn col in _columnCollection ) { if ( col.SortIcon != SortIcon.None && col != exceptColumn ) { _sortColumn = col; _arrangeByHeaderSection.Text = "Arranged By: " + col.SortMenuText; _sortOrderHeaderSection.Text = (col.SortIcon == SortIcon.Ascending) ? col.SortMenuAscText : col.SortMenuDescText; foundSortColumn = true; break; } } if ( !foundSortColumn ) { _arrangeByHeaderSection.Text = "Arranged By:"; _sortOrderHeaderSection.Text = ""; } } private void HandleSectionClick( object sender, HeaderSectionEventArgs ea ) { if ( ea.Item == _arrangeByHeaderSection ) { ShowSortContextMenu(); } else if ( ea.Item == _sortOrderHeaderSection ) { OnColumnClick( _sortColumn ); } } private void ShowSortContextMenu() { ContextMenu menu = new ContextMenu(); foreach( JetListViewColumn col in _columnCollection ) { if ( col.ShowHeader && col.SortMenuText != null && col.SortMenuText.Length > 0 ) { MenuItem item = menu.MenuItems.Add( col.SortMenuText, new EventHandler( HandleSortContextMenuClick ) ); if ( col == _sortColumn ) { item.Checked = true; } } } menu.Show( _headerControl, _headerControl.PointToClient( Cursor.Position ) ); } private void HandleSortContextMenuClick( object sender, EventArgs e ) { MenuItem item = (MenuItem) sender; foreach( JetListViewColumn col in _columnCollection ) { if ( col.ShowHeader && col.SortMenuText == item.Text ) { if ( col != _sortColumn ) { OnColumnClick( col ); } } } } private void HandleBeforeSectionTrack( object sender, HeaderSectionWidthConformableEventArgs ea ) { ea.Accepted = false; } private void HandleBeforeSectionDrag( object sender, HeaderSectionOrderConformableEventArgs ea ) { ea.Accepted = false; } } }