///
/// 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.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using JetBrains.Interop.WinApi;
using JetBrains.Omea.GUIControls.CommandBar;
using JetBrains.UI.Interop;
namespace JetBrains.Omea.GUIControls
{
///
/// A bar of tabs.
///
public class TabBar: Control, ICommandBar
{
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
private class TabBarTab
{
private readonly string _text;
private readonly object _tag;
public TabBarTab( string text, object tag )
{
_text = text;
_tag = tag;
}
public object Tag
{
get { return _tag; }
}
public string Text
{
get { return _text; }
}
public int Width { get; set; }
public int PreferredWidth { get; set; }
}
private readonly ArrayList _tabs = new ArrayList();
private int _activeTabIndex = -1;
private IntPtr _fontHandle;
private ColorScheme _colorScheme;
///
/// Scaling factor for the component.
///
protected SizeF _sizeScale = new SizeF(1, 1);
///
/// Command bar site.
///
private ICommandBarSite _site;
public event EventHandler SelectedIndexChanged;
public TabBar()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
SetStyle( ControlStyles.AllPaintingInWmPaint
| ControlStyles.CacheText
| ControlStyles.ResizeRedraw
| ControlStyles.UserPaint
| ControlStyles.Opaque
| ControlStyles.DoubleBuffer
, true );
SetStyle( ControlStyles.Selectable
| ControlStyles.ContainerControl
| ControlStyles.SupportsTransparentBackColor
, false );
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
Win32Declarations.DeleteObject( _fontHandle );
base.Dispose( disposing );
}
#region Component Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
[DefaultValue(null)]
public ColorScheme ColorScheme
{
get { return _colorScheme; }
set
{
if ( _colorScheme != value )
{
_colorScheme = value;
Invalidate();
}
}
}
private void CreateFontHandle()
{
if ( _fontHandle != IntPtr.Zero )
{
Win32Declarations.DeleteObject( _fontHandle );
_fontHandle = IntPtr.Zero;
}
int logPixY;
using( Graphics g = CreateGraphics() )
{
IntPtr hdc = g.GetHdc();
logPixY = Win32Declarations.GetDeviceCaps( hdc, Win32Declarations.LOGPIXELSY );
g.ReleaseHdc( hdc );
}
_fontHandle = Win32Declarations.CreateFont( (int) (-Font.SizeInPoints * logPixY / 72),
0, 0, 0, Win32Declarations.FW_NORMAL, 0, 0, 0, 0, 0, 0, 0, 0, Font.Name );
}
public void AddTab( string text, object tag )
{
InsertTab( _tabs.Count, text, tag );
}
public void InsertTab( int index, string text, object tag )
{
TabBarTab newTab = new TabBarTab( text, tag );
_tabs.Insert( index, newTab );
if ( _tabs.Count == 1 )
{
_activeTabIndex = 0;
}
else if ( _activeTabIndex >= index )
{
_activeTabIndex++;
}
CalcPreferredWidth( newTab );
newTab.Width = newTab.PreferredWidth;
// Request relayouting
if(_site != null) // Needed?
_site.PerformLayout( this );
Invalidate();
}
protected override void OnHandleCreated( EventArgs e )
{
base.OnHandleCreated( e );
CreateFontHandle();
UpdatePreferredWidth();
UpdateTabWidth();
}
private void UpdatePreferredWidth()
{
foreach( TabBarTab tab in _tabs )
{
CalcPreferredWidth( tab );
}
}
///
/// Calculates the preferred width on a per-tab basis.
///
private void CalcPreferredWidth( TabBarTab tab )
{
IntPtr hdc = (IntPtr) 0;
try
{
if( IsHandleCreated )
hdc = Win32Declarations.GetDC( Handle );
else
hdc = Win32Declarations.GetDC( (IntPtr) 0 );
IntPtr oldFont = Win32Declarations.SelectObject( hdc, _fontHandle );
Win32Declarations.SelectObject( hdc, oldFont );
SIZE sz = new SIZE();
Win32Declarations.GetTextExtentPoint32( hdc, tab.Text, tab.Text.Length, ref sz );
int prefWidth = sz.cx + 2 * 8;
tab.PreferredWidth = Math.Max( 50, prefWidth );
tab.Width = tab.PreferredWidth;
}
finally
{
if( hdc != (IntPtr) 0 )
{
Win32Declarations.ReleaseDC( Handle, hdc );
hdc = (IntPtr) 0;
}
}
}
public int TabCount
{
get { return _tabs.Count; }
}
public int SelectedIndex
{
get
{
return _activeTabIndex;
}
set
{
if ( _activeTabIndex != value )
{
if ( value < 0 || value >= _tabs.Count )
{
throw new ArgumentOutOfRangeException( "value", value, "Tab index out of range" );
}
InvalidateTab( _activeTabIndex );
_activeTabIndex = value;
InvalidateTab( _activeTabIndex );
if ( SelectedIndexChanged != null )
{
SelectedIndexChanged( this, EventArgs.Empty );
}
// Request relayouting
if(_site != null) // Needed?
_site.PerformLayout( this );
}
}
}
public object SelectedTabTag
{
get
{
if ( _activeTabIndex < 0 )
return null;
return ((TabBarTab) _tabs [_activeTabIndex]).Tag;
}
}
public object GetTabTag( int tabIndex )
{
return ((TabBarTab) _tabs [tabIndex]).Tag;
}
public string GetTabText( int tabIndex )
{
return ((TabBarTab) _tabs [tabIndex]).Text;
}
protected override void OnFontChanged( EventArgs e )
{
base.OnFontChanged( e );
if ( IsHandleCreated )
{
CreateFontHandle();
UpdatePreferredWidth();
}
}
protected override void OnPaint( PaintEventArgs e )
{
e.Graphics.FillRectangle( SystemBrushes.Control, ClientRectangle );
for( int i = 0; i < _tabs.Count; i++ )
{
Rectangle rc = GetTabRect( i );
if( e.ClipRectangle.IntersectsWith( rc ) )
{
DrawTab( e.Graphics, i );
}
}
}
private void DrawTab( Graphics g, int index )
{
Rectangle rc = GetTabRect( index );
if ( rc.IsEmpty )
return;
GraphicsPath gp = BuildBorderPath( rc );
using( gp )
{
if ( index == _activeTabIndex )
{
g.FillPath( GUIControls.ColorScheme.GetBrush( _colorScheme, "ResourceTypeTabs.ActiveBackground",
rc, SystemBrushes.Control ), gp );
}
else
{
g.FillPath( GUIControls.ColorScheme.GetBrush( _colorScheme, "ResourceTypeTabs.InactiveBackground",
rc, SystemBrushes.Control ), gp );
Rectangle rcGradient = new Rectangle( rc.Left, rc.Bottom-6, rc.Width, 6 );
g.FillRectangle( GUIControls.ColorScheme.GetBrush( _colorScheme, "ResourceTypeTabs.InactiveBackgroundBottom",
rcGradient, SystemBrushes.Control ), rcGradient );
}
g.DrawPath( GUIControls.ColorScheme.GetPen( _colorScheme,
(index == _activeTabIndex) ? "PaneCaption.Border" : "ResourceTypeTabs.Border",
Pens.Black ), gp );
}
string tabText = ((TabBarTab) _tabs [index]).Text;
IntPtr hdc = g.GetHdc();
try
{
Color clrText = (index == _activeTabIndex)
? GUIControls.ColorScheme.GetColor( _colorScheme, "ResourceTypeTabs.ActiveText", Color.Black )
: GUIControls.ColorScheme.GetColor( _colorScheme, "ResourceTypeTabs.InactiveText", Color.Black );
int oldColor = Win32Declarations.SetTextColor( hdc, ColorTranslator.ToWin32( clrText ) );
IntPtr oldFont = Win32Declarations.SelectObject( hdc, _fontHandle );
BackgroundMode oldBkMode = Win32Declarations.SetBkMode( hdc, BackgroundMode.TRANSPARENT );
RECT rect = Win32Declarations.RectangleToRECT( rc );
Win32Declarations.DrawText( hdc, tabText, tabText.Length, ref rect,
DrawTextFormatFlags.DT_CENTER | DrawTextFormatFlags.DT_VCENTER | DrawTextFormatFlags.DT_NOPREFIX |
DrawTextFormatFlags.DT_END_ELLIPSIS | DrawTextFormatFlags.DT_SINGLELINE );
Win32Declarations.SetBkMode( hdc, oldBkMode );
Win32Declarations.SelectObject( hdc, oldFont );
Win32Declarations.SetTextColor( hdc, oldColor );
}
finally
{
g.ReleaseHdc( hdc );
}
}
public Rectangle GetTabRect( int index )
{
int left = 0;
for( int i=0; i= 0 )
{
Invalidate( GetTabRect( tabIndex ) );
}
}
protected override void OnMouseDown( MouseEventArgs e )
{
base.OnMouseDown( e );
if ( e.Button == MouseButtons.Left )
{
int tabIndex = GetTabAt( e.X, e.Y );
if ( tabIndex >= 0 )
{
SelectedIndex = tabIndex;
}
}
}
private int GetTabAt( int x, int y )
{
for( int i=0; i<_tabs.Count; i++ )
{
Rectangle rc = GetTabRect( i );
if ( rc.Contains( x, y ) )
{
return i;
}
}
return -1;
}
///
/// When the size of the tab control changes, reduces the tab width if necessary.
///
protected override void OnLayout( LayoutEventArgs levent )
{
base.OnLayout( levent );
if ( levent.AffectedControl != null && levent.AffectedProperty != null )
{
if ( levent.AffectedProperty.ToString() == "Bounds" )
{
Form frm = FindForm();
if ( frm != null && frm.WindowState != FormWindowState.Minimized )
{
UpdateTabWidth();
}
}
}
}
private void UpdateTabWidth()
{
int availWidth = ClientSize.Width - (_tabs.Count + 1) * 2; // leave space for tab borders
int totalWidth = OptimalWidth;
if ( totalWidth == 0 )
return;
double availRatio = (double) availWidth / (double) totalWidth;
//if ( availRatio > 1.0 )
// availRatio = 1.0;
bool needInvalidate = false;
// if we don't have enough space, shrink all tabs proportionally
foreach( TabBarTab tab in _tabs )
{
int tabWidth = (int) (tab.PreferredWidth * availRatio );
if ( tabWidth != tab.Width )
{
tab.Width = tabWidth;
needInvalidate = true;
}
}
if ( needInvalidate )
{
Invalidate();
}
}
///
/// Scales the control.
///
protected override void ScaleCore(float dx, float dy)
{
_sizeScale = new SizeF(dx, dy);
base.ScaleCore (dx, dy);
}
///
/// Optimal width of the whole control the tabs would like to occupy.
///
private int OptimalWidth
{
get
{
int totalWidth = 0;
foreach( TabBarTab tab in _tabs )
totalWidth += tab.PreferredWidth;
return totalWidth;
}
}
#region ICommandBar Interface Members
public void SetSite( ICommandBarSite site )
{
_site = site;
//if(_site != null)
// _site.PerformLayout(this);
// TODO: ???
}
public Size MinSize
{
get { return new Size(100, (int) (15 * _sizeScale.Height)); }
}
public Size MaxSize
{
get { return new Size(OptimalWidth, int.MaxValue); }
}
public Size OptimalSize
{
get { return new Size(OptimalWidth, (int) (27 * _sizeScale.Height)); }
}
public Size Integral
{
get { return new Size(1, 1); }
}
#endregion
}
}