///
/// 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.ComponentModel;
using System.Drawing;
using System.Drawing.Text;
using System.Windows.Forms;
using JetBrains.UI.RichText;
namespace JetBrains.JetListViewLibrary
{
public enum SortIcon { None = -1, Ascending = 0, Descending = 1 };
///
/// A column in JetListView.
///
public class JetListViewColumn: IDisposable
{
///
///
///
public const int MinimumColumnWidth = 0;
private JetListViewColumnCollection _owner = null;
private int _width = MinimumColumnWidth;
private int _autoSizeMinWidth = 0;
private bool _autoSize = false;
private bool _sizeToContent = false;
private bool _handleAllClicks = false;
private int _leftMargin = 2;
private int _rightMargin = 2;
private bool _fixedSize = false;
///
///
///
private HorizontalAlignment _alignment = HorizontalAlignment.Left;
private StringAlignment _verticalAlignment = StringAlignment.Center;
private string _text;
private string _sortMenuText;
private string _sortMenuAscText = "Ascending";
private string _sortMenuDescText = "Descending";
private SortIcon _sortIcon = SortIcon.None;
#region Callback variables
private ItemColorCallback _foreColorCallback;
private ItemColorCallback _backColorCallback;
private ItemFontCallback _fontCallback;
private ItemTextCallback _itemTextCallback;
private ItemCursorCallback _itemCursorCallback;
protected ItemTextCallback _itemToolTipCallback;
protected bool _showHeader;
protected bool _noWrap = true;
private static FontCache _fontCache = new FontCache();
#endregion Callback variables
public JetListViewColumn()
{
_showHeader = true;
}
public virtual void Dispose()
{
}
public event EventHandler AutoSizeChanged;
public event EventHandler SizeToContentChanged;
public event EventHandler WidthChanged;
public event EventHandler SortIconChanged;
public event ItemMouseEventHandler MouseDown;
///
/// Occurs when the text of the column is changed.
///
public event EventHandler TextChanged;
protected internal JetListViewColumnCollection Owner
{
get { return _owner; }
set
{
SetOwner( value );
}
}
protected virtual void SetOwner( JetListViewColumnCollection value )
{
_owner = value;
}
public JetListView OwnerControl
{
get { return _owner.OwnerControl; }
}
#region Callback properties
///
/// The callback called to get the foreground color of an item.
///
public ItemColorCallback ForeColorCallback
{
get { return _foreColorCallback; }
set { _foreColorCallback = value; }
}
///
/// The callback called to get the background color of an item.
///
public ItemColorCallback BackColorCallback
{
get { return _backColorCallback; }
set { _backColorCallback = value; }
}
///
/// The callback called to get the font of an item.
///
public ItemFontCallback FontCallback
{
get { return _fontCallback; }
set { _fontCallback = value; }
}
///
/// The callback called to get the text of an item.
///
public ItemTextCallback ItemTextCallback
{
get { return _itemTextCallback; }
set { _itemTextCallback = value; }
}
///
/// The callback called to get the tooltip of an item.
///
public ItemTextCallback ItemToolTipCallback
{
get { return _itemToolTipCallback; }
set { _itemToolTipCallback = value; }
}
///
/// The callback called to get the cursor of an item.
///
public ItemCursorCallback CursorCallback
{
get { return _itemCursorCallback; }
set { _itemCursorCallback = value; }
}
#endregion Callback properties
public string Text
{
get { return _text; }
set
{
if ( _text != value )
{
_text = value;
OnTextChanged();
}
}
}
public string SortMenuText
{
get
{
if ( _sortMenuText == null )
{
return _text;
}
return _sortMenuText;
}
set { _sortMenuText = value; }
}
public string SortMenuAscText
{
get { return _sortMenuAscText; }
set { _sortMenuAscText = value; }
}
public string SortMenuDescText
{
get { return _sortMenuDescText; }
set { _sortMenuDescText = value; }
}
///
/// Width of the column, in pixels.
///
public int Width
{
get { return _width; }
set
{
if ( value != _width )
{
if (value >= MinimumColumnWidth)
{
_width = value;
OnWidthChanged();
}
else
throw new InvalidOperationException("Column width cannot be smaller than minimal value set in JetListViewColumn class");
}
}
}
///
/// The minimum width to which the column can be autosized.
///
public int AutoSizeMinWidth
{
get { return _autoSizeMinWidth; }
set { _autoSizeMinWidth = value; }
}
public SortIcon SortIcon
{
get { return _sortIcon; }
set
{
if ( _sortIcon != value )
{
_sortIcon = value;
OnSortIconChanged();
}
}
}
///
///
///
public bool AutoSize
{
get
{
return _autoSize;
}
set
{
if ( _autoSize != value )
{
_autoSize = value;
if (_autoSize)
_fixedSize = false;
if ( AutoSizeChanged != null )
{
AutoSizeChanged( this, EventArgs.Empty );
}
}
}
}
public bool SizeToContent
{
get { return _sizeToContent; }
set
{
if ( _sizeToContent != value )
{
_sizeToContent = value;
if( SizeToContentChanged != null )
{
SizeToContentChanged( this, EventArgs.Empty );
}
}
}
}
public int LeftMargin
{
get { return _leftMargin; }
set { _leftMargin = value; }
}
public int RightMargin
{
get { return _rightMargin; }
set { _rightMargin = value; }
}
public bool HandleAllClicks
{
get { return _handleAllClicks; }
set { _handleAllClicks = value; }
}
///
/// If true, the column gets its separate header. If false, the header of the column
/// is merged with the header of the column that follows it.
///
public bool ShowHeader
{
get { return _showHeader; }
set { _showHeader = value; }
}
///
///
///
public bool FixedSize
{
get
{
return _fixedSize;
}
set
{
_fixedSize = value;
if (_fixedSize)
_autoSize = false;
}
}
///
/// The alignment of text in the column.
///
public HorizontalAlignment Alignment
{
get { return _alignment; }
set { _alignment = value; }
}
///
/// The vertical alignment of text in the column.
///
[DefaultValue(StringAlignment.Center)]
public StringAlignment VerticalAlignment
{
get { return _verticalAlignment; }
set { _verticalAlignment = value; }
}
///
/// Returns the difference between the common width of the column and its width for
/// a specific node. Applies to fixed-size columns only.
///
/// The node for which the difference is returned.
/// The difference.
protected internal virtual int GetWidthDelta( JetListViewNode node )
{
return 0;
}
protected virtual Font GetItemFont( object item )
{
if ( FontCallback != null )
{
FontStyle fontStyle = FontCallback( item );
if ( fontStyle != FontStyle.Regular )
{
return _fontCache.GetFont( _owner.Font, fontStyle );
}
}
return _owner.Font;
}
protected virtual Color GetItemForeColor( object item )
{
if ( ForeColorCallback != null )
{
return ForeColorCallback( item );
}
return SystemColors.WindowText;
}
protected virtual Color GetItemBackColor( object item )
{
if ( BackColorCallback != null )
{
return BackColorCallback( item );
}
return SystemColors.Window;
}
public virtual Cursor GetItemCursor( object item )
{
if ( CursorCallback != null )
{
return CursorCallback( item );
}
return null;
}
protected internal virtual void DrawHeader( Graphics g, Rectangle bounds )
{
}
protected internal virtual void DrawNode( Graphics g, Rectangle rc, JetListViewNode node,
RowState state, string highlightText )
{
DrawItem( g, rc, node.Data, state, highlightText );
}
protected internal virtual void DrawItem( Graphics g, Rectangle rc, object item,
RowState state, string highlightText )
{
#region Preconditions
if( item == null )
{
throw new ArgumentNullException( "JetListViewColumn -- Source object is NULL." );
}
#endregion Preconditions
Rectangle rcText = new Rectangle( rc.Left + _leftMargin, rc.Top,
rc.Width - _leftMargin - _rightMargin, rc.Height );
Rectangle rcFocus = rc;
if ( _alignment == HorizontalAlignment.Left &&
( ( state & (RowState.ActiveSelected | RowState.InactiveSelected | RowState.Focused | RowState.DropTarget) ) != 0 ||
highlightText != null ) )
{
int textWidth = GetDesiredWidth( item );
if ( textWidth < Width )
{
rcFocus.Width = textWidth;
}
}
Color textColor = DrawItemBackground( g, rc, rcFocus, item, state, highlightText );
DrawItemText( g, rcText, item, textColor, state, highlightText );
if ( ( state & RowState.DropTarget ) != 0 )
{
DrawDropTarget( g, rcFocus );
}
else if ( ( state & RowState.Focused ) != 0 )
{
_owner.ControlPainter.DrawFocusRect( g, rcFocus );
}
}
internal static void DrawDropTarget( Graphics g, Rectangle rcFocus )
{
rcFocus.Width--;
rcFocus.Height -= 2;
rcFocus.Offset( 1, 1 );
g.DrawRectangle( Pens.DarkGray, rcFocus );
rcFocus.Offset( -1, -1 );
g.DrawRectangle( Pens.BlueViolet, rcFocus );
}
protected internal Color DrawItemBackground( Graphics g, Rectangle rc, Rectangle rcFocus,
object item, RowState state, string highlightText )
{
Color textColor = GetItemForeColor( item );
if ( ( state & RowState.Disabled ) != 0 )
{
textColor = SystemColors.GrayText;
}
else if ( highlightText != null )
{
Rectangle rcHlFocus = rcFocus;
if ( _alignment == HorizontalAlignment.Right )
{
rcHlFocus.Offset( -_rightMargin, 0 );
}
int deltaWidth = (_alignment == HorizontalAlignment.Left) ? _leftMargin : 0;
Rectangle rcHighlight, rcRest;
BuildHighlightRects( g, item, highlightText, rcHlFocus, deltaWidth, out rcHighlight, out rcRest );
g.FillRectangle( SystemBrushes.Highlight, rcHighlight );
g.FillRectangle( SystemBrushes.Control, rcRest );
}
else if ( ( state & RowState.ActiveSelected ) != 0 )
{
g.FillRectangle( SystemBrushes.Highlight, rcFocus );
textColor = SystemColors.HighlightText;
}
else if ( ( state & RowState.InactiveSelected ) != 0 )
{
g.FillRectangle( SystemBrushes.Control, rcFocus );
}
else
{
Color color = GetItemBackColor( item );
if ( color != SystemColors.Window )
{
using( Brush b = new SolidBrush( color ) )
{
g.FillRectangle( b, rc );
}
}
}
return textColor;
}
private void BuildHighlightRects( Graphics g, object item, string highlightText, Rectangle rc,
int deltaWidth, out Rectangle rcHighlight, out Rectangle rcRest )
{
int hlWidth = GetHighlightWidth( g, item, highlightText ) + deltaWidth;
if ( _alignment == HorizontalAlignment.Right )
{
int itemWidth = GetTextWidth( item );
rcHighlight = new Rectangle( rc.Right - itemWidth, rc.Top, hlWidth, rc.Height );
rcRest = new Rectangle( rc.Right - itemWidth + hlWidth, rc.Top, itemWidth - hlWidth, rc.Height );
}
else
{
rcHighlight = new Rectangle( rc.Left, rc.Top, hlWidth, rc.Height );
rcRest = new Rectangle( rc.Left + hlWidth, rc.Top, rc.Width - hlWidth, rc.Height );
}
}
public virtual int GetDesiredWidth( object item )
{
return GetTextWidth( item ) + _leftMargin + _rightMargin;
}
protected virtual int GetHighlightWidth( Graphics g, object item, string hlText )
{
// the casing of hlText and actual text found in the item may be different, and
// we need to measure the actual text
string itemText = GetItemText( item );
int index = itemText.ToLower().IndexOf( hlText.ToLower() );
if ( index != 0 )
{
return 0;
}
string itemHlText = itemText.Substring( index, hlText.Length );
return _owner.ControlPainter.MeasureText( g, itemHlText, GetItemFont( item ) ).Width;
}
protected internal virtual void DrawItemText( Graphics g, Rectangle rcText,
object item, Color textColor, RowState state, string highlightText )
{
string text = GetItemText( item, rcText.Width );
StringFormat fmt = GetColumnStringFormat();
Font itemFont = GetItemFont( item );
if ( highlightText != null && highlightText.Length <= text.Length )
{
Rectangle rcHighlight, rcRest;
BuildHighlightRects( g,item, highlightText, rcText, 0, out rcHighlight, out rcRest );
_owner.ControlPainter.DrawText( g, text.Substring( 0, highlightText.Length ),
itemFont, SystemColors.HighlightText, rcHighlight, fmt );
_owner.ControlPainter.DrawText( g, text.Substring( highlightText.Length ),
itemFont, textColor, rcRest, fmt );
}
else
{
_owner.ControlPainter.DrawText( g, text, itemFont, textColor, rcText, fmt );
}
}
protected StringFormat GetColumnStringFormat()
{
StringFormat fmt = new StringFormat( StringFormat.GenericDefault );
if ( _noWrap )
{
fmt.FormatFlags |= StringFormatFlags.NoWrap;
}
else
{
fmt.FormatFlags &= ~StringFormatFlags.NoWrap;
}
fmt.Alignment = GetHorizontalAlignment( Alignment );
fmt.LineAlignment = _verticalAlignment;
fmt.Trimming = StringTrimming.EllipsisCharacter;
fmt.HotkeyPrefix = HotkeyPrefix.None;
return fmt;
}
private StringAlignment GetHorizontalAlignment( HorizontalAlignment alignment )
{
switch( alignment )
{
case HorizontalAlignment.Center: return StringAlignment.Center;
case HorizontalAlignment.Right: return StringAlignment.Far;
default: return StringAlignment.Near;
}
}
protected virtual int GetTextWidth( object item )
{
return GetTextWidth( item, Width );
}
protected virtual int GetTextWidth( object item, int baseWidth )
{
Font itemFont = GetItemFont( item );
return _owner.ControlPainter.MeasureText( GetItemText( item, baseWidth ), itemFont ).Width;
}
protected internal virtual string GetItemText( object item )
{
if ( _itemTextCallback != null )
{
return _itemTextCallback( item );
}
return item.ToString();
}
protected internal virtual string GetItemText( object item, int width )
{
return GetItemText( item );
}
protected internal virtual MouseHandleResult HandleMouseDown( JetListViewNode node, int x, int y )
{
MouseHandleResult result = 0;
if ( MouseDown != null )
{
ItemMouseEventArgs args = new ItemMouseEventArgs( node.Data, x, y );
MouseDown( this, args );
if( args.Handled )
{
result |= MouseHandleResult.Handled;
}
}
return result;
}
protected internal virtual bool HandleMouseUp( JetListViewNode node, int x, int y )
{
return false;
}
protected internal virtual bool HandleKeyDown( JetListViewNode node, KeyEventArgs e )
{
return false;
}
protected internal virtual bool HandleContextMenu( JetListViewNode node, int x, int y )
{
return false;
}
protected internal virtual void HandleDragHover( JetListViewNode node )
{
node.Expanded = true;
}
public virtual bool IsIndentColumn()
{
return false;
}
public virtual int GetIndent( JetListViewNode node )
{
return 0;
}
protected internal virtual void UpdateItem( object item )
{
}
public virtual bool MatchIncrementalSearch( JetListViewNode node, string text )
{
string matchText = GetItemText( node.Data ).ToLower();
return matchText.StartsWith( text.ToLower() );
}
public virtual string GetToolTip( JetListViewNode node, Rectangle rc, ref bool needPlace )
{
string toolTip = null;
if ( _itemToolTipCallback != null )
{
toolTip = _itemToolTipCallback( node.Data );
}
if ( OwnerControl != null && OwnerControl.AutoToolTips && GetTextWidth( node.Data, rc.Width ) > rc.Width )
{
if ( toolTip == null || toolTip.Length == 0 )
{
toolTip = GetItemText( node.Data, rc.Width );
}
else
{
toolTip = GetItemText( node.Data, rc.Width ) + " (" + toolTip + ")";
}
}
else
{
needPlace = false;
}
return toolTip;
}
public virtual bool AcceptColumnDoubleClick
{
get { return true; }
}
public virtual bool HandleDoubleClick( JetListViewNode node )
{
return false;
}
protected virtual void OnWidthChanged()
{
if ( WidthChanged != null )
{
WidthChanged( this, EventArgs.Empty );
}
}
private void OnSortIconChanged()
{
if ( SortIconChanged != null )
{
SortIconChanged( this, EventArgs.Empty );
}
}
private void OnTextChanged()
{
if ( TextChanged != null )
{
TextChanged( this, EventArgs.Empty );
}
}
public override string ToString()
{
return GetType().Name + " Text='" + Text + "' Width=" + Width;
}
}
}