468663ddbb
Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
2014 lines
50 KiB
C#
2014 lines
50 KiB
C#
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// Copyright (c) 2004-2005 Novell, Inc.
|
|
//
|
|
// Authors:
|
|
// Jackson Harper (jackson@ximian.com)
|
|
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Drawing;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Forms.Theming;
|
|
using System.Windows.Forms.VisualStyles;
|
|
|
|
namespace System.Windows.Forms {
|
|
[ComVisibleAttribute (true)]
|
|
[ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
|
|
[DefaultEvent("SelectedIndexChanged")]
|
|
[DefaultProperty("TabPages")]
|
|
[Designer("System.Windows.Forms.Design.TabControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
|
|
public class TabControl : Control {
|
|
#region Fields
|
|
private int selected_index = -1;
|
|
private TabAlignment alignment;
|
|
private TabAppearance appearance;
|
|
private TabDrawMode draw_mode;
|
|
private bool multiline;
|
|
private ImageList image_list;
|
|
private Size item_size = Size.Empty;
|
|
private bool item_size_manual;
|
|
private Point padding;
|
|
private int row_count = 0;
|
|
private bool hottrack;
|
|
private TabPageCollection tab_pages;
|
|
private bool show_tool_tips;
|
|
private TabSizeMode size_mode;
|
|
private bool show_slider = false;
|
|
private PushButtonState right_slider_state = PushButtonState.Normal;
|
|
private PushButtonState left_slider_state = PushButtonState.Normal;
|
|
private int slider_pos = 0;
|
|
TabPage entered_tab_page;
|
|
bool mouse_down_on_a_tab_page;
|
|
ToolTip tooltip;
|
|
ToolTip.TipState tooltip_state = ToolTip.TipState.Down;
|
|
Timer tooltip_timer;
|
|
|
|
private bool rightToLeftLayout;
|
|
#endregion // Fields
|
|
|
|
#region UIA Framework Events
|
|
static object UIAHorizontallyScrollableChangedEvent = new object ();
|
|
|
|
internal event EventHandler UIAHorizontallyScrollableChanged {
|
|
add { Events.AddHandler (UIAHorizontallyScrollableChangedEvent, value); }
|
|
remove { Events.RemoveHandler (UIAHorizontallyScrollableChangedEvent, value); }
|
|
}
|
|
|
|
internal void OnUIAHorizontallyScrollableChanged (EventArgs e)
|
|
{
|
|
EventHandler eh
|
|
= (EventHandler) Events [UIAHorizontallyScrollableChangedEvent];
|
|
if (eh != null)
|
|
eh (this, e);
|
|
}
|
|
|
|
static object UIAHorizontallyScrolledEvent = new object ();
|
|
|
|
internal event EventHandler UIAHorizontallyScrolled {
|
|
add { Events.AddHandler (UIAHorizontallyScrolledEvent, value); }
|
|
remove { Events.RemoveHandler (UIAHorizontallyScrolledEvent, value); }
|
|
}
|
|
|
|
internal void OnUIAHorizontallyScrolled (EventArgs e)
|
|
{
|
|
EventHandler eh
|
|
= (EventHandler) Events [UIAHorizontallyScrolledEvent];
|
|
if (eh != null)
|
|
eh (this, e);
|
|
}
|
|
#endregion
|
|
|
|
#region UIA Framework Property
|
|
internal double UIAHorizontalViewSize {
|
|
get { return LeftScrollButtonArea.Left * 100 / TabPages [TabCount - 1].TabBounds.Right; }
|
|
}
|
|
#endregion
|
|
|
|
#region Public Constructors
|
|
public TabControl ()
|
|
{
|
|
tab_pages = new TabPageCollection (this);
|
|
SetStyle (ControlStyles.UserPaint, false);
|
|
padding = ThemeEngine.Current.TabControlDefaultPadding;
|
|
|
|
MouseDown += new MouseEventHandler (MouseDownHandler);
|
|
MouseLeave += new EventHandler (OnMouseLeave);
|
|
MouseMove += new MouseEventHandler (OnMouseMove);
|
|
MouseUp += new MouseEventHandler (MouseUpHandler);
|
|
SizeChanged += new EventHandler (SizeChangedHandler);
|
|
}
|
|
|
|
#endregion // Public Constructors
|
|
|
|
#region Public Instance Properties
|
|
[DefaultValue(TabAlignment.Top)]
|
|
[Localizable(true)]
|
|
[RefreshProperties(RefreshProperties.All)]
|
|
public TabAlignment Alignment {
|
|
get { return alignment; }
|
|
set {
|
|
if (alignment == value)
|
|
return;
|
|
alignment = value;
|
|
if (alignment == TabAlignment.Left || alignment == TabAlignment.Right)
|
|
multiline = true;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[DefaultValue(TabAppearance.Normal)]
|
|
[Localizable(true)]
|
|
public TabAppearance Appearance {
|
|
get { return appearance; }
|
|
set {
|
|
if (appearance == value)
|
|
return;
|
|
appearance = value;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override Color BackColor {
|
|
get { return ThemeEngine.Current.ColorControl; }
|
|
set { /* nothing happens on set on MS */ }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override Image BackgroundImage {
|
|
get { return base.BackgroundImage; }
|
|
set { base.BackgroundImage = value; }
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public override ImageLayout BackgroundImageLayout {
|
|
get { return base.BackgroundImageLayout; }
|
|
set { base.BackgroundImageLayout = value; }
|
|
}
|
|
|
|
public override Rectangle DisplayRectangle {
|
|
get {
|
|
return ThemeEngine.Current.TabControlGetDisplayRectangle (this);
|
|
}
|
|
}
|
|
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
protected override bool DoubleBuffered {
|
|
get { return base.DoubleBuffered; }
|
|
set { base.DoubleBuffered = value; }
|
|
}
|
|
|
|
[DefaultValue(TabDrawMode.Normal)]
|
|
public TabDrawMode DrawMode {
|
|
get { return draw_mode; }
|
|
set {
|
|
if (draw_mode == value)
|
|
return;
|
|
draw_mode = value;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override Color ForeColor {
|
|
get { return base.ForeColor; }
|
|
set { base.ForeColor = value; }
|
|
}
|
|
|
|
[DefaultValue(false)]
|
|
public bool HotTrack {
|
|
get { return hottrack; }
|
|
set {
|
|
if (hottrack == value)
|
|
return;
|
|
hottrack = value;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[RefreshProperties (RefreshProperties.Repaint)]
|
|
[DefaultValue(null)]
|
|
public ImageList ImageList {
|
|
get { return image_list; }
|
|
set {
|
|
image_list = value;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[Localizable(true)]
|
|
public Size ItemSize {
|
|
get {
|
|
if (item_size_manual)
|
|
return item_size;
|
|
|
|
if (!IsHandleCreated)
|
|
return Size.Empty;
|
|
|
|
Size size = item_size;
|
|
if (SizeMode != TabSizeMode.Fixed) {
|
|
size.Width += padding.X * 2;
|
|
size.Height += padding.Y * 2;
|
|
}
|
|
|
|
if (tab_pages.Count == 0)
|
|
size.Width = 0;
|
|
|
|
return size;
|
|
}
|
|
set {
|
|
if (value.Height < 0 || value.Width < 0)
|
|
throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
|
|
item_size = value;
|
|
item_size_manual = true;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[DefaultValue(false)]
|
|
public bool Multiline {
|
|
get { return multiline; }
|
|
set {
|
|
if (multiline == value)
|
|
return;
|
|
multiline = value;
|
|
if (!multiline && alignment == TabAlignment.Left || alignment == TabAlignment.Right)
|
|
alignment = TabAlignment.Top;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[Localizable(true)]
|
|
public
|
|
new
|
|
Point Padding {
|
|
get { return padding; }
|
|
set {
|
|
if (value.X < 0 || value.Y < 0)
|
|
throw new ArgumentException ("'" + value + "' is not a valid value for 'Padding'.");
|
|
if (padding == value)
|
|
return;
|
|
padding = value;
|
|
Redraw ();
|
|
}
|
|
|
|
}
|
|
|
|
[MonoTODO ("RTL not supported")]
|
|
[Localizable (true)]
|
|
[DefaultValue (false)]
|
|
public virtual bool RightToLeftLayout {
|
|
get { return this.rightToLeftLayout; }
|
|
set {
|
|
if (value != this.rightToLeftLayout) {
|
|
this.rightToLeftLayout = value;
|
|
this.OnRightToLeftLayoutChanged (EventArgs.Empty);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public int RowCount {
|
|
get { return row_count; }
|
|
}
|
|
|
|
[DefaultValue(-1)]
|
|
[Browsable(false)]
|
|
public int SelectedIndex {
|
|
get { return selected_index; }
|
|
set {
|
|
|
|
if (value < -1) {
|
|
throw new ArgumentOutOfRangeException ("SelectedIndex", "Value of '" + value + "' is valid for 'SelectedIndex'. " +
|
|
"'SelectedIndex' must be greater than or equal to -1.");
|
|
}
|
|
if (!this.IsHandleCreated) {
|
|
if (selected_index != value) {
|
|
selected_index = value;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (value >= TabCount) {
|
|
if (value != selected_index)
|
|
OnSelectedIndexChanged (EventArgs.Empty);
|
|
return;
|
|
}
|
|
|
|
if (value == selected_index) {
|
|
if (selected_index > -1)
|
|
Invalidate(GetTabRect (selected_index));
|
|
return;
|
|
}
|
|
|
|
TabControlCancelEventArgs ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Deselecting);
|
|
OnDeselecting (ret);
|
|
if (ret.Cancel)
|
|
return;
|
|
|
|
Focus ();
|
|
int old_index = selected_index;
|
|
int new_index = value;
|
|
|
|
selected_index = new_index;
|
|
|
|
ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Selecting);
|
|
OnSelecting (ret);
|
|
if (ret.Cancel) {
|
|
selected_index = old_index;
|
|
return;
|
|
}
|
|
|
|
SuspendLayout ();
|
|
|
|
Rectangle invalid = Rectangle.Empty;
|
|
bool refresh = false;
|
|
|
|
if (new_index != -1 && show_slider && new_index < slider_pos) {
|
|
slider_pos = new_index;
|
|
refresh = true;
|
|
}
|
|
|
|
if (new_index != -1) {
|
|
int le = TabPages[new_index].TabBounds.Right;
|
|
int re = LeftScrollButtonArea.Left;
|
|
if (show_slider && le > re) {
|
|
int i = 0;
|
|
for (i = 0; i < new_index; i++) {
|
|
if (TabPages [i].TabBounds.Left < 0) // tab scrolled off the visible area, ignore
|
|
continue;
|
|
|
|
if (TabPages [new_index].TabBounds.Right - TabPages[i].TabBounds.Right < re) {
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
slider_pos = i;
|
|
refresh = true;
|
|
}
|
|
}
|
|
|
|
if (old_index != -1 && new_index != -1) {
|
|
if (!refresh)
|
|
invalid = GetTabRect (old_index);
|
|
((TabPage) Controls[old_index]).SetVisible (false);
|
|
}
|
|
|
|
TabPage selected = null;
|
|
|
|
if (new_index != -1) {
|
|
selected = (TabPage) Controls[new_index];
|
|
invalid = Rectangle.Union (invalid, GetTabRect (new_index));
|
|
selected.SetVisible (true);
|
|
}
|
|
|
|
OnSelectedIndexChanged (EventArgs.Empty);
|
|
|
|
ResumeLayout ();
|
|
|
|
if (refresh) {
|
|
SizeTabs ();
|
|
Refresh ();
|
|
} else if (new_index != -1 && selected.Row != BottomRow) {
|
|
DropRow (TabPages[new_index].Row);
|
|
// calculating what to invalidate here seems to be slower then just
|
|
// refreshing the whole thing
|
|
SizeTabs ();
|
|
Refresh ();
|
|
} else {
|
|
SizeTabs ();
|
|
// The lines are drawn on the edges of the tabs so the invalid area should
|
|
// needs to include the extra pixels of line width (but should not
|
|
// overflow the control bounds).
|
|
if (appearance == TabAppearance.Normal) {
|
|
invalid.Inflate (6, 4);
|
|
invalid.Intersect (ClientRectangle);
|
|
}
|
|
Invalidate (invalid);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public TabPage SelectedTab {
|
|
get {
|
|
if (selected_index == -1)
|
|
return null;
|
|
return tab_pages [selected_index];
|
|
}
|
|
set {
|
|
int index = IndexForTabPage (value);
|
|
if (index == selected_index)
|
|
return;
|
|
SelectedIndex = index;
|
|
}
|
|
}
|
|
|
|
[DefaultValue(false)]
|
|
[Localizable(true)]
|
|
public bool ShowToolTips {
|
|
get { return show_tool_tips; }
|
|
set {
|
|
if (show_tool_tips == value)
|
|
return;
|
|
show_tool_tips = value;
|
|
}
|
|
}
|
|
|
|
[DefaultValue(TabSizeMode.Normal)]
|
|
[RefreshProperties(RefreshProperties.Repaint)]
|
|
public TabSizeMode SizeMode {
|
|
get { return size_mode; }
|
|
set {
|
|
if (size_mode == value)
|
|
return;
|
|
size_mode = value;
|
|
Redraw ();
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public int TabCount {
|
|
get {
|
|
return tab_pages.Count;
|
|
}
|
|
}
|
|
|
|
[Editor ("System.Windows.Forms.Design.TabPageCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
[MergableProperty(false)]
|
|
public TabPageCollection TabPages {
|
|
get { return tab_pages; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[Bindable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override string Text {
|
|
get { return base.Text; }
|
|
set { base.Text = value; }
|
|
}
|
|
#endregion // Public Instance Properties
|
|
|
|
#region Internal Properties
|
|
internal bool ShowSlider {
|
|
get { return show_slider; }
|
|
set {
|
|
show_slider = value;
|
|
|
|
// UIA Framework Event: HorizontallyScrollable Changed
|
|
OnUIAHorizontallyScrollableChanged (EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
internal int SliderPos {
|
|
get { return slider_pos; }
|
|
}
|
|
|
|
internal PushButtonState RightSliderState {
|
|
get { return right_slider_state; }
|
|
private set {
|
|
if (right_slider_state == value)
|
|
return;
|
|
PushButtonState old_value = right_slider_state;
|
|
right_slider_state = value;
|
|
if (NeedsToInvalidateScrollButton (old_value, value))
|
|
Invalidate (RightScrollButtonArea);
|
|
}
|
|
}
|
|
|
|
internal PushButtonState LeftSliderState {
|
|
get { return left_slider_state; }
|
|
set {
|
|
if (left_slider_state == value)
|
|
return;
|
|
PushButtonState old_value = left_slider_state;
|
|
left_slider_state = value;
|
|
if (NeedsToInvalidateScrollButton (old_value, value))
|
|
Invalidate (LeftScrollButtonArea);
|
|
}
|
|
}
|
|
|
|
bool NeedsToInvalidateScrollButton (PushButtonState oldState, PushButtonState newState)
|
|
{
|
|
if ((oldState == PushButtonState.Hot && newState == PushButtonState.Normal) ||
|
|
(oldState == PushButtonState.Normal && newState == PushButtonState.Hot))
|
|
return HasHotElementStyles;
|
|
return true;
|
|
}
|
|
|
|
internal TabPage EnteredTabPage {
|
|
get { return entered_tab_page; }
|
|
private set {
|
|
if (entered_tab_page == value)
|
|
return;
|
|
if (HasHotElementStyles) {
|
|
Region area_to_invalidate = new Region ();
|
|
area_to_invalidate.MakeEmpty ();
|
|
if (entered_tab_page != null)
|
|
area_to_invalidate.Union (entered_tab_page.TabBounds);
|
|
entered_tab_page = value;
|
|
if (entered_tab_page != null)
|
|
area_to_invalidate.Union (entered_tab_page.TabBounds);
|
|
Invalidate (area_to_invalidate);
|
|
area_to_invalidate.Dispose ();
|
|
} else
|
|
entered_tab_page = value;
|
|
|
|
if (value == null)
|
|
CloseToolTip ();
|
|
else
|
|
SetToolTip (GetToolTipText (value));
|
|
}
|
|
}
|
|
#endregion // Internal Properties
|
|
|
|
#region Protected Instance Properties
|
|
protected override CreateParams CreateParams {
|
|
get {
|
|
CreateParams c = base.CreateParams;
|
|
return c;
|
|
}
|
|
}
|
|
|
|
protected override Size DefaultSize {
|
|
get { return new Size (200, 100); }
|
|
}
|
|
|
|
#endregion // Protected Instance Properties
|
|
|
|
#region Public Instance Methods
|
|
public Rectangle GetTabRect (int index)
|
|
{
|
|
TabPage page = GetTab (index);
|
|
return page.TabBounds;
|
|
}
|
|
|
|
public Control GetControl (int index)
|
|
{
|
|
return GetTab (index);
|
|
}
|
|
|
|
public void SelectTab (TabPage tabPage)
|
|
{
|
|
if (tabPage == null)
|
|
throw new ArgumentNullException ("tabPage");
|
|
|
|
SelectTab (this.tab_pages [tabPage]);
|
|
}
|
|
|
|
public void SelectTab (string tabPageName)
|
|
{
|
|
if (tabPageName == null)
|
|
throw new ArgumentNullException ("tabPageName");
|
|
|
|
SelectTab (this.tab_pages [tabPageName]);
|
|
}
|
|
|
|
public void SelectTab (int index)
|
|
{
|
|
if (index < 0 || index > this.tab_pages.Count - 1)
|
|
throw new ArgumentOutOfRangeException ("index");
|
|
|
|
SelectedIndex = index;
|
|
}
|
|
|
|
public void DeselectTab (TabPage tabPage)
|
|
{
|
|
if (tabPage == null)
|
|
throw new ArgumentNullException ("tabPage");
|
|
|
|
DeselectTab (this.tab_pages [tabPage]);
|
|
}
|
|
|
|
public void DeselectTab (string tabPageName)
|
|
{
|
|
if (tabPageName == null)
|
|
throw new ArgumentNullException ("tabPageName");
|
|
|
|
DeselectTab (this.tab_pages [tabPageName]);
|
|
}
|
|
|
|
public void DeselectTab (int index)
|
|
{
|
|
if (index == SelectedIndex) {
|
|
if (index >= 0 && index < this.tab_pages.Count - 1)
|
|
SelectedIndex = ++index;
|
|
else
|
|
SelectedIndex = 0;
|
|
}
|
|
}
|
|
|
|
|
|
public override string ToString ()
|
|
{
|
|
string res = String.Concat (base.ToString (),
|
|
", TabPages.Count: ",
|
|
TabCount);
|
|
if (TabCount > 0)
|
|
res = String.Concat (res, ", TabPages[0]: ",
|
|
TabPages [0]);
|
|
return res;
|
|
}
|
|
|
|
#endregion // Public Instance Methods
|
|
|
|
#region Protected Instance Methods
|
|
|
|
#region Handles
|
|
protected override Control.ControlCollection CreateControlsInstance ()
|
|
{
|
|
return new TabControl.ControlCollection (this);
|
|
}
|
|
|
|
protected override void CreateHandle ()
|
|
{
|
|
base.CreateHandle ();
|
|
selected_index = (selected_index >= TabCount ? (TabCount > 0 ? 0 : -1) : selected_index);
|
|
|
|
if (TabCount > 0) {
|
|
if (selected_index > -1)
|
|
this.SelectedTab.SetVisible(true);
|
|
else
|
|
tab_pages[0].SetVisible(true);
|
|
}
|
|
ResizeTabPages ();
|
|
}
|
|
|
|
protected override void OnHandleCreated (EventArgs e)
|
|
{
|
|
base.OnHandleCreated (e);
|
|
}
|
|
|
|
protected override void OnHandleDestroyed (EventArgs e)
|
|
{
|
|
base.OnHandleDestroyed (e);
|
|
}
|
|
|
|
protected override void Dispose (bool disposing)
|
|
{
|
|
if (tooltip_timer != null)
|
|
tooltip_timer.Dispose();
|
|
|
|
if (tooltip != null)
|
|
tooltip.Dispose();
|
|
|
|
base.Dispose (disposing);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
protected virtual void OnDrawItem (DrawItemEventArgs e)
|
|
{
|
|
if (DrawMode != TabDrawMode.OwnerDrawFixed)
|
|
return;
|
|
|
|
DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
}
|
|
|
|
internal void OnDrawItemInternal (DrawItemEventArgs e)
|
|
{
|
|
OnDrawItem (e);
|
|
}
|
|
|
|
protected override void OnFontChanged (EventArgs e)
|
|
{
|
|
base.OnFontChanged (e);
|
|
ResizeTabPages ();
|
|
}
|
|
|
|
protected override void OnResize (EventArgs e)
|
|
{
|
|
base.OnResize (e);
|
|
}
|
|
|
|
protected override void OnStyleChanged (EventArgs e)
|
|
{
|
|
base.OnStyleChanged (e);
|
|
}
|
|
|
|
protected virtual void OnSelectedIndexChanged (EventArgs e)
|
|
{
|
|
EventHandler eh = (EventHandler) (Events[SelectedIndexChangedEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
}
|
|
|
|
internal override void OnPaintInternal (PaintEventArgs pe)
|
|
{
|
|
if (GetStyle (ControlStyles.UserPaint))
|
|
return;
|
|
|
|
Draw (pe.Graphics, pe.ClipRectangle);
|
|
pe.Handled = true;
|
|
}
|
|
|
|
protected override void OnEnter (EventArgs e)
|
|
{
|
|
base.OnEnter (e);
|
|
if (SelectedTab != null)
|
|
SelectedTab.FireEnter ();
|
|
}
|
|
|
|
protected override void OnLeave (EventArgs e)
|
|
{
|
|
if (SelectedTab != null)
|
|
SelectedTab.FireLeave ();
|
|
base.OnLeave (e);
|
|
}
|
|
|
|
[EditorBrowsable (EditorBrowsableState.Advanced)]
|
|
protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
|
|
{
|
|
EventHandler eh = (EventHandler) (Events[RightToLeftLayoutChangedEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
}
|
|
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
protected override void ScaleCore (float dx, float dy)
|
|
{
|
|
base.ScaleCore (dx, dy);
|
|
}
|
|
|
|
protected virtual void OnDeselecting (TabControlCancelEventArgs e)
|
|
{
|
|
TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[DeselectingEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
|
|
if (!e.Cancel)
|
|
OnDeselected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Deselected));
|
|
}
|
|
|
|
protected virtual void OnDeselected (TabControlEventArgs e)
|
|
{
|
|
TabControlEventHandler eh = (TabControlEventHandler) (Events[DeselectedEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
|
|
if (this.SelectedTab != null)
|
|
this.SelectedTab.FireLeave ();
|
|
}
|
|
|
|
protected virtual void OnSelecting (TabControlCancelEventArgs e)
|
|
{
|
|
TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[SelectingEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
|
|
if (!e.Cancel)
|
|
OnSelected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Selected));
|
|
}
|
|
|
|
protected virtual void OnSelected (TabControlEventArgs e)
|
|
{
|
|
TabControlEventHandler eh = (TabControlEventHandler) (Events[SelectedEvent]);
|
|
if (eh != null)
|
|
eh (this, e);
|
|
|
|
if (this.SelectedTab != null)
|
|
this.SelectedTab.FireEnter ();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Keys
|
|
protected override bool ProcessKeyPreview (ref Message m)
|
|
{
|
|
return base.ProcessKeyPreview (ref m);
|
|
}
|
|
|
|
protected override void OnKeyDown (KeyEventArgs ke)
|
|
{
|
|
base.OnKeyDown (ke);
|
|
if (ke.Handled)
|
|
return;
|
|
|
|
if (ke.KeyCode == Keys.Tab && (ke.KeyData & Keys.Control) != 0) {
|
|
if ((ke.KeyData & Keys.Shift) == 0)
|
|
SelectedIndex = (SelectedIndex + 1) % TabCount;
|
|
else
|
|
SelectedIndex = (SelectedIndex + TabCount - 1) % TabCount;
|
|
ke.Handled = true;
|
|
} else if (ke.KeyCode == Keys.PageUp && (ke.KeyData & Keys.Control) != 0) {
|
|
SelectedIndex = (SelectedIndex + TabCount - 1) % TabCount;
|
|
ke.Handled = true;
|
|
} else if (ke.KeyCode == Keys.PageDown && (ke.KeyData & Keys.Control) != 0) {
|
|
SelectedIndex = (SelectedIndex + 1) % TabCount;
|
|
ke.Handled = true;
|
|
} else if (ke.KeyCode == Keys.Home) {
|
|
SelectedIndex = 0;
|
|
ke.Handled = true;
|
|
} else if (ke.KeyCode == Keys.End) {
|
|
SelectedIndex = TabCount - 1;
|
|
ke.Handled = true;
|
|
} else if (NavigateTabs (ke.KeyCode))
|
|
ke.Handled = true;
|
|
}
|
|
|
|
protected override bool IsInputKey (Keys keyData)
|
|
{
|
|
switch (keyData & Keys.KeyCode) {
|
|
case Keys.Home:
|
|
case Keys.End:
|
|
case Keys.Left:
|
|
case Keys.Right:
|
|
case Keys.Up:
|
|
case Keys.Down:
|
|
return true;
|
|
}
|
|
return base.IsInputKey (keyData);
|
|
}
|
|
|
|
private bool NavigateTabs (Keys keycode)
|
|
{
|
|
bool move_left = false;
|
|
bool move_right = false;
|
|
|
|
if (alignment == TabAlignment.Bottom || alignment == TabAlignment.Top) {
|
|
if (keycode == Keys.Left)
|
|
move_left = true;
|
|
else if (keycode == Keys.Right)
|
|
move_right = true;
|
|
} else {
|
|
if (keycode == Keys.Up)
|
|
move_left = true;
|
|
else if (keycode == Keys.Down)
|
|
move_right = true;
|
|
}
|
|
|
|
if (move_left) {
|
|
if (SelectedIndex > 0) {
|
|
SelectedIndex--;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (move_right) {
|
|
if (SelectedIndex < TabCount - 1) {
|
|
SelectedIndex++;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endregion
|
|
|
|
#region Pages Collection
|
|
protected void RemoveAll ()
|
|
{
|
|
Controls.Clear ();
|
|
}
|
|
|
|
protected virtual object [] GetItems ()
|
|
{
|
|
TabPage [] pages = new TabPage [Controls.Count];
|
|
Controls.CopyTo (pages, 0);
|
|
return pages;
|
|
}
|
|
|
|
protected virtual object [] GetItems (Type baseType)
|
|
{
|
|
object[] pages = (object[])Array.CreateInstance (baseType, Controls.Count);
|
|
Controls.CopyTo (pages, 0);
|
|
return pages;
|
|
}
|
|
#endregion
|
|
|
|
protected void UpdateTabSelection (bool updateFocus)
|
|
{
|
|
ResizeTabPages ();
|
|
}
|
|
|
|
protected string GetToolTipText (object item)
|
|
{
|
|
TabPage page = (TabPage) item;
|
|
return page.ToolTipText;
|
|
}
|
|
|
|
protected override void WndProc (ref Message m)
|
|
{
|
|
switch ((Msg)m.Msg) {
|
|
case Msg.WM_SETFOCUS:
|
|
if (selected_index != -1)
|
|
Invalidate(GetTabRect(selected_index));
|
|
base.WndProc (ref m);
|
|
break;
|
|
case Msg.WM_KILLFOCUS:
|
|
if (selected_index != -1)
|
|
Invalidate(GetTabRect(selected_index));
|
|
base.WndProc (ref m);
|
|
break;
|
|
default:
|
|
base.WndProc (ref m);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endregion // Protected Instance Methods
|
|
|
|
#region Internal & Private Methods
|
|
private bool CanScrollRight {
|
|
get {
|
|
return (slider_pos < TabCount - 1);
|
|
}
|
|
}
|
|
|
|
private bool CanScrollLeft {
|
|
get { return slider_pos > 0; }
|
|
}
|
|
|
|
private void MouseDownHandler (object sender, MouseEventArgs e)
|
|
{
|
|
if ((e.Button & MouseButtons.Left) == 0)
|
|
return;
|
|
|
|
if (ShowSlider) {
|
|
Rectangle right = RightScrollButtonArea;
|
|
Rectangle left = LeftScrollButtonArea;
|
|
if (right.Contains (e.X, e.Y)) {
|
|
right_slider_state = PushButtonState.Pressed;
|
|
if (CanScrollRight) {
|
|
slider_pos++;
|
|
SizeTabs ();
|
|
|
|
// UIA Framework Event: Horizontally Scrolled
|
|
OnUIAHorizontallyScrolled (EventArgs.Empty);
|
|
|
|
switch (this.Alignment) {
|
|
case TabAlignment.Top:
|
|
Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
|
|
break;
|
|
case TabAlignment.Bottom:
|
|
Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
|
|
break;
|
|
case TabAlignment.Left:
|
|
Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
|
|
break;
|
|
case TabAlignment.Right:
|
|
Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
Invalidate (right);
|
|
}
|
|
return;
|
|
} else if (left.Contains (e.X, e.Y)) {
|
|
left_slider_state = PushButtonState.Pressed;
|
|
if (CanScrollLeft) {
|
|
slider_pos--;
|
|
SizeTabs ();
|
|
|
|
// UIA Framework Event: Horizontally Scrolled
|
|
OnUIAHorizontallyScrolled (EventArgs.Empty);
|
|
|
|
switch (this.Alignment) {
|
|
case TabAlignment.Top:
|
|
Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
|
|
break;
|
|
case TabAlignment.Bottom:
|
|
Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
|
|
break;
|
|
case TabAlignment.Left:
|
|
Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
|
|
break;
|
|
case TabAlignment.Right:
|
|
Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
|
|
break;
|
|
}
|
|
} else {
|
|
Invalidate (left);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
int count = Controls.Count;
|
|
for (int i = SliderPos; i < count; i++) {
|
|
if (!GetTabRect (i).Contains (e.X, e.Y))
|
|
continue;
|
|
SelectedIndex = i;
|
|
mouse_down_on_a_tab_page = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void MouseUpHandler (object sender, MouseEventArgs e)
|
|
{
|
|
mouse_down_on_a_tab_page = false;
|
|
if (ShowSlider && (left_slider_state == PushButtonState.Pressed || right_slider_state == PushButtonState.Pressed)) {
|
|
Rectangle invalid;
|
|
if (left_slider_state == PushButtonState.Pressed) {
|
|
invalid = LeftScrollButtonArea;
|
|
left_slider_state = GetScrollButtonState (invalid, e.Location);
|
|
} else {
|
|
invalid = RightScrollButtonArea;
|
|
right_slider_state = GetScrollButtonState (invalid, e.Location);
|
|
}
|
|
Invalidate (invalid);
|
|
}
|
|
}
|
|
|
|
bool HasHotElementStyles {
|
|
get {
|
|
return ThemeElements.CurrentTheme.TabControlPainter.HasHotElementStyles (this);
|
|
}
|
|
}
|
|
|
|
Rectangle LeftScrollButtonArea {
|
|
get {
|
|
return ThemeElements.CurrentTheme.TabControlPainter.GetLeftScrollRect (this);
|
|
}
|
|
}
|
|
|
|
Rectangle RightScrollButtonArea {
|
|
get {
|
|
return ThemeElements.CurrentTheme.TabControlPainter.GetRightScrollRect (this);
|
|
}
|
|
}
|
|
|
|
static PushButtonState GetScrollButtonState (Rectangle scrollButtonArea, Point cursorLocation)
|
|
{
|
|
return scrollButtonArea.Contains (cursorLocation) ? PushButtonState.Hot : PushButtonState.Normal;
|
|
}
|
|
|
|
private void SizeChangedHandler (object sender, EventArgs e)
|
|
{
|
|
Redraw ();
|
|
}
|
|
|
|
internal int IndexForTabPage (TabPage page)
|
|
{
|
|
for (int i = 0; i < tab_pages.Count; i++) {
|
|
if (page == tab_pages [i])
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private void ResizeTabPages ()
|
|
{
|
|
CalcTabRows ();
|
|
SizeTabs ();
|
|
Rectangle r = DisplayRectangle;
|
|
foreach (TabPage page in Controls) {
|
|
page.Bounds = r;
|
|
}
|
|
}
|
|
|
|
private int MinimumTabWidth {
|
|
get {
|
|
return ThemeEngine.Current.TabControlMinimumTabWidth;
|
|
}
|
|
}
|
|
|
|
private Size TabSpacing {
|
|
get {
|
|
return ThemeEngine.Current.TabControlGetSpacing (this);
|
|
}
|
|
}
|
|
|
|
private void CalcTabRows ()
|
|
{
|
|
switch (Alignment) {
|
|
case TabAlignment.Right:
|
|
case TabAlignment.Left:
|
|
CalcTabRows (Height);
|
|
break;
|
|
default:
|
|
CalcTabRows (Width);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void CalcTabRows (int row_width)
|
|
{
|
|
int xpos = 0;
|
|
int ypos = 0;
|
|
Size spacing = TabSpacing;
|
|
|
|
if (TabPages.Count > 0)
|
|
row_count = 1;
|
|
show_slider = false;
|
|
|
|
CalculateItemSize ();
|
|
|
|
for (int i = 0; i < TabPages.Count; i++) {
|
|
TabPage page = TabPages [i];
|
|
int aux = 0;
|
|
SizeTab (page, i, row_width, ref xpos, ref ypos, spacing, 0, ref aux, true);
|
|
}
|
|
|
|
if (SelectedIndex != -1 && TabPages.Count > SelectedIndex && TabPages[SelectedIndex].Row != BottomRow)
|
|
DropRow (TabPages [SelectedIndex].Row);
|
|
}
|
|
|
|
// ItemSize per-se is used mostly only to retrieve the Height,
|
|
// since the actual Width of the tabs is computed individually,
|
|
// except when SizeMode is TabSizeMode.Fixed, where Width is used as well.
|
|
private void CalculateItemSize ()
|
|
{
|
|
if (item_size_manual)
|
|
return;
|
|
|
|
SizeF size;
|
|
if (tab_pages.Count > 0) {
|
|
// .Net uses the first tab page if available.
|
|
size = TextRenderer.MeasureString (tab_pages [0].Text, Font);
|
|
|
|
} else {
|
|
size = TextRenderer.MeasureString ("a", Font);
|
|
size.Width = 0;
|
|
}
|
|
|
|
if (size_mode == TabSizeMode.Fixed)
|
|
size.Width = 96;
|
|
if (size.Width < MinimumTabWidth)
|
|
size.Width = MinimumTabWidth;
|
|
if (image_list != null && image_list.ImageSize.Height > size.Height)
|
|
size.Height = image_list.ImageSize.Height;
|
|
|
|
item_size = size.ToSize ();
|
|
}
|
|
|
|
private int BottomRow {
|
|
get { return 1; }
|
|
}
|
|
|
|
private int Direction
|
|
{
|
|
get {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
private void DropRow (int row)
|
|
{
|
|
if (Appearance != TabAppearance.Normal)
|
|
return;
|
|
|
|
int bottom = BottomRow;
|
|
int direction = Direction;
|
|
|
|
foreach (TabPage page in TabPages) {
|
|
if (page.Row == row) {
|
|
page.Row = bottom;
|
|
} else if (direction == 1 && page.Row < row) {
|
|
page.Row += direction;
|
|
} else if (direction == -1 && page.Row > row) {
|
|
page.Row += direction;
|
|
}
|
|
}
|
|
}
|
|
|
|
private int CalcYPos ()
|
|
{
|
|
if (Alignment == TabAlignment.Bottom || Alignment == TabAlignment.Left)
|
|
return ThemeEngine.Current.TabControlGetPanelRect (this).Bottom;
|
|
|
|
if (Appearance == TabAppearance.Normal)
|
|
return this.ClientRectangle.Y + ThemeEngine.Current.TabControlSelectedDelta.Y;
|
|
|
|
return this.ClientRectangle.Y;
|
|
|
|
}
|
|
|
|
private int CalcXPos ()
|
|
{
|
|
if (Alignment == TabAlignment.Right)
|
|
return ThemeEngine.Current.TabControlGetPanelRect (this).Right;
|
|
|
|
if (Appearance == TabAppearance.Normal)
|
|
return this.ClientRectangle.X + ThemeEngine.Current.TabControlSelectedDelta.X;
|
|
|
|
return this.ClientRectangle.X;
|
|
}
|
|
|
|
private void SizeTabs ()
|
|
{
|
|
switch (Alignment) {
|
|
case TabAlignment.Right:
|
|
case TabAlignment.Left:
|
|
SizeTabs (Height, true);
|
|
break;
|
|
default:
|
|
SizeTabs (Width, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void SizeTabs (int row_width, bool vertical)
|
|
{
|
|
int ypos = 0;
|
|
int xpos = 0;
|
|
int prev_row = 1;
|
|
Size spacing = TabSpacing;
|
|
int begin_prev = 0;
|
|
|
|
if (TabPages.Count == 0)
|
|
return;
|
|
|
|
prev_row = TabPages [0].Row;
|
|
|
|
// Reset the slider position if the slider isn't needed
|
|
// anymore (ie window size was increased so all tabs are visible)
|
|
if (!show_slider)
|
|
slider_pos = 0;
|
|
else {
|
|
// set X = -1 for marking tabs that are not visible due to scrolling
|
|
for (int i = 0; i < slider_pos; i++) {
|
|
TabPage page = TabPages[i];
|
|
Rectangle x = page.TabBounds;
|
|
x.X = -1;
|
|
page.TabBounds = x;
|
|
}
|
|
}
|
|
|
|
for (int i = slider_pos; i < TabPages.Count; i++) {
|
|
TabPage page = TabPages[i];
|
|
SizeTab (page, i, row_width, ref xpos, ref ypos, spacing, prev_row, ref begin_prev, false);
|
|
prev_row = page.Row;
|
|
}
|
|
|
|
if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
|
|
FillRow (begin_prev, TabPages.Count - 1,
|
|
((row_width - TabPages [TabPages.Count - 1].TabBounds.Right) / (TabPages.Count - begin_prev)),
|
|
spacing, vertical);
|
|
}
|
|
|
|
if (SelectedIndex != -1) {
|
|
ExpandSelected (TabPages [SelectedIndex], 0, row_width - 1);
|
|
}
|
|
}
|
|
|
|
private void SizeTab (TabPage page, int i, int row_width, ref int xpos, ref int ypos,
|
|
Size spacing, int prev_row, ref int begin_prev, bool widthOnly)
|
|
{
|
|
int width, height = 0;
|
|
|
|
if (SizeMode == TabSizeMode.Fixed) {
|
|
width = item_size.Width;
|
|
} else {
|
|
width = MeasureStringWidth (DeviceContext, page.Text, Font);
|
|
width += (Padding.X * 2) + 2;
|
|
|
|
if (ImageList != null && page.ImageIndex >= 0) {
|
|
width += ImageList.ImageSize.Width + ThemeEngine.Current.TabControlImagePadding.X;
|
|
|
|
int image_size = ImageList.ImageSize.Height + ThemeEngine.Current.TabControlImagePadding.Y;
|
|
if (item_size.Height < image_size)
|
|
item_size.Height = image_size;
|
|
}
|
|
|
|
if (width < MinimumTabWidth)
|
|
width = MinimumTabWidth;
|
|
}
|
|
|
|
// Use ItemSize property to recover the padding info as well.
|
|
height = ItemSize.Height - ThemeEngine.Current.TabControlSelectedDelta.Height; // full height only for selected tab
|
|
|
|
if (i == SelectedIndex)
|
|
width += ThemeEngine.Current.TabControlSelectedSpacing;
|
|
|
|
if (widthOnly) {
|
|
page.TabBounds = new Rectangle (xpos, 0, width, 0);
|
|
if (xpos + width > row_width && multiline) {
|
|
xpos = 0;
|
|
row_count++;
|
|
} else if (xpos + width > row_width) {
|
|
show_slider = true;
|
|
}
|
|
page.Row = row_count;
|
|
if (i == selected_index && show_slider) {
|
|
for (int j = i-1; j >= 0; j--) {
|
|
if (TabPages [j].TabBounds.Left < xpos + width - row_width) {
|
|
slider_pos = j+1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (page.Row != prev_row) {
|
|
xpos = 0;
|
|
}
|
|
|
|
switch (Alignment) {
|
|
case TabAlignment.Top:
|
|
case TabAlignment.Bottom:
|
|
page.TabBounds = new Rectangle (
|
|
xpos + CalcXPos (),
|
|
ypos + (height + spacing.Height) * (row_count - page.Row) + CalcYPos (),
|
|
width,
|
|
height);
|
|
break;
|
|
case TabAlignment.Left:
|
|
if (Appearance == TabAppearance.Normal) {
|
|
// tab rows are positioned right to left
|
|
page.TabBounds = new Rectangle (
|
|
ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
|
|
xpos,
|
|
height,
|
|
width);
|
|
} else {
|
|
// tab rows are positioned left to right
|
|
page.TabBounds = new Rectangle (
|
|
ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
|
|
xpos,
|
|
height,
|
|
width);
|
|
}
|
|
|
|
break;
|
|
case TabAlignment.Right:
|
|
if (Appearance == TabAppearance.Normal) {
|
|
// tab rows are positioned left to right
|
|
page.TabBounds = new Rectangle (
|
|
ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
|
|
xpos,
|
|
height,
|
|
width);
|
|
} else {
|
|
// tab rows are positioned right to left
|
|
page.TabBounds = new Rectangle (
|
|
ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
|
|
xpos,
|
|
height,
|
|
width);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (page.Row != prev_row) {
|
|
if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
|
|
bool vertical = alignment == TabAlignment.Right || alignment == TabAlignment.Left;
|
|
int offset = vertical ? TabPages [i - 1].TabBounds.Bottom : TabPages [i - 1].TabBounds.Right;
|
|
FillRow (begin_prev, i - 1, ((row_width - offset) / (i - begin_prev)), spacing,
|
|
vertical);
|
|
}
|
|
begin_prev = i;
|
|
}
|
|
}
|
|
|
|
xpos += width + spacing.Width + ThemeEngine.Current.TabControlColSpacing;
|
|
}
|
|
|
|
private void FillRow (int start, int end, int amount, Size spacing, bool vertical)
|
|
{
|
|
if (vertical)
|
|
FillRowV (start, end, amount, spacing);
|
|
else
|
|
FillRow (start, end, amount, spacing);
|
|
}
|
|
|
|
private void FillRow (int start, int end, int amount, Size spacing)
|
|
{
|
|
int xpos = TabPages [start].TabBounds.Left;
|
|
for (int i = start; i <= end; i++) {
|
|
TabPage page = TabPages [i];
|
|
int left = xpos;
|
|
int width = (i == end ? Width - left - 3 : page.TabBounds.Width + amount);
|
|
|
|
page.TabBounds = new Rectangle (left, page.TabBounds.Top,
|
|
width, page.TabBounds.Height);
|
|
xpos = page.TabBounds.Right + 1 + spacing.Width;
|
|
}
|
|
}
|
|
|
|
private void FillRowV (int start, int end, int amount, Size spacing)
|
|
{
|
|
int ypos = TabPages [start].TabBounds.Top;
|
|
for (int i = start; i <= end; i++) {
|
|
TabPage page = TabPages [i];
|
|
int top = ypos;
|
|
int height = (i == end ? Height - top - 5 : page.TabBounds.Height + amount);
|
|
|
|
page.TabBounds = new Rectangle (page.TabBounds.Left, top,
|
|
page.TabBounds.Width, height);
|
|
ypos = page.TabBounds.Bottom + 1;
|
|
}
|
|
}
|
|
|
|
private void ExpandSelected (TabPage page, int left_edge, int right_edge)
|
|
{
|
|
if (Appearance != TabAppearance.Normal)
|
|
return;
|
|
|
|
Rectangle r = page.TabBounds;
|
|
r.Y -= ThemeEngine.Current.TabControlSelectedDelta.Y;
|
|
r.X -= ThemeEngine.Current.TabControlSelectedDelta.X;
|
|
|
|
r.Width += ThemeEngine.Current.TabControlSelectedDelta.Width;
|
|
r.Height += ThemeEngine.Current.TabControlSelectedDelta.Height;
|
|
if (r.Left < left_edge)
|
|
r.X = left_edge;
|
|
// Adjustment can't be used for right alignment, since it is
|
|
// the only one that has a different X origin than 0
|
|
if (r.Right > right_edge && SizeMode != TabSizeMode.Normal &&
|
|
alignment != TabAlignment.Right)
|
|
r.Width = right_edge - r.X;
|
|
page.TabBounds = r;
|
|
}
|
|
|
|
private void Draw (Graphics dc, Rectangle clip)
|
|
{
|
|
ThemeEngine.Current.DrawTabControl (dc, clip, this);
|
|
}
|
|
|
|
private TabPage GetTab (int index)
|
|
{
|
|
return Controls [index] as TabPage;
|
|
}
|
|
|
|
private void SetTab (int index, TabPage value)
|
|
{
|
|
if (!tab_pages.Contains (value)) {
|
|
this.Controls.Add (value);
|
|
}
|
|
this.Controls.RemoveAt (index);
|
|
this.Controls.SetChildIndex (value, index);
|
|
Redraw ();
|
|
}
|
|
private void InsertTab (int index, TabPage value)
|
|
{
|
|
if (!tab_pages.Contains (value)) {
|
|
this.Controls.Add (value);
|
|
}
|
|
this.Controls.SetChildIndex (value, index);
|
|
Redraw ();
|
|
}
|
|
internal void Redraw ()
|
|
{
|
|
if (!IsHandleCreated)
|
|
return;
|
|
|
|
ResizeTabPages ();
|
|
Refresh ();
|
|
}
|
|
|
|
private int MeasureStringWidth (Graphics graphics, string text, Font font)
|
|
{
|
|
if (text == String.Empty)
|
|
return 0;
|
|
StringFormat format = new StringFormat();
|
|
RectangleF rect = new RectangleF(0, 0, 1000, 1000);
|
|
CharacterRange[] ranges = { new CharacterRange(0, text.Length) };
|
|
Region[] regions = new Region[1];
|
|
|
|
format.SetMeasurableCharacterRanges(ranges);
|
|
format.FormatFlags = StringFormatFlags.NoClip;
|
|
format.FormatFlags |= StringFormatFlags.NoWrap;
|
|
regions = graphics.MeasureCharacterRanges(text + "I", font, rect, format);
|
|
rect = regions[0].GetBounds(graphics);
|
|
|
|
return (int)(rect.Width);
|
|
}
|
|
|
|
void SetToolTip (string text)
|
|
{
|
|
if (!show_tool_tips)
|
|
return;
|
|
|
|
if (text == null || text.Length == 0) {
|
|
CloseToolTip ();
|
|
return;
|
|
}
|
|
|
|
if (tooltip == null) {
|
|
tooltip = new ToolTip ();
|
|
tooltip_timer = new Timer ();
|
|
tooltip_timer.Tick += new EventHandler (ToolTipTimerTick);
|
|
}
|
|
|
|
CloseToolTip ();
|
|
|
|
tooltip_state = ToolTip.TipState.Initial;
|
|
tooltip_timer.Interval = 500;
|
|
tooltip_timer.Start ();
|
|
}
|
|
|
|
void CloseToolTip ()
|
|
{
|
|
if (tooltip == null)
|
|
return;
|
|
|
|
tooltip.Hide (this);
|
|
tooltip_timer.Stop ();
|
|
tooltip_state = ToolTip.TipState.Down;
|
|
}
|
|
|
|
void ToolTipTimerTick (object o, EventArgs args)
|
|
{
|
|
switch (tooltip_state) {
|
|
case ToolTip.TipState.Initial:
|
|
tooltip_timer.Stop ();
|
|
tooltip_timer.Interval = 5000;
|
|
tooltip_timer.Start ();
|
|
tooltip_state = ToolTip.TipState.Show;
|
|
tooltip.Present (this, GetToolTipText (EnteredTabPage));
|
|
break;
|
|
case ToolTip.TipState.Show:
|
|
CloseToolTip ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OnMouseMove (object sender, MouseEventArgs e)
|
|
{
|
|
if (!mouse_down_on_a_tab_page && ShowSlider) {
|
|
if (LeftSliderState == PushButtonState.Pressed ||
|
|
RightSliderState == PushButtonState.Pressed)
|
|
return;
|
|
if (LeftScrollButtonArea.Contains (e.Location)) {
|
|
LeftSliderState = PushButtonState.Hot;
|
|
RightSliderState = PushButtonState.Normal;
|
|
EnteredTabPage = null;
|
|
return;
|
|
}
|
|
if (RightScrollButtonArea.Contains (e.Location)) {
|
|
RightSliderState = PushButtonState.Hot;
|
|
LeftSliderState = PushButtonState.Normal;
|
|
EnteredTabPage = null;
|
|
return;
|
|
}
|
|
LeftSliderState = PushButtonState.Normal;
|
|
RightSliderState = PushButtonState.Normal;
|
|
}
|
|
if (EnteredTabPage != null && EnteredTabPage.TabBounds.Contains (e.Location))
|
|
return;
|
|
for (int index = 0; index < TabCount; index++) {
|
|
TabPage tab_page = TabPages[index];
|
|
if (tab_page.TabBounds.Contains (e.Location)) {
|
|
EnteredTabPage = tab_page;
|
|
return;
|
|
}
|
|
}
|
|
EnteredTabPage = null;
|
|
}
|
|
|
|
void OnMouseLeave (object sender, EventArgs e)
|
|
{
|
|
if (ShowSlider) {
|
|
LeftSliderState = PushButtonState.Normal;
|
|
RightSliderState = PushButtonState.Normal;
|
|
}
|
|
EnteredTabPage = null;
|
|
}
|
|
#endregion // Internal & Private Methods
|
|
|
|
#region Events
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public new event EventHandler BackColorChanged {
|
|
add { base.BackColorChanged += value; }
|
|
remove { base.BackColorChanged -= value; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public new event EventHandler BackgroundImageChanged {
|
|
add { base.BackgroundImageChanged += value; }
|
|
remove { base.BackgroundImageChanged -= value; }
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public new event EventHandler BackgroundImageLayoutChanged
|
|
{
|
|
add { base.BackgroundImageLayoutChanged += value; }
|
|
remove { base.BackgroundImageLayoutChanged -= value; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public new event EventHandler ForeColorChanged {
|
|
add { base.ForeColorChanged += value; }
|
|
remove { base.ForeColorChanged -= value; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public new event PaintEventHandler Paint {
|
|
add { base.Paint += value; }
|
|
remove { base.Paint -= value; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public new event EventHandler TextChanged {
|
|
add { base.TextChanged += value; }
|
|
remove { base.TextChanged -= value; }
|
|
}
|
|
|
|
static object DrawItemEvent = new object ();
|
|
static object SelectedIndexChangedEvent = new object ();
|
|
|
|
public event DrawItemEventHandler DrawItem {
|
|
add { Events.AddHandler (DrawItemEvent, value); }
|
|
remove { Events.RemoveHandler (DrawItemEvent, value); }
|
|
}
|
|
|
|
public event EventHandler SelectedIndexChanged {
|
|
add { Events.AddHandler (SelectedIndexChangedEvent, value); }
|
|
remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
|
|
}
|
|
|
|
static object SelectedEvent = new object ();
|
|
|
|
public event TabControlEventHandler Selected {
|
|
add { Events.AddHandler (SelectedEvent, value); }
|
|
remove { Events.RemoveHandler (SelectedEvent, value); }
|
|
}
|
|
|
|
static object DeselectedEvent = new object ();
|
|
|
|
public event TabControlEventHandler Deselected
|
|
{
|
|
add { Events.AddHandler (DeselectedEvent, value); }
|
|
remove { Events.RemoveHandler (DeselectedEvent, value); }
|
|
}
|
|
|
|
static object SelectingEvent = new object ();
|
|
|
|
public event TabControlCancelEventHandler Selecting
|
|
{
|
|
add { Events.AddHandler (SelectingEvent, value); }
|
|
remove { Events.RemoveHandler (SelectingEvent, value); }
|
|
}
|
|
|
|
static object DeselectingEvent = new object ();
|
|
|
|
public event TabControlCancelEventHandler Deselecting
|
|
{
|
|
add { Events.AddHandler (DeselectingEvent, value); }
|
|
remove { Events.RemoveHandler (DeselectingEvent, value); }
|
|
}
|
|
|
|
static object RightToLeftLayoutChangedEvent = new object ();
|
|
public event EventHandler RightToLeftLayoutChanged
|
|
{
|
|
add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
|
|
remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
|
|
}
|
|
#endregion // Events
|
|
|
|
|
|
#region Class TaControl.ControlCollection
|
|
[ComVisible (false)]
|
|
public new class ControlCollection : System.Windows.Forms.Control.ControlCollection {
|
|
|
|
private TabControl owner;
|
|
|
|
public ControlCollection (TabControl owner) : base (owner)
|
|
{
|
|
this.owner = owner;
|
|
}
|
|
|
|
public override void Add (Control value)
|
|
{
|
|
TabPage page = value as TabPage;
|
|
if (page == null)
|
|
throw new ArgumentException ("Cannot add " +
|
|
value.GetType ().Name + " to TabControl. " +
|
|
"Only TabPages can be directly added to TabControls.");
|
|
|
|
page.SetVisible (false);
|
|
base.Add (value);
|
|
if (owner.TabCount == 1 && owner.selected_index < 0)
|
|
owner.SelectedIndex = 0;
|
|
owner.Redraw ();
|
|
}
|
|
|
|
public override void Remove (Control value)
|
|
{
|
|
bool invalid_selected_index = false;
|
|
|
|
TabPage page = value as TabPage;
|
|
int next_selected_index = -1;
|
|
if (page != null && owner.Controls.Contains (page)) {
|
|
int index = owner.IndexForTabPage (page);
|
|
|
|
if (owner.SelectedIndex < index || owner.SelectedIndex == 0)
|
|
next_selected_index = owner.SelectedIndex;
|
|
else
|
|
next_selected_index = owner.SelectedIndex - 1;
|
|
|
|
owner.selected_index = -1; // Invalidate internal selected Index
|
|
invalid_selected_index = true;
|
|
}
|
|
|
|
base.Remove (value);
|
|
|
|
// We don't want to raise SelectedIndexChanged until after we
|
|
// have removed from the collection, so TabCount will be
|
|
// correct for the user.
|
|
if (invalid_selected_index) {
|
|
if (Count > 0)
|
|
owner.SelectedIndex = next_selected_index;
|
|
else // Count == 0
|
|
owner.OnSelectedIndexChanged (EventArgs.Empty);
|
|
owner.Invalidate ();
|
|
} else
|
|
owner.Redraw ();
|
|
}
|
|
}
|
|
#endregion // Class TabControl.ControlCollection
|
|
|
|
#region Class TabPage.TabPageCollection
|
|
public class TabPageCollection : IList, ICollection, IEnumerable {
|
|
|
|
private TabControl owner;
|
|
|
|
public TabPageCollection (TabControl owner)
|
|
{
|
|
if (owner == null)
|
|
throw new ArgumentNullException ("Value cannot be null.");
|
|
this.owner = owner;
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public int Count {
|
|
get { return owner.Controls.Count; }
|
|
}
|
|
|
|
public bool IsReadOnly {
|
|
get { return false; }
|
|
}
|
|
|
|
public virtual TabPage this [int index] {
|
|
get {
|
|
return owner.GetTab (index);
|
|
}
|
|
set {
|
|
owner.SetTab (index, value);
|
|
}
|
|
}
|
|
public virtual TabPage this [string key] {
|
|
get {
|
|
if (string.IsNullOrEmpty (key))
|
|
return null;
|
|
|
|
int index = this.IndexOfKey (key);
|
|
if (index < 0 || index >= this.Count)
|
|
return null;
|
|
|
|
return this[index];
|
|
}
|
|
}
|
|
|
|
internal int this[TabPage tabPage] {
|
|
get {
|
|
if (tabPage == null)
|
|
return -1;
|
|
|
|
for (int i = 0; i < this.Count; i++)
|
|
if (this[i].Equals (tabPage))
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bool ICollection.IsSynchronized {
|
|
get { return false; }
|
|
}
|
|
|
|
object ICollection.SyncRoot {
|
|
get { return this; }
|
|
}
|
|
|
|
bool IList.IsFixedSize {
|
|
get { return false; }
|
|
}
|
|
|
|
object IList.this [int index] {
|
|
get {
|
|
return owner.GetTab (index);
|
|
}
|
|
set {
|
|
owner.SetTab (index, (TabPage) value);
|
|
}
|
|
}
|
|
|
|
public void Add (TabPage value)
|
|
{
|
|
if (value == null)
|
|
throw new ArgumentNullException ("Value cannot be null.");
|
|
owner.Controls.Add (value);
|
|
}
|
|
|
|
public void Add (string text)
|
|
{
|
|
TabPage page = new TabPage (text);
|
|
this.Add (page);
|
|
}
|
|
|
|
public void Add (string key, string text)
|
|
{
|
|
TabPage page = new TabPage (text);
|
|
page.Name = key;
|
|
this.Add (page);
|
|
}
|
|
|
|
public void Add (string key, string text, int imageIndex)
|
|
{
|
|
TabPage page = new TabPage (text);
|
|
page.Name = key;
|
|
page.ImageIndex = imageIndex;
|
|
this.Add (page);
|
|
}
|
|
|
|
// .Net sets the ImageKey, but does not show the image when this is used
|
|
public void Add (string key, string text, string imageKey)
|
|
{
|
|
TabPage page = new TabPage (text);
|
|
page.Name = key;
|
|
page.ImageKey = imageKey;
|
|
this.Add (page);
|
|
}
|
|
|
|
public void AddRange (TabPage [] pages)
|
|
{
|
|
if (pages == null)
|
|
throw new ArgumentNullException ("Value cannot be null.");
|
|
owner.Controls.AddRange (pages);
|
|
}
|
|
|
|
public virtual void Clear ()
|
|
{
|
|
owner.Controls.Clear ();
|
|
owner.Invalidate ();
|
|
}
|
|
|
|
public bool Contains (TabPage page)
|
|
{
|
|
if (page == null)
|
|
throw new ArgumentNullException ("Value cannot be null.");
|
|
return owner.Controls.Contains (page);
|
|
}
|
|
|
|
public virtual bool ContainsKey (string key)
|
|
{
|
|
int index = this.IndexOfKey (key);
|
|
return (index >= 0 && index < this.Count);
|
|
}
|
|
|
|
public IEnumerator GetEnumerator ()
|
|
{
|
|
return owner.Controls.GetEnumerator ();
|
|
}
|
|
|
|
public int IndexOf (TabPage page)
|
|
{
|
|
return owner.Controls.IndexOf (page);
|
|
}
|
|
|
|
public virtual int IndexOfKey(string key)
|
|
{
|
|
if (string.IsNullOrEmpty (key))
|
|
return -1;
|
|
|
|
for (int i = 0; i < this.Count; i++) {
|
|
if (string.Compare (this[i].Name, key, true,
|
|
System.Globalization.CultureInfo.InvariantCulture) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public void Remove (TabPage value)
|
|
{
|
|
owner.Controls.Remove (value);
|
|
owner.Invalidate ();
|
|
}
|
|
|
|
public void RemoveAt (int index)
|
|
{
|
|
owner.Controls.RemoveAt (index);
|
|
owner.Invalidate ();
|
|
}
|
|
|
|
public virtual void RemoveByKey (string key)
|
|
{
|
|
int index = this.IndexOfKey (key);
|
|
if (index >= 0 && index < this.Count)
|
|
this.RemoveAt (index);
|
|
}
|
|
|
|
void ICollection.CopyTo (Array dest, int index)
|
|
{
|
|
owner.Controls.CopyTo (dest, index);
|
|
}
|
|
|
|
int IList.Add (object value)
|
|
{
|
|
TabPage page = value as TabPage;
|
|
if (value == null)
|
|
throw new ArgumentException ("value");
|
|
owner.Controls.Add (page);
|
|
return owner.Controls.IndexOf (page);
|
|
}
|
|
|
|
bool IList.Contains (object page)
|
|
{
|
|
TabPage tabPage = page as TabPage;
|
|
if (tabPage == null)
|
|
return false;
|
|
return Contains (tabPage);
|
|
}
|
|
|
|
int IList.IndexOf (object page)
|
|
{
|
|
TabPage tabPage = page as TabPage;
|
|
if (tabPage == null)
|
|
return -1;
|
|
return IndexOf (tabPage);
|
|
}
|
|
|
|
void IList.Insert (int index, object tabPage)
|
|
{
|
|
throw new NotSupportedException ();
|
|
}
|
|
|
|
public void Insert (int index, string text)
|
|
{
|
|
owner.InsertTab (index, new TabPage (text));
|
|
}
|
|
|
|
public void Insert (int index, TabPage tabPage)
|
|
{
|
|
owner.InsertTab (index, tabPage);
|
|
}
|
|
|
|
public void Insert (int index, string key, string text)
|
|
{
|
|
TabPage page = new TabPage(text);
|
|
page.Name = key;
|
|
owner.InsertTab (index, page);
|
|
}
|
|
|
|
public void Insert (int index, string key, string text, int imageIndex)
|
|
{
|
|
TabPage page = new TabPage(text);
|
|
page.Name = key;
|
|
owner.InsertTab (index, page);
|
|
page.ImageIndex = imageIndex;
|
|
}
|
|
|
|
public void Insert (int index, string key, string text, string imageKey)
|
|
{
|
|
TabPage page = new TabPage(text);
|
|
page.Name = key;
|
|
owner.InsertTab (index, page);
|
|
page.ImageKey = imageKey;
|
|
}
|
|
void IList.Remove (object value)
|
|
{
|
|
TabPage page = value as TabPage;
|
|
if (page == null)
|
|
return;
|
|
Remove ((TabPage) value);
|
|
}
|
|
}
|
|
#endregion // Class TabPage.TabPageCollection
|
|
}
|
|
}
|
|
|
|
|