// 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;


namespace System.Windows.Forms {
	[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 Point padding;
		private int row_count = 1;
		private bool hottrack;
		private TabPageCollection tab_pages;
		private bool show_tool_tips;
		private TabSizeMode size_mode;
		private Rectangle display_rect;
		private bool show_slider = false;
		private ButtonState right_slider_state;
		private ButtonState left_slider_state;
		private int slider_pos = 0;
		#endregion	// Fields

		#region Public Constructors
		public TabControl ()
		{
			tab_pages = new TabPageCollection (this);
			SetStyle (ControlStyles.UserPaint, false);
			padding = ThemeEngine.Current.TabControlDefaultPadding;
			item_size = ThemeEngine.Current.TabControlDefaultItemSize;

			MouseDown += new MouseEventHandler (MouseDownHandler);
			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; }
		}

		public override Rectangle DisplayRectangle {
			get {
				return ThemeEngine.Current.GetTabControlDisplayRectangle (this);
			}
		}

		[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 ();
			}
		}

		[DefaultValue(null)]
		public ImageList ImageList {
			get { return image_list; }
			set { image_list = value; }
		}

		[Localizable(true)]
		public Size ItemSize {
			get {
				return item_size;
			}
			set {
				if (value.Height < 0 || value.Width < 0)
					throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
				item_size = value;
				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 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 ();
			}

		}

		[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 (selected_index == value)
					return;
				if (selected_index < -1) {
					throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
							"'value' must be greater than or equal to -1.");
				}
				if (value >= TabCount)
					return;

				SuspendLayout ();

				Rectangle invalid = Rectangle.Empty;
				bool refresh = false;
				
				if (-1 != value && show_slider && value < slider_pos) {
					slider_pos = value;
					refresh = true;
				}

				if (-1 != value) {
        				int le = TabPages [value].TabBounds.Right;
                                	int re = ThemeEngine.Current.GetTabControlLeftScrollRect (this).Left;
					if (show_slider && le > re) {
						int i = 0;

						for (i = value; i < TabPages.Count; i++) {
							if (TabPages [i].TabBounds.Right > re)
								break;
						}
						slider_pos = i;
						refresh = true;
					}
				}

				if (selected_index != -1) {
					if (!refresh)
						invalid = GetTabRect (selected_index);
					((TabPage) Controls [selected_index]).SetVisible (false);
				}
				selected_index = value;

				OnSelectedIndexChanged (EventArgs.Empty);

				TabPage selected = null;

				if (selected_index != -1) {
					selected = (TabPage) Controls [selected_index];
					invalid = Rectangle.Union (invalid, GetTabRect (selected_index));
					selected.SetVisible (true);
				}

				ResumeLayout ();

				if (refresh) {
					SizeTabs ();
					Refresh ();
				} else if (selected_index != -1 && selected.Row != BottomRow) {
					DropRow (TabPages [selected_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.
					if (appearance == TabAppearance.Normal) {
						invalid.Inflate (6, 4);
					}
					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;
				Redraw ();
			}
		}

		[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;
			}
		}

		[DefaultValue(null)]
		[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; }
		}

		internal int SliderPos {
			get { return slider_pos; }
		}

		internal ButtonState RightSliderState {
			get { return right_slider_state; }
		}

		internal ButtonState LeftSliderState {
			get { return left_slider_state; }
		}

		private Size DefaultItemSize {
			get {
				return ThemeEngine.Current.TabControlDefaultItemSize;
			}
		}

		#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 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
		protected override Control.ControlCollection CreateControlsInstance ()
		{
			return new TabControl.ControlCollection (this);
		}

		protected void UpdateTabSelection (bool uiselected)
		{
			ResizeTabPages ();
		}

		protected override void CreateHandle ()
		{
			base.CreateHandle ();
			ResizeTabPages ();
		}

		protected override void OnHandleCreated (EventArgs e)
		{
			base.OnHandleCreated (e);
		}

		protected override void OnHandleDestroyed (EventArgs e)
		{
			base.OnHandleDestroyed (e);
		}

		protected virtual void OnDrawItem (DrawItemEventArgs e)
		{
			if (DrawItem != null && DrawMode == TabDrawMode.OwnerDrawFixed)
				DrawItem (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 override bool ProcessKeyPreview (ref Message m)
		{
			return base.ProcessKeyPreview (ref m);
		}

		protected override void OnKeyDown (KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Tab && (e.KeyData & Keys.Control) != 0) {
				if ((e.KeyData & Keys.Shift) == 0)
					SelectedIndex = (SelectedIndex + 1) % TabCount;
				else
					SelectedIndex = (SelectedIndex - 1) % TabCount;
				e.Handled = true;
			} else if (e.KeyCode == Keys.Home) {
				SelectedIndex = 0;
				e.Handled = true;
			} else if (e.KeyCode == Keys.End) {
				SelectedIndex = TabCount - 1;
				e.Handled = true;
			} else if (e.KeyCode == Keys.Left && SelectedIndex > 0) {
				SelectedIndex--;
				e.Handled = true;
			} else if (e.KeyCode == Keys.Right && SelectedIndex < TabCount - 1) {
				SelectedIndex++;
				e.Handled = true;
			}

			base.OnKeyDown (e);
		}

		protected override bool IsInputKey (Keys key)
		{
			switch (key & Keys.KeyCode) {
			case Keys.Home:
			case Keys.End:
			case Keys.Left:
			case Keys.Right:
			case Keys.Tab:
				return true;
			}
			return base.IsInputKey (key);
		}

		protected override void Dispose (bool disposing)
		{
			base.Dispose (disposing);
		}

		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 type)
		{
			object [] pages = (object []) Array.CreateInstance (type, Controls.Count);
			Controls.CopyTo (pages, 0);
			return pages;
		}

		protected string GetToolTipText (object item)
		{
			TabPage page = (TabPage) item;
			return page.ToolTipText;
		}

		protected override void WndProc (ref Message m)
		{
			base.WndProc (ref m);
		}

		protected virtual void OnSelectedIndexChanged (EventArgs e)
		{
			if (SelectedIndexChanged != null)
				SelectedIndexChanged (this, e);
		}

		internal override void OnPaintInternal (PaintEventArgs pe)
		{
			Draw (pe.Graphics, pe.ClipRectangle);
			pe.Handled = true;
		}
		#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 (ShowSlider) {
				Rectangle right = ThemeEngine.Current.GetTabControlRightScrollRect (this);
				Rectangle left = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
				if (right.Contains (e.X, e.Y)) {
					right_slider_state = ButtonState.Pushed;
					if (CanScrollRight) {
						slider_pos++;
						SizeTabs ();

						Invalidate (new Rectangle (0, 0, Width, DisplayRectangle.Top));
					} else {
						Invalidate (right);
					}
					return;
				} else if (left.Contains (e.X, e.Y)) {
					left_slider_state = ButtonState.Pushed;
					if (CanScrollLeft) {
						slider_pos--;
						SizeTabs ();

						Invalidate (new Rectangle (0, 0, Width, DisplayRectangle.Top));
					} 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;
				break;
			}
		}

		private void MouseUpHandler (object sender, MouseEventArgs e)
		{
			if (ShowSlider && (left_slider_state == ButtonState.Pushed || right_slider_state == ButtonState.Pushed)) {
				Rectangle invalid;
				if (left_slider_state == ButtonState.Pushed)
					invalid = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
				else
					invalid = ThemeEngine.Current.GetTabControlRightScrollRect (this);
				left_slider_state = ButtonState.Normal;
				right_slider_state = ButtonState.Normal;

				Invalidate (invalid);
			}
		}

		private void SizeChangedHandler (object sender, EventArgs e)
		{
			Redraw ();
		}

		internal void UpdateTabpage (TabPage page)
		{

		}

		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 = 4;
			Size spacing = TabSpacing;

			row_count = 1;
			show_slider = false;
			
			for (int i = 0; i < TabPages.Count; i++) {
				TabPage page = TabPages [i];
				int width;

				page.Row = 1;

				if (SizeMode == TabSizeMode.Fixed) {
					width = item_size.Width;
				} else {
					width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
					if (ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < ImageList.Images.Count) {
						width += ImageList.ImageSize.Width + 2;
						item_size.Height = ImageList.ImageSize.Height + 3;
					}
				}

				if (i == SelectedIndex)
					width += 8;
				if (width < MinimumTabWidth)
					width = MinimumTabWidth;

				if (xpos + width > row_width && multiline) {
					xpos = 4;
					for (int j = 0; j < i; j++) {
						TabPages [j].Row++;
					}
					row_count++;
				} else if (xpos + width > row_width) {
					show_slider = true;
				}

				xpos += width + 1 + spacing.Width;
			}

			if (SelectedIndex != -1 && TabPages [SelectedIndex].Row != BottomRow)
				DropRow (TabPages [SelectedIndex].Row);
		}

		private int BottomRow {
			get {
				switch (Alignment) {
				case TabAlignment.Right:
				case TabAlignment.Bottom:
					return row_count;
				default:
					return 1;
				}
			}
		}

		private int Direction
		{
			get {
				switch (Alignment) {
				case TabAlignment.Right:
				case TabAlignment.Bottom:
					return -1;
				default:
					return 1;
				}
			}
		}

		private void DropRow (int row)
		{
			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) {
				Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
				return r.Bottom + 3;
			}
			return 1;
		}

		private int CalcXPos ()
		{
			if (Alignment == TabAlignment.Right) {
				Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
				return r.Right + 4;
			}
			return 4;

		}

		private void SizeTabs ()
		{
			switch (Alignment) {
			case TabAlignment.Right:
			case TabAlignment.Left:
				SizeTabsV (Height);
				break;
			default:
				SizeTabs (Width);
				break;
			}
		}

		private void SizeTabsV (int row_width)
		{
			int ypos = 1;
			int prev_row = 1;
			Size spacing = TabSpacing;
			int xpos = CalcXPos ();
			int begin_prev = 0;
			
			if (TabPages.Count == 0)
				return;

			prev_row = TabPages [0].Row;

			for (int i = 0; i < TabPages.Count; i++) {
				TabPage page = TabPages [i];
				int width;

				if (SizeMode == TabSizeMode.Fixed) {
					width = item_size.Width;
				} else {
					width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
					if (ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < ImageList.Images.Count) {
						width += ImageList.ImageSize.Width + 2;
						item_size.Height = ImageList.ImageSize.Height + 3;
					}
				}

				if (width < MinimumTabWidth)
					width = MinimumTabWidth;
				if (page.Row != prev_row)
					ypos = 1;

				page.TabBounds = new Rectangle (xpos + (row_count - page.Row) * ((item_size.Height - 2) + spacing.Width),
						ypos, item_size.Height - 2, width);

				if (page.Row != prev_row) {
					if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
						FillRowV (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Bottom) / (i - begin_prev)), spacing);
					}
					begin_prev = i;
				}

				ypos += width + spacing.Width;
				prev_row = page.Row;
			}

			if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
				FillRowV (begin_prev, TabPages.Count - 1,
						((row_width - TabPages [TabPages.Count - 1].TabBounds.Bottom) / (TabPages.Count - begin_prev)), spacing);
			}

			if (SelectedIndex != -1) {
				ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
			}
		}

		private void SizeTabs (int row_width)
		{
			int ypos = CalcYPos ();
			int prev_row = 1;
			Size spacing = TabSpacing;
			int xpos = 4;
			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;

			for (int i = slider_pos; i < TabPages.Count; i++) {
				TabPage page = TabPages [i];
				int width;
 
				if (SizeMode == TabSizeMode.Fixed) {
					width = item_size.Width;
				} else {					
					width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
					if (ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < ImageList.Images.Count) {
						width += ImageList.ImageSize.Width + 2;
						item_size.Height = ImageList.ImageSize.Height + 3;
					}
				}

				if (width < MinimumTabWidth)
					width = MinimumTabWidth;
				if (page.Row != prev_row)
					xpos = 4;

				page.TabBounds = new Rectangle (xpos,
						ypos + (row_count - page.Row) * (item_size.Height + spacing.Height),
						width, item_size.Height);
				
				if (page.Row != prev_row) {
					if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
						FillRow (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Right) / (i - begin_prev)), spacing);
					}
					begin_prev = i;
				}

				xpos += width + 1 + spacing.Width;
				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);
			}

			if (SelectedIndex != -1) {
				ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
			}
		}
		
		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;

			if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom) {
				int l = page.TabBounds.Left - 4;
				int r = page.TabBounds.Right + 4;
				int y = page.TabBounds.Y;
				int h = page.TabBounds.Height + 3;

				if (l < left_edge)
					l = left_edge;
				if (r > right_edge && SizeMode != TabSizeMode.Normal)
					r = right_edge;
				if (Alignment == TabAlignment.Top)
					y -= 1;
				if (Alignment == TabAlignment.Bottom)
					y -= 2;

				page.TabBounds = new Rectangle (l, y, r - l, h);
			} else {
				int l = page.TabBounds.Left - 3;
				int r = page.TabBounds.Right + 3;
				int t = page.TabBounds.Top - 3;
				int b = page.TabBounds.Bottom + 3;

				if (t < left_edge)
					t = left_edge;
				if (b > right_edge)
					b = right_edge;

				page.TabBounds = new Rectangle (l, t, r - l, b - t);
			}
		}

		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)
		{
			((IList) Controls).Insert (index, value);
			Redraw ();
		}

		internal void Redraw ()
		{
			ResizeTabPages ();
			Refresh ();
		}

		#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 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; }
		}

		public event DrawItemEventHandler DrawItem;
		public event EventHandler SelectedIndexChanged;
		#endregion	// Events


		#region Class TaControl.ControlCollection
		public class ControlCollection : System.Windows.Forms.Control.ControlCollection {

			private TabControl owner;
			private ArrayList list = new ArrayList ();

			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 (Count == 1) {
					owner.SelectedIndex = 0;
				} else {
					// Setting the selected index will calc the tab rows so
					// we don't need to do it again
					owner.Redraw ();
				}
			}

			public override void Remove (Control value)
			{
				TabPage page = value as TabPage;
				if (page != null) {
					int index = owner.IndexForTabPage (page);
					if (index < owner.SelectedIndex || owner.SelectedIndex == Count - 1)
						owner.SelectedIndex--;
				}
				base.Remove (value);
			}
		}
		#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);
				}    
			}

			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 page)
			{
				if (page == null)
					throw new ArgumentNullException ("Value cannot be null.");
				owner.Controls.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 ();
			}

			public bool Contains (TabPage page)
			{
				if (page == null)
					throw new ArgumentNullException ("Value cannot be null.");
				return owner.Controls.Contains (page);
			}

			public IEnumerator GetEnumerator ()
			{
				return owner.Controls.GetEnumerator ();
			}

			public int IndexOf (TabPage page)
			{
				return owner.Controls.IndexOf (page);
			}

			public void Remove (TabPage page)
			{
				owner.Controls.Remove (page);
			}

			public void RemoveAt (int index)
			{
				owner.Controls.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 value)
			{
				TabPage page = value as TabPage;
				if (page == null)
					return false;
				return Contains (page);
			}

			int IList.IndexOf (object value)
			{
				TabPage page = value as TabPage;
				if (page == null)
					return -1;
				return IndexOf ((TabPage) page);
			}

			void IList.Insert (int index, object value)
			{
				throw new NotSupportedException ();
			}

			void IList.Remove (object value)
			{
				TabPage page = value as TabPage;
				if (page == null)
					return;
				Remove ((TabPage) value);
			}
		}
		#endregion	// Class TabPage.TabPageCollection
	}
}


