Click here to Skip to main content
15,891,621 members
Articles / Programming Languages / C#

SharpPrivacy - OpenPGP for C#

Rate me:
Please Sign up or sign in to vote.
4.92/5 (86 votes)
7 Jun 200314 min read 354K   8.4K   227  
SharpPrivacy is an OpenPGP implementation in C#. It can be used to encrypt and sign data, created OpenPGP compatible keys, and a lot more. This article explains how to use the library in your own .NET application or webpage to encrypt, sign, decrypt or verify OpenPGP messages.
// *****************************************************************************
// 
//  (c) Crownwood Consulting Limited 2002-2003
//  All rights reserved. The software and associated documentation 
//  supplied hereunder are the proprietary information of Crownwood Consulting 
//	Limited, Crownwood, Bracknell, Berkshire, England and are supplied subject 
//  to licence terms.
// 
//  Magic Version 1.7.4.0 	www.dotnetmagic.com
// *****************************************************************************

using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Resources;
using System.Reflection;
using System.Collections;
using System.Drawing.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Imaging;
using Microsoft.Win32;
using Crownwood.Magic.Win32;
using Crownwood.Magic.Menus;
using Crownwood.Magic.Common;
using Crownwood.Magic.Collections;

namespace Crownwood.Magic.Controls
{
    [ToolboxBitmap(typeof(TabControl))]
    [DefaultProperty("Appearance")]
    [DefaultEvent("SelectionChanged")]
    [Designer(typeof(Crownwood.Magic.Controls.TabControlDesigner))]
    public class TabControl : Panel
    {
        // Enumeration of appearance styles
        public enum VisualAppearance
        {
            MultiDocument = 0,
            MultiForm = 1,
            MultiBox = 2
        }

        // Enumeration of modes that control display of the tabs area
        public enum HideTabsModes
        {
            ShowAlways,
            HideAlways,
            HideUsingLogic,
            HideWithoutMouse
        }

        // Indexes into the menu images strip
        protected enum ImageStrip
        {
            LeftEnabled = 0,
            LeftDisabled = 1,
            RightEnabled = 2,
            RightDisabled = 3,
            Close = 4,
            Error = 5
        }

        // Enumeration of Indexes into positioning constants array
        protected enum PositionIndex
        {
            BorderTop			= 0,
            BorderLeft			= 1,
            BorderBottom		= 2, 
            BorderRight			= 3,
            ImageGapTop			= 4,
            ImageGapLeft		= 5,
            ImageGapBottom		= 6,
            ImageGapRight		= 7,
            TextOffset			= 8,
            TextGapLeft			= 9,
            TabsBottomGap		= 10,
            ButtonOffset		= 11,
        }

        // Helper class for handling multiline calculations
        protected class MultiRect
        {
            protected Rectangle _rect;
            protected int _index;

            public MultiRect(Rectangle rect, int index)
            {
                _rect = rect;
                _index = index;
            }

            public int Index
            {
                get { return _index; }
            }            
            
            public Rectangle Rect
            {
                get { return _rect; }
                set { _rect = value; }
            }
            
            public int X
            {
                get { return _rect.X; }
                set { _rect.X = value; }
            }

            public int Y
            {
                get { return _rect.Y; }
                set { _rect.Y = value; }
            }

            public int Width
            {
                get { return _rect.Width; }
                set { _rect.Width = value; }
            }

            public int Height
            {
                get { return _rect.Height; }
                set { _rect.Height = value; }
            }
        }
        
        // Class constants for sizing/positioning each style
        protected static int[,] _position = { 
                                                {3, 1, 1, 1, 1, 2, 1, 1, 2, 1, 3, 2},	// IDE
                                                {6, 2, 2, 3, 3, 1, 1, 0, 1, 1, 2, 0}	// Plain
                                            };

        // Class constants
        protected static int _plainBorder = 3;
        protected static int _plainBorderDouble = 6;
        protected static int _tabsAreaStartInset = 5;
        protected static int _tabsAreaEndInset = 5;
        protected static float _alphaIDE = 1.5F;
        protected static int _buttonGap = 3;
        protected static int _buttonWidth = 14;
        protected static int _buttonHeight = 14;
        protected static int _imageButtonWidth = 12;
        protected static int _imageButtonHeight = 12;
        protected static int _multiBoxAdjust = 2;
        protected readonly Rectangle _nullPosition = new Rectangle(-999, -999, 0, 0);

        // Class state
        protected static ImageList _internalImages;

        // Instance fields - size/positioning
        protected int _textHeight;
        protected int _imageWidth;
        protected int _imageHeight;
        protected int _imageGapTopExtra;
        protected int _imageGapBottomExtra;
        protected Rectangle _pageRect;
        protected Rectangle _pageAreaRect;
        protected Rectangle _tabsAreaRect;

        // Instance fields - state
        protected int _ctrlTopOffset;			// How far from top edge embedded controls should offset
        protected int _ctrlLeftOffset;			// How far from left edge embedded controls should offset
        protected int _ctrlRightOffset;			// How far from right edgeembedded controls should offset
        protected int _ctrlBottomOffset;	    // How far from bottom edge embedded controls should offset
        protected int _styleIndex;				// Index into position array
        protected int _pageSelected;			// index of currently selected page (-1 is none)
        protected int _startPage;				// index of first page to draw, used when scrolling pages
        protected int _hotTrackPage;			// which page is currently displayed as being tracked
        protected int _topYPos;                 // Y position of first line in multiline mode
        protected int _bottomYPos;              // Y position of last line in multiline mode
        protected int _leaveTimeout;            // How long from leaving to timeout occuring
        protected bool _dragFromControl;        // Must drag away from whole control before drag events generated
        protected bool _mouseOver;              // Mouse currently over the control (or child pages)
        protected bool _multiline;              // should tabs that cannot fit on a line create new lines
        protected bool _multilineFullWidth;     // when in multiline mode, all lines are extended to end 
        protected bool _shrinkPagesToFit;		// pages are shrunk so they all fit in control width
        protected bool _changed;				// Flag for use when updating contents of collection
        protected bool _positionAtTop;			// display tabs at top or bottom of the control
        protected bool _showClose;				// should the close button be displayed
        protected bool _showArrows;				// should then scroll arrow be displayed
        protected bool _insetPlain;				// Show the inset border for controls
        protected bool _insetBorderPagesOnly;   // Remove the border entirely for Plain mode
        protected bool _selectedTextOnly;	    // Only draw text for selected tab
        protected bool _rightScroll;			// Should the right scroll button be enabled
        protected bool _leftScroll;				// Should the left scroll button be enabled
        protected bool _dimUnselected;			// should unselected pages be drawn slightly dimmed
        protected bool _boldSelected;			// should selected page use a bold font
        protected bool _hotTrack;				// should mouve moving over text hot track it
        protected bool _hoverSelect;			// select a page when he mouse hovers over it
        protected bool _recalculate;			// flag to indicate recalculation is needed before painting
        protected bool _leftMouseDown;			// Is the left mouse button down
        protected bool _leftMouseDownDrag;		// Has a drag operation begun
        protected bool _ignoreDownDrag;         // When pressed the left button cannot generate two drags
        protected bool _defaultColor;			// Is the background color the default one?
        protected bool _defaultFont;			// Is the Font the default one?
		protected bool _recordFocus;			// Record the control with focus when leaving a page
        protected bool _idePixelArea;           // Place a one pixel border at top/bottom of tabs area
        protected bool _idePixelBorder;         // Place a one pixel border around control
		protected bool _dragOverSelect;			// Should dragging over a tab cause selection?
        protected PopupMenu _contextMenu;       // Context menu to show on right mouse up
        protected Point _leftMouseDownPos;		// Initial mouse down position for left mouse button
		protected Point _mouseDragOver;			// Mouse position for drag over event
        protected Color _hotTextColor;			// color for use when drawing text as hot
        protected Color _textColor;				// color for use when text not hot
        protected Color _textInactiveColor;	    // color for use when text not hot and not the active tab
        protected Color _backIDE;				// background drawing color when in IDE appearance
        protected Color _buttonActiveColor;		// color for drawing buttons images when active
        protected Color _buttonInactiveColor;	// color for drawing buttons images when inactive
        protected Color _backLight;				// light variation of the back color
        protected Color _backLightLight;		// lightlight variation of the back color
        protected Color _backDark;				// dark variation of the back color
        protected Color _backDarkDark;			// darkdark variation of the back color
        protected VisualStyle _style;			// which style of use 
        protected HideTabsModes _hideTabsMode;  // Decide when to hide/show tabs area
        protected Timer _overTimer;             // Time when mouse has left control
		protected Timer _dragTimer;             // Time when mouse hovers in drag over
		protected VisualAppearance _appearance;	// which appearance style
        protected ImageList _imageList;			// collection of images for use is tabs
        protected ArrayList _tabRects;			// display rectangles for associated page
        protected TabPageCollection _tabPages;	// collection of pages

        // Instance fields - buttons
        protected InertButton _closeButton;
        protected InertButton _leftArrow;
        protected InertButton _rightArrow;

        public delegate void DoubleClickTabHandler(TabControl sender, TabPage page);

        // Exposed events
        public event EventHandler ClosePressed;
        public event EventHandler SelectionChanging;
        public event EventHandler SelectionChanged;
        public event EventHandler PageGotFocus;
        public event EventHandler PageLostFocus;
        public event CancelEventHandler PopupMenuDisplay;
        public event MouseEventHandler PageDragStart;
        public event MouseEventHandler PageDragMove;
        public event MouseEventHandler PageDragEnd;
        public event MouseEventHandler PageDragQuit;
        public event DoubleClickTabHandler DoubleClickTab;

        static TabControl()
        {
            // Create a strip of images by loading an embedded bitmap resource
            _internalImages = ResourceHelper.LoadBitmapStrip(Type.GetType("Crownwood.Magic.Controls.TabControl"),
                                                             "MagicLibrary.ImagesTabControl.bmp",
                                                             new Size(_imageButtonWidth, _imageButtonHeight),
                                                             new Point(0,0));
        }

        public TabControl()
        {
            // Prevent flicker with double buffering and all painting inside WM_PAINT
			SetStyle(ControlStyles.DoubleBuffer | 
				     ControlStyles.AllPaintingInWmPaint |
				     ControlStyles.UserPaint, true);

            // Create collections
            _tabRects = new ArrayList();
            _tabPages = new TabPageCollection();

            // Hookup to collection events
            _tabPages.Clearing += new CollectionClear(OnClearingPages);
            _tabPages.Cleared += new CollectionClear(OnClearedPages);
            _tabPages.Inserting += new CollectionChange(OnInsertingPage);
            _tabPages.Inserted += new CollectionChange(OnInsertedPage);
            _tabPages.Removing += new CollectionChange(OnRemovingPage);
            _tabPages.Removed += new CollectionChange(OnRemovedPage);

            // Define the default state of the control
            _startPage = -1;						
            _pageSelected = -1;						
            _hotTrackPage = -1;					
            _imageList = null;						
            _insetPlain = true;
            _multiline = false;
            _multilineFullWidth = false;
            _dragFromControl = true;
            _mouseOver = false;
            _leftScroll = false;
            _defaultFont = true;
            _defaultColor = true;
            _rightScroll = false;
            _hoverSelect = false;
            _leftMouseDown = false;
            _ignoreDownDrag = true;
            _selectedTextOnly = false;
            _leftMouseDownDrag = false;
            _insetBorderPagesOnly = false;
			_dragOverSelect = true;
            _hideTabsMode = HideTabsModes.ShowAlways;
			_recordFocus = true;
            _styleIndex = 1;
            _leaveTimeout = 200;
            _ctrlTopOffset = 0;
            _ctrlLeftOffset = 0;
            _ctrlRightOffset = 0;
            _ctrlBottomOffset = 0;
            _style = VisualStyle.IDE;
            _buttonActiveColor = Color.FromArgb(128, this.ForeColor);
            _buttonInactiveColor = _buttonActiveColor;
            _textColor = TabControl.DefaultForeColor;	
            _textInactiveColor = Color.FromArgb(128, _textColor);
            _hotTextColor = SystemColors.ActiveCaption;

            // Create hover buttons
            _closeButton = new InertButton(_internalImages, (int)ImageStrip.Close);
            _leftArrow = new InertButton(_internalImages, (int)ImageStrip.LeftEnabled, (int)ImageStrip.LeftDisabled);
            _rightArrow = new InertButton(_internalImages, (int)ImageStrip.RightEnabled, (int)ImageStrip.RightDisabled);

            // We want our buttons to have very thin borders
            _closeButton.BorderWidth = _leftArrow.BorderWidth = _rightArrow.BorderWidth = 1;

            // Hookup to the button events
            _closeButton.Click += new EventHandler(OnCloseButton);
            _leftArrow.Click += new EventHandler(OnLeftArrow);
            _rightArrow.Click += new EventHandler(OnRightArrow);

            // Set their fixed sizes
            _leftArrow.Size = _rightArrow.Size = _closeButton.Size = new Size(_buttonWidth, _buttonHeight);

            // Add child controls
            Controls.AddRange(new Control[]{_closeButton, _leftArrow, _rightArrow});

            // Grab some contant values
            _imageWidth = 16;
            _imageHeight = 16;

            // Default to having a MultiForm usage
            SetAppearance(VisualAppearance.MultiForm);
            
            // Need a timer so that when the mouse leaves, a fractionaly delay occurs before
            // noticing and hiding the tabs area when the appropriate style is set
            _overTimer = new Timer();
            _overTimer.Interval = _leaveTimeout;
            _overTimer.Tick += new EventHandler(OnMouseTick);

			// Need a timer so that when the mouse hovers in drag over mode the page then changes
			_dragTimer = new Timer();
			_dragTimer.Interval = 200;
			_dragTimer.Tick += new EventHandler(OnDragOverTick);

            // Need notification when the MenuFont is changed
            Microsoft.Win32.SystemEvents.UserPreferenceChanged += 
                new UserPreferenceChangedEventHandler(OnPreferenceChanged);

            // Define the default Font, BackColor and Button images
            DefineFont(SystemInformation.MenuFont);
            DefineBackColor(SystemColors.Control);
            DefineButtonImages();
			ResetAllowDrop();
			ResetDragOverSelect();

            Recalculate();
        }

        protected override void Dispose( bool disposing )
        {
            if(disposing)
            {
                // Remove notifications
                Microsoft.Win32.SystemEvents.UserPreferenceChanged -= 
                    new UserPreferenceChangedEventHandler(OnPreferenceChanged);
            }
            base.Dispose(disposing);
        }

		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public new Control.ControlCollection Controls 
		{
			get { return base.Controls; }
		}

		[Category("Appearance")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public virtual TabPageCollection TabPages
        {
            get { return _tabPages; }
        }

        [Category("Appearance")]
        public override Font Font
        {
            get { return base.Font; }

            set
            {
				if (value != null)
				{
					if (value != base.Font)
					{
						_defaultFont = (value == SystemInformation.MenuFont);

						DefineFont(value);

						_recalculate = true;
						Invalidate();
					}
				}
            }
        }

        private bool ShouldSerializeFont()
        {
            return !_defaultFont;
        }

		[DefaultValue(true)]
		public override bool AllowDrop
		{
			get { return base.AllowDrop; }
			set { base.AllowDrop = value; }
		}

		public void ResetAllowDrop()
		{
			AllowDrop = true;
		}
        
        [Category("Appearance")]
        public override Color ForeColor
        {
            get { return _textColor; }
			
            set 
            {
                if (_textColor != value)
                {
                    _textColor = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        private bool ShouldSerializeForeColor()
        {
            return _textColor != TabControl.DefaultForeColor;
        }

        [Category("Appearance")]
        public override Color BackColor
        {
            get { return base.BackColor; }
			
            set 
            {
                if (this.BackColor != value)
                {
                    _defaultColor = (value == SystemColors.Control);

                    DefineBackColor(value);
		
                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        private bool ShouldSerializeBackColor()
        {
            return this.BackColor != SystemColors.Control;
        }

        [Category("Appearance")]
        public virtual Color ButtonActiveColor
        {
            get { return _buttonActiveColor; }

            set
            {
                if (_buttonActiveColor != value)
                {
                    _buttonActiveColor = value;
                    DefineButtonImages();
                }
            }
        }

        private bool ShouldSerializeButtonActiveColor()
        {
            return _buttonActiveColor != Color.FromArgb(128, this.ForeColor);
        }

        public void ResetButtonActiveColor()
        {
            ButtonActiveColor = Color.FromArgb(128, this.ForeColor);
        }

        [Category("Appearance")]
        public virtual Color ButtonInactiveColor
        {
            get { return _buttonInactiveColor; }

            set
            {
                if (_buttonInactiveColor != value)
                {
                    _buttonInactiveColor = value;
                    DefineButtonImages();
                }
            }
        }

        private bool ShouldSerializeButtonInactiveColor()
        {
            return _buttonInactiveColor != Color.FromArgb(128, this.ForeColor);
        }

        public void ResetButtonInactiveColor()
        {
            ButtonInactiveColor = Color.FromArgb(128, this.ForeColor); 
        }
        
        [Category("Appearance")]
        [DefaultValue(typeof(VisualAppearance), "MultiForm")]
        public virtual VisualAppearance Appearance
        {
            get { return _appearance; }
			
            set
            {
                if (_appearance != value)
                {
                    SetAppearance(value);

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetAppearance()
        {
            Appearance = VisualAppearance.MultiForm;
        }

        [Category("Appearance")]
        [DefaultValue(typeof(VisualStyle), "IDE")]
        public virtual VisualStyle Style
        {
            get { return _style; }
			
            set
            {
                if (_style != value)
                {
                    _style = value;

                    // Define the correct style indexer
                    SetStyleIndex();

                    Recalculate();
                    Invalidate();
                }
            }
        }
        
        public void ResetStyle()
        {
            Style = VisualStyle.IDE;
        }
        
        [Category("Behavour")]
        public virtual PopupMenu ContextPopupMenu
        {
            get { return _contextMenu; }
            set { _contextMenu = value; }
        }
        
        protected bool ShouldSerializeContextPopupMenu()
        {
            return _contextMenu != null;
        }
        
        public void ResetContextPopupMenu()
        {
            ContextPopupMenu = null;
        }

		[Category("Behavour")]
		[DefaultValue(true)]
		public virtual bool DragOverSelect
		{
			get { return _dragOverSelect; }
			set { _dragOverSelect = value; }
		}

		public void ResetDragOverSelect()
		{
			DragOverSelect = true;
		}

		[Category("Appearance")]
        [DefaultValue(false)]
        public virtual bool HotTrack
        {
            get { return _hotTrack; }
			
            set 
            {
                if (_hotTrack != value)
                {
                    _hotTrack = value;

                    if (!_hotTrack)
                        _hotTrackPage = -1;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        public void ResetHotTrack()
        {
            HotTrack = false;
        }

        [Category("Appearance")]
        public virtual Color HotTextColor
        {
            get { return _hotTextColor; }
			
            set 
            {
                if (_hotTextColor != value)
                {
                    _hotTextColor = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        private bool ShouldSerializeHotTextColor()
        {
            return _hotTextColor != SystemColors.ActiveCaption;
        }
        
        public void ResetHotTextColor()
        {
            HotTextColor = SystemColors.ActiveCaption;
        }

        [Category("Appearance")]
        public virtual Color TextColor
        {
            get { return _textColor; }
			
            set 
            {
                if (_textColor != value)
                {
                    _textColor = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        private bool ShouldSerializeTextColor()
        {
            return _textColor != TabControl.DefaultForeColor;
        }

        public void ResetTextColor()
        {   
            TextColor = TabControl.DefaultForeColor;
        }

        [Category("Appearance")]
        public virtual Color TextInactiveColor
        {
            get { return _textInactiveColor; }
			
            set 
            {
                if (_textInactiveColor != value)
                {
                    _textInactiveColor = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        private bool ShouldSerializeTextInactiveColor()
        {
            return _textInactiveColor != Color.FromArgb(128, TabControl.DefaultForeColor);
        }

        public void TextTextInactiveColor()
        {
            TextInactiveColor = Color.FromArgb(128, TabControl.DefaultForeColor);
        }

        [Browsable(false)]
        public virtual Rectangle TabsAreaRect
        {
            get { return _tabsAreaRect; }
        }

		private bool ShouldSerializeTabsAreaRect()
		{
			return false;
		}

        [Category("Appearance")]
        public virtual ImageList ImageList
        {
            get { return _imageList; }

            set 
            {
                if (_imageList != value)
                {
                    _imageList = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }
		
        private bool ShouldSerializeImageList()
        {
            return _imageList != null;
        }
        
        public void ResetImageList()
        {
            ImageList = null;
        }

        [Category("Appearance")]
        public virtual bool PositionTop
        {
            get { return _positionAtTop; }
			
            set
            {
                if (_positionAtTop != value)
                {
                    _positionAtTop = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        protected bool ShouldSerializePositionTop()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    return _positionAtTop != false;
                case VisualAppearance.MultiDocument:
                default:
                    return _positionAtTop != true;
            }
        }

        public void ResetPositionTop()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    PositionTop = false;
                    break;
                case VisualAppearance.MultiDocument:
                default:
                    PositionTop = true;
                    break;
            }
        }

        [Category("Appearance")]
        public virtual bool ShowClose
        {
            get { return _showClose; }
			
            set
            {
                if (_showClose != value)
                {
                    _showClose = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }
        
        protected bool ShouldSerializeShowClose()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    return _showClose != false;
                case VisualAppearance.MultiDocument:
                default:
                    return _showClose != true;
            }
        }

        public void ResetShowClose()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    ShowClose = false;
                    break;
                case VisualAppearance.MultiDocument:
                default:
                    ShowClose = true;
                    break;
            }
        }

        [Category("Appearance")]
        public virtual bool ShowArrows
        {
            get { return _showArrows; }
			
            set
            {
                if (_showArrows != value)
                {
                    _showArrows = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        protected bool ShouldSerializeShowArrows()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    return _showArrows != false;
                case VisualAppearance.MultiDocument:
                default:
                    return _showArrows != true;
            }
        }
        
        public void ResetShowArrows()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    ShowArrows = false;
                    break;
                case VisualAppearance.MultiDocument:
                default:
                    ShowArrows = true;
                    break;
            }
        }

        [Category("Appearance")]
        public virtual bool ShrinkPagesToFit
        {
            get { return _shrinkPagesToFit; }
			
            set 
            {
                if (_shrinkPagesToFit != value)
                {
                    _shrinkPagesToFit = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        protected bool ShouldSerializeShrinkPagesToFit()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    return _shrinkPagesToFit != true;
                case VisualAppearance.MultiDocument:
                default:
                    return _shrinkPagesToFit != false;
            }
        }
        
        public void ResetShrinkPagesToFit()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    ShrinkPagesToFit = true;
                    break;
                case VisualAppearance.MultiDocument:
                default:
                    ShrinkPagesToFit = false;
                    break;
            }
        }

        [Category("Appearance")]
        public virtual bool BoldSelectedPage
        {
            get { return _boldSelected; }
			
            set 
            {
                if (_boldSelected != value)
                {
                    _boldSelected = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        protected bool ShouldSerializeBoldSelectedPage()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    return _boldSelected != false;
                case VisualAppearance.MultiDocument:
                default:
                    return _boldSelected != true;
            }
        }
        
        public void ResetBoldSelectedPage()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    BoldSelectedPage = false;
                    break;
                case VisualAppearance.MultiDocument:
                default:
                    BoldSelectedPage = true;
                    break;
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        public virtual bool MultilineFullWidth
        {
            get { return _multilineFullWidth; }
            
            set
            {
                if (_multilineFullWidth != value)
                {
                    _multilineFullWidth = value;
                    
                    _recalculate = true;
                    Invalidate();
                }
            }
        }
        
        public void ResetMultilineFullWidth()
        {
            MultilineFullWidth = false;
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        public virtual bool Multiline
        {
            get { return _multiline; }
			
            set 
            {
                if (_multiline != value)
                {
                    _multiline = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }
        
        public void ResetMultiline()
        {
            Multiline = false;
        }

        [Category("Appearance")]
        [DefaultValue(0)]
        public virtual int ControlLeftOffset
        {
            get { return _ctrlLeftOffset; }
			
            set 
            {
                if (_ctrlLeftOffset != value)
                {
                    _ctrlLeftOffset = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetControlLeftOffset()
        {
            ControlLeftOffset = 0;
        }

        [Category("Appearance")]
        [DefaultValue(0)]
        public virtual int ControlTopOffset
        {
            get { return _ctrlTopOffset; }
			
            set 
            {
                if (_ctrlTopOffset != value)
                {
                    _ctrlTopOffset = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetControlTopOffset()
        {
            ControlTopOffset = 0;
        }

        [Category("Appearance")]
        [DefaultValue(0)]
        public virtual int ControlRightOffset
        {
            get { return _ctrlRightOffset; }
			
            set 
            {
                if (_ctrlRightOffset != value)
                {
                    _ctrlRightOffset = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetControlRightOffset()
        {
            ControlRightOffset = 0;
        }

        [Category("Appearance")]
        [DefaultValue(0)]
        public virtual int ControlBottomOffset
        {
            get { return _ctrlBottomOffset; }
			
            set 
            {
                if (_ctrlBottomOffset != value)
                {
                    _ctrlBottomOffset = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetControlBottomOffset()
        {
            ControlBottomOffset = 0;
        }

        [Category("Appearance")]
        [DefaultValue(true)]
        public virtual bool InsetPlain
        {
            get { return _insetPlain; }
			
            set 
            {
                if (_insetPlain != value)
                {
                    _insetPlain = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetInsetPlain()
        {
            InsetPlain = true;
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        public virtual bool InsetBorderPagesOnly
        {
            get { return _insetBorderPagesOnly; }
			
            set 
            {
                if (_insetBorderPagesOnly != value)
                {
                    _insetBorderPagesOnly = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetInsetBorderPagesOnly()
        {
            InsetBorderPagesOnly = true;
        }

        [Category("Appearance")]
        public virtual bool IDEPixelBorder
        {
            get { return _idePixelBorder; }
			
            set 
            {
                if (_idePixelBorder != value)
                {
                    _idePixelBorder = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        protected bool ShouldSerializeIDEPixelBorder()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    return _idePixelBorder != false;
                case VisualAppearance.MultiDocument:
                default:
                    return _idePixelBorder != true;
            }
        }

        public void ResetIDEPixelBorder()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                case VisualAppearance.MultiForm:
                    IDEPixelBorder = false;
                    break;
                case VisualAppearance.MultiDocument:
                default:
                    IDEPixelBorder = true;
                    break;
            }
        }

        [Category("Appearance")]
        public virtual bool IDEPixelArea
        {
            get { return _idePixelArea; }
			
            set 
            {
                if (_idePixelArea != value)
                {
                    _idePixelArea = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        public void ResetIDEPixelArea()
        {
            IDEPixelArea = true;
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        public virtual bool SelectedTextOnly
        {
            get { return _selectedTextOnly; }
			
            set 
            {
                if (_selectedTextOnly != value)
                {
                    _selectedTextOnly = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        public void ResetSelectedTextOnly()
        {
            SelectedTextOnly = false;
        }

        [Category("Behavour")]
        [DefaultValue(200)]
        public int MouseLeaveTimeout
        {
            get { return _leaveTimeout; }
            
            set 
            { 
                if (_leaveTimeout != value)
                {
                    _leaveTimeout = value;
                    _overTimer.Interval = value;
                }
            }
        }

        public void ResetMouseLeaveTimeout()
        {
            _leaveTimeout = 200;
        }

        [Category("Behavour")]
        [DefaultValue(true)]
        public bool DragFromControl
        {
            get { return _dragFromControl; }
            set { _dragFromControl = value ; }
        }
        
        public void ResetDragFromControl()
        {
            DragFromControl = true;
        }
        
        [Category("Appearance")]
        [DefaultValue(typeof(HideTabsModes), "ShowAlways")]
        public virtual HideTabsModes HideTabsMode
        {
            get { return _hideTabsMode; }
			
            set 
            {
                if (_hideTabsMode != value)
                {
                    _hideTabsMode = value;

                    Recalculate();
                    Invalidate();
                }
            }
        }

        protected bool ShouldSerializeHideTabsMode()
        {
            return HideTabsMode != HideTabsModes.ShowAlways;
        }

        public void ResetHideTabsMode()
        {
            HideTabsMode = HideTabsModes.ShowAlways;
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        public virtual bool HoverSelect
        {
            get { return _hoverSelect; }
			
            set 
            {
                if (_hoverSelect != value)
                {
                    _hoverSelect = value;

                    _recalculate = true;
                    Invalidate();
                }
            }
        }

        public void ResetHoverSelect()
        {
            HoverSelect = false;
        }

        [Category("Behavour")]
        [DefaultValue(true)]
        public virtual bool RecordFocus
        {
            get { return _recordFocus; }
			
            set 
            {
                if (_recordFocus != value)
                    _recordFocus = value;
            }
        }

        public void ResetRecordFocus()
        {
            RecordFocus = true;
        }

        [Browsable(false)]
        [DefaultValue(-1)]
        public virtual int SelectedIndex
        {
            get { return _pageSelected; }

            set
            {
                if ((value >= 0) && (value < _tabPages.Count))
                {
                    if (_pageSelected != value)
                    {
                        // Raise selection changing event
                        OnSelectionChanging(EventArgs.Empty);

                        // Any page currently selected?
                        if (_pageSelected != -1)
                            DeselectPage(_tabPages[_pageSelected]);

                        _pageSelected = value;

                        if (_pageSelected != -1)
                        {
                            SelectPage(_tabPages[_pageSelected]);

                            // If newly selected page is scrolled off the left hand side
                            if (_pageSelected < _startPage)
                                _startPage = _pageSelected;  // then bring it into view
                        }

                        // Change in selection causes tab pages sizes to change
                        if (_boldSelected)
                        {
                            Recalculate();
                            Invalidate();
                        }

                        // Raise selection change event
                        OnSelectionChanged(EventArgs.Empty);

                        Invalidate();
                    }
                }
            }
        }

        [Browsable(false)]
        [DefaultValue(null)]
        public virtual TabPage SelectedTab
        {
            get 
            {
                // If nothing is selected we return null
                if (_pageSelected == -1)
                    return null;
                else
                    return _tabPages[_pageSelected];
            }

            set
            {
                // Cannot change selection to be none of the tabs
                if (value != null)
                {
                    // Get the requested page from the collection
                    int index = _tabPages.IndexOf(value);

                    // If a valid known page then using existing property to perform switch
                    if (index != -1)
                        this.SelectedIndex = index;
                }
            }
        }

		public TabPage TabPageFromPoint(Point mousePos)
		{
			// Clicked on a tab page?
			for(int i=0; i<_tabPages.Count; i++)
			{
				Rectangle rect = (Rectangle)_tabRects[i];

				// Does tab page rectangle contain mouse?
				if (rect.Contains(mousePos))
				{
					// Is left scroll button showing?
					if (_leftArrow.Visible)
					{
						// Ignore mouse down over then buttons area
						if (mousePos.X >= _leftArrow.Left)
							return null;
					}
					else
					{
						// No, is the close button showing?
						if (_closeButton.Visible)
						{
							// Ignore mouse down over then close button area
							if (mousePos.X >= _closeButton.Left)
								return null;
						}
					}

					return _tabPages[i];
				}
			}

			// No matching tab header found
			return null;
		}

		public virtual void MakePageVisible(TabPage page)
        {
            MakePageVisible(_tabPages.IndexOf(page));
        }

        public virtual void MakePageVisible(int index)
        {
            // Only relevant if we do not shrink all pages to fit and not in multiline
            if (!_shrinkPagesToFit && !_multiline)
            {
                // Range check the request page
                if ((index >= 0) && (index < _tabPages.Count))
                {
                    // Is requested page before those shown?
                    if (index < _startPage)
                    {
                        // Define it as the new start page
                        _startPage = index;

                        _recalculate = true;
                        Invalidate();
                    }
                    else
                    {
                        // Find the last visible position
                        int xMax = GetMaximumDrawPos();

                        Rectangle rect = (Rectangle)_tabRects[index];

                        // Is the page drawn off over the maximum position?
                        if (rect.Right >= xMax)
                        {
                            // Need to find the new start page to bring this one into view
                            int newStart = index;

                            // Space left over for other tabs to be drawn inside
                            int spaceLeft = xMax - rect.Width - _tabsAreaRect.Left - _tabsAreaStartInset;

                            do
                            {
                                // Is there a previous tab to check?
                                if (newStart == 0)
                                    break;

                                Rectangle rectStart = (Rectangle)_tabRects[newStart - 1];
		
                                // Is there enough space to draw it?
                                if (rectStart.Width > spaceLeft)
                                    break;

                                // Move to new tab and reduce available space left
                                newStart--;
                                spaceLeft -= rectStart.Width;

                            } while(true);

                            // Define the new starting page
                            _startPage = newStart;

                            _recalculate = true;
                            Invalidate();
                        }
                    }
                }
            }
        }

        protected override bool ProcessMnemonic(char key)
        {
            int total = _tabPages.Count;
            int index = this.SelectedIndex + 1;
            
            for(int count=0; count<total; count++, index++)
            {
                // Range check the index
                if (index >= total)
                    index = 0;

                TabPage page = _tabPages[index];
        
                // Find position of first mnemonic character
                int position = page.Title.IndexOf('&');

                // Did we find a mnemonic indicator?
                if (IsMnemonic(key, page.Title))
                {
                    // Select this page
                    this.SelectedTab = page;
                
                    return true;
                }
            }
                        
            return false;
        }

        protected override void OnResize(EventArgs e)
        {
            Recalculate();
            Invalidate();
            
            base.OnResize(e);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            Recalculate();
            Invalidate();

            base.OnSizeChanged(e);
        }

        public virtual void OnPopupMenuDisplay(CancelEventArgs e)
        {
            // Has anyone registered for the event?
            if (PopupMenuDisplay != null)
                PopupMenuDisplay(this, e);
        }

        public virtual void OnSelectionChanging(EventArgs e)
        {
            // Has anyone registered for the event?
            if (SelectionChanging != null)
                SelectionChanging(this, e);
        }

        public virtual void OnSelectionChanged(EventArgs e)
        {
            // Has anyone registered for the event?
            if (SelectionChanged != null)
                SelectionChanged(this, e);
        }

        public virtual void OnClosePressed(EventArgs e)
        {
            // Has anyone registered for the event?
            if (ClosePressed != null)
                ClosePressed(this, e);
        }

        public virtual void OnPageGotFocus(EventArgs e)
        {
            // Has anyone registered for the event?
            if (PageGotFocus != null)
                PageGotFocus(this, e);
        }
		
        public virtual void OnPageLostFocus(EventArgs e)
        {
			// Has anyone registered for the event?
            if (PageLostFocus != null)
                PageLostFocus(this, e);
        }

        public virtual void OnPageDragStart(MouseEventArgs e)
        {
            // Has anyone registered for the event?
            if (PageDragStart != null)
                PageDragStart(this, e);
        }

        public virtual void OnPageDragMove(MouseEventArgs e)
        {
            // Has anyone registered for the event?
            if (PageDragMove != null)
                PageDragMove(this, e);
        }

        public virtual void OnPageDragEnd(MouseEventArgs e)
        {
            // Has anyone registered for the event?
            if (PageDragEnd != null)
                PageDragEnd(this, e);
        }

        public virtual void OnPageDragQuit(MouseEventArgs e)
        {
            // Has anyone registered for the event?
            if (PageDragQuit != null)
                PageDragQuit(this, e);
        }
        
        public virtual void OnDoubleClickTab(TabPage page)
        {
            // Has anyone registered for the event?
            if (DoubleClickTab != null)
                DoubleClickTab(this, page);
        }

        protected virtual void OnCloseButton(object sender, EventArgs e)
        {
            OnClosePressed(EventArgs.Empty);
        }

        protected virtual void OnLeftArrow(object sender, EventArgs e)
        {
            // Set starting page back one
            _startPage--;

            _recalculate = true;
            Invalidate();
        }
	
        protected virtual void OnRightArrow(object sender, EventArgs e)
        {
            // Set starting page forward one
            _startPage++;

            _recalculate = true;
            Invalidate();
        }

        protected virtual void DefineFont(Font newFont)
        {
            // Use base class for storage of value
            base.Font = newFont;
	
            // Update internal height value using Font
            _textHeight = newFont.Height;

            // Is the font height bigger than the image height?
            if (_imageHeight >= _textHeight)
            {
                // No, do not need extra spacing around the image to fit in text
                _imageGapTopExtra = 0;
                _imageGapBottomExtra = 0;
            }
            else
            {
                // Yes, need to make the image area bigger so that its height calculation
                // matchs that height of the text
                int extraHeight = _textHeight - _imageHeight;

                // Split the extra height between the top and bottom of image
                _imageGapTopExtra = extraHeight / 2;
                _imageGapBottomExtra = extraHeight - _imageGapTopExtra;
            }
        }

        protected virtual void DefineBackColor(Color newColor)
        {
            base.BackColor = newColor;

            // Calculate the modified colors from this base
            _backLight = ControlPaint.Light(newColor);
            _backLightLight = ControlPaint.LightLight(newColor);
            _backDark = ControlPaint.Dark(newColor);
            _backDarkDark = ControlPaint.DarkDark(newColor);

            _backIDE = ColorHelper.TabBackgroundFromBaseColor(newColor);
        }
		
        protected virtual void DefineButtonImages()
        {
            ImageAttributes ia = new ImageAttributes();

            ColorMap activeMap = new ColorMap();
            ColorMap inactiveMap = new ColorMap();

            // Define the color transformations needed
            activeMap.OldColor = Color.Black;
            activeMap.NewColor = _buttonActiveColor;
            inactiveMap.OldColor = Color.White;
            inactiveMap.NewColor = _buttonInactiveColor;

            // Create remap attributes for use by button
            ia.SetRemapTable(new ColorMap[]{activeMap, inactiveMap}, ColorAdjustType.Bitmap);

            // Pass attributes to the buttons
            _leftArrow.ImageAttributes = ia;
            _rightArrow.ImageAttributes = ia;
            _closeButton.ImageAttributes = ia;
        }

        protected virtual void SetAppearance(VisualAppearance appearance)
        {
            switch(appearance)
            {
                case VisualAppearance.MultiForm:
                case VisualAppearance.MultiBox:
                    _shrinkPagesToFit = true;					// shrink tabs to fit width
                    _positionAtTop = false;						// draw tabs at bottom of control
                    _showClose = false;							// do not show the close button
                    _showArrows = false;						// do not show the scroll arrow buttons
                    _boldSelected = false;						// do not show selected pages in bold
                    _idePixelArea = true;                       // show a one pixel border at top or bottom
                    _idePixelBorder = false;                     // do not show a one pixel border round control
                    break;
                case VisualAppearance.MultiDocument:
                    _shrinkPagesToFit = false;					// shrink tabs to fit width
                    _positionAtTop = true;						// draw tabs at bottom of control
                    _showClose = true;							// do not show the close button
                    _showArrows = true;						    // do not show the scroll arrow buttons
                    _boldSelected = true;						// do not show selected pages in bold
                    _idePixelArea = true;                       // show a one pixel border at top or bottom
                    _idePixelBorder = false;                     // do not show a one pixel border round control
                    break;
            }

            // These properties are the same whichever style is selected
            _hotTrack = false;							// do not hot track paes
            _dimUnselected = true;						// draw dimmed non selected pages

            // Define then starting page for drawing
            if (_tabPages.Count > 0)
                _startPage = 0;
            else
                _startPage = -1;

            _appearance = appearance;

            // Define the correct style indexer
            SetStyleIndex();
        }

        protected virtual void SetStyleIndex()
        {
            switch(_appearance)
            {
                case VisualAppearance.MultiBox:
                    // Always pretend we are plain style
                    _styleIndex = 1;
                    break;
                case VisualAppearance.MultiForm:
                case VisualAppearance.MultiDocument:
                    _styleIndex = (int)_style;
                    break;
            }
        }

        protected virtual bool HideTabsCalculation()
        {
            bool hideTabs = false;
        
            switch(_hideTabsMode)
            {
                case HideTabsModes.ShowAlways:
                    hideTabs = false;
                    break;
                case HideTabsModes.HideAlways:
                    hideTabs = true;
                    break;
                case HideTabsModes.HideUsingLogic:
                    hideTabs = (_tabPages.Count <= 1);                            
                    break;
                case HideTabsModes.HideWithoutMouse:
                    hideTabs = !_mouseOver;
                    break;
            }
            
            return hideTabs;
        }
		
        protected virtual void Recalculate()
        {
            // Reset the need for a recalculation
            _recalculate = false;

            // The height of a tab button is...
            int tabButtonHeight = _position[_styleIndex, (int)PositionIndex.ImageGapTop] + 
                                  _imageGapTopExtra +
                                  _imageHeight + 
                                  _imageGapBottomExtra + 
                                  _position[_styleIndex, (int)PositionIndex.ImageGapBottom] +
                                  _position[_styleIndex, (int)PositionIndex.BorderBottom]; 

            // The height of the tabs area is...
            int tabsAreaHeight = _position[_styleIndex, (int)PositionIndex.BorderTop] + 
                                 tabButtonHeight + _position[_styleIndex, (int)PositionIndex.TabsBottomGap];

			bool hideTabsArea = HideTabsCalculation();

            // Should the tabs area be hidden?
            if (hideTabsArea)
            {
                // ... then do not show the tabs or button controls
                _pageAreaRect = new Rectangle(0, 0, this.Width, this.Height);
                _tabsAreaRect = new Rectangle(0, 0, 0, 0);
            }
            else
            {
                if (_positionAtTop)
                {
                    // Create rectangle that represents the entire tabs area
                    _pageAreaRect = new Rectangle(0, tabsAreaHeight, this.Width, this.Height - tabsAreaHeight);

                    // Create rectangle that represents the entire area for pages
                    _tabsAreaRect = new Rectangle(0, 0, this.Width, tabsAreaHeight);
                }
                else
                {
                    // Create rectangle that represents the entire tabs area
                    _tabsAreaRect = new Rectangle(0, this.Height - tabsAreaHeight, this.Width, tabsAreaHeight);

                    // Create rectangle that represents the entire area for pages
                    _pageAreaRect = new Rectangle(0, 0, this.Width, this.Height - tabsAreaHeight);
                }
            }

            int xEndPos = 0;

            if (!hideTabsArea && _tabPages.Count > 0)
            {
                // The minimum size of a button includes its left and right borders for width,
                // and then fixed height which is based on the size of the image and font
                Rectangle tabPosition;
				
                if (_positionAtTop)
                    tabPosition = new Rectangle(0,		
                                                _tabsAreaRect.Bottom - tabButtonHeight - 
                                                _position[_styleIndex, (int)PositionIndex.BorderTop],
                                                _position[_styleIndex, (int)PositionIndex.BorderLeft] + 
                                                _position[_styleIndex, (int)PositionIndex.BorderRight],
                                                tabButtonHeight);
                else
                    tabPosition = new Rectangle(0,		
                                                _tabsAreaRect.Top + 
                                                _position[_styleIndex, (int)PositionIndex.BorderTop],
                                                _position[_styleIndex, (int)PositionIndex.BorderLeft] + 
                                                _position[_styleIndex, (int)PositionIndex.BorderRight],
                                                tabButtonHeight);

                // Find starting and ending positons for drawing tabs
                int xStartPos = _tabsAreaRect.Left + _tabsAreaStartInset;
                xEndPos = GetMaximumDrawPos();

                // Available width for tabs is size between start and end positions
                int xWidth = xEndPos - xStartPos;

                if (_multiline)
                    RecalculateMultilineTabs(xStartPos, xEndPos, tabPosition, tabButtonHeight);
                else
                    RecalculateSinglelineTabs(xWidth, xStartPos, tabPosition);
            }

            // Position of Controls defaults to the entire page area
            _pageRect = _pageAreaRect;

            // Adjust child controls positions depending on style
            if ((_style == VisualStyle.Plain) && (_appearance != VisualAppearance.MultiBox))
            {
                _pageRect = _pageAreaRect;

                // Shrink by having a border on left,top and right borders
                _pageRect.X += _plainBorderDouble;
                _pageRect.Width -= (_plainBorderDouble * 2) - 1;

                if (!_positionAtTop)
                    _pageRect.Y += _plainBorderDouble;

                _pageRect.Height -= _plainBorderDouble - 1;
				
                // If hiding the tabs then need to adjust the controls positioning
                if (hideTabsArea)
                {
                    _pageRect.Height -= _plainBorderDouble;

                    if (_positionAtTop)
                        _pageRect.Y += _plainBorderDouble;
                }
            }

            // Calcualte positioning of the child controls/forms
            int leftOffset = _ctrlLeftOffset;
            int rightOffset = _ctrlRightOffset;
            int topOffset = _ctrlTopOffset;
            int bottomOffset = _ctrlBottomOffset;

            if (_idePixelBorder && (_style == VisualStyle.IDE))
            {
                leftOffset += 2;
                rightOffset += 2;

                if (_positionAtTop || hideTabsArea)
                    bottomOffset += 2;
                    
                if (!_positionAtTop || hideTabsArea)
                    topOffset += 2;
            }
        
            Point pageLoc = new Point(_pageRect.Left + leftOffset,
                                      _pageRect.Top + topOffset);

            Size pageSize = new Size(_pageRect.Width - leftOffset - rightOffset,
                                     _pageRect.Height - topOffset - bottomOffset);

            // If in Plain style and requested to only show top or bottom border
            if ((_style == VisualStyle.Plain) && _insetBorderPagesOnly)
            {
                // Then need to increase width to occupy where borders would have been 
                pageLoc.X -= _plainBorderDouble;
                pageSize.Width += _plainBorderDouble * 2;

                if (hideTabsArea || _positionAtTop)
                {
                    // Draw into the bottom border area
                    pageSize.Height += _plainBorderDouble;
                }

                if (hideTabsArea || !_positionAtTop)
                {
                    // Draw into the top border area
                    pageLoc.Y -= _plainBorderDouble;
                    pageSize.Height += _plainBorderDouble;
                }
            }

			// Position each page control correctly
			foreach(TabPage page in _tabPages)
			{
				if (page.Control != null)
				{
					page.Control.Size = pageSize;
					page.Control.Location = pageLoc;
				}
				else
				{
					page.Size = pageSize;
					page.Location = pageLoc;
				}
			}
            
			// If we have any tabs at all
            if (_tabPages.Count > 0)
            {
                Rectangle rect = (Rectangle)_tabRects[_tabPages.Count - 1];
				
				// Determine is the right scrolling button should be enabled
                _rightScroll = (rect.Right > xEndPos);
            }
            else
            {
                // No pages means there can be no right scrolling
                _rightScroll = false;
            }

            // Determine if left scrolling is possible
            _leftScroll = (_startPage > 0);

            // Handle then display and positioning of buttons
            RecalculateButtons();
        }

        protected virtual void RecalculateMultilineTabs(int xStartPos, int xEndPos, 
                                                        Rectangle tabPosition, int tabButtonHeight)
        {
            using (Graphics g = this.CreateGraphics())
            {
                // MultiBox style needs a pixel extra drawing room on right
                if (_appearance == VisualAppearance.MultiBox)
                    xEndPos-=2;
                        
                // How many tabs on this line
                int lineCount = 0;
                            
                // Remember which line is the first displayed
                _topYPos = tabPosition.Y;
                            
                // Next tab starting position
                int xPos = xStartPos;
                int yPos = tabPosition.Y;
                            
                // How many full lines were there
                int fullLines = 0;
                            
                // Line increment value
                int lineIncrement = tabButtonHeight + 1;

                // Track which line has the selection on it                                
                int selectedLine = 0;

                // Vertical adjustment
                int yAdjust = 0;
                        
                // Create array for holding lines of tabs
                ArrayList lineList = new ArrayList();
                            
                // Add the initial line
                lineList.Add(new ArrayList());
                        
                // Process each tag page in turn
                for(int i=0; i<_tabPages.Count; i++)
                {
                    // Get the tab instance for this position
                    TabPage page = _tabPages[i];
                            
                    // Find out the tabs total width
                    int tabWidth = GetTabPageSpace(g, page);
                                
                    // If not the first on the line, then check if newline should be started
                    if (lineCount > 0)
                    {
                        // Does this tab extend pass end of the lines
                        if ((xPos + tabWidth) > xEndPos)
                        {
                            // Next tab position is down a line and back to the start
                            xPos = xStartPos;
                            yPos += lineIncrement;
                                        
                            // Remember which line is the last displayed
                            _bottomYPos = tabPosition.Y;

                            // Increase height of the tabs area
                            _tabsAreaRect.Height += lineIncrement;
                                        
                            // Decrease height of the control area
                            _pageAreaRect.Height -= lineIncrement;
                                        
                            // Moving areas depends on drawing at top or bottom
                            if (_positionAtTop)
                                _pageAreaRect.Y += lineIncrement;
                            else
                            {
                                yAdjust -= lineIncrement;
                                _tabsAreaRect.Y -= lineIncrement;
                            }
                                        
                            // Start a new line 
                            lineList.Add(new ArrayList());
                                        
                            // Make sure the entries are aligned to fill entire line
                            fullLines++;
                        }
                    }

                    // Limit the width of a tab to the whole line
                    if (tabWidth > (xEndPos - xStartPos))
                        tabWidth = xEndPos - xStartPos;
                                
                    // Construct rectangle for representing this tab
                    Rectangle tabRect = new Rectangle(xPos, yPos, tabWidth, tabButtonHeight);
                                
                    // Add this tab to the current line array
                    ArrayList thisLine = lineList[lineList.Count - 1] as ArrayList;
                                
                    // Create entry to represent the sizing of the given page index
                    MultiRect tabEntry = new MultiRect(tabRect, i);
                                
                    thisLine.Add(tabEntry);
                                
                    // Track which line has the selection on it
                    if (i == _pageSelected)
                        selectedLine = fullLines;
                                
                    // Move position of next tab along
                    xPos += tabWidth + 1;
                                
                    // Increment number of tabs on this line
                    lineCount++;
                }

                int line = 0;

                // Do we need all lines to extend full width
                if (_multilineFullWidth)
                    fullLines++;
                            
                // Make each full line stretch the whole line width
                foreach(ArrayList lineArray in lineList)
                {
                    // Only right fill the full lines
                    if (line < fullLines)
                    {
                        // Number of items on this line
                        int numLines = lineArray.Count;
                                
                        // Find ending position of last entry
                        MultiRect itemEntry = (MultiRect)lineArray[numLines - 1];

                        // Is there spare room between last entry and end of line?                            
                        if (itemEntry.Rect.Right < (xEndPos - 1))
                        {
                            // Work out how much extra to give each one
                            int extra = (int)((xEndPos - itemEntry.Rect.Right - 1) / numLines);
                                        
                            // Keep track of how much items need moving across
                            int totalMove = 0;
                                        
                            // Add into each entry
                            for(int i=0; i<numLines; i++)
                            {
                                // Get the entry class instance
                                MultiRect expandEntry = (MultiRect)lineArray[i];
                                        
                                // Move across requried amount
                                expandEntry.X += totalMove;
                                            
                                // Add extra width
                                expandEntry.Width += (int)extra;

                                // All items after this needing moving
                                totalMove += extra;
                            }
                                        
                            // Extend the last position, in case rounding errors means its short
                            itemEntry.Width += (xEndPos - itemEntry.Rect.Right - 1);
                        }
                    }
                                
                    line++;
                }

                if (_positionAtTop)
                {
                    // If the selected line is not the bottom line
                    if (selectedLine != (lineList.Count - 1))
                    {
                        ArrayList lastLine = (ArrayList)(lineList[lineList.Count - 1]);
                                    
                        // Find y offset of last line
                        int lastOffset = ((MultiRect)lastLine[0]).Rect.Y;
                                
                        // Move all lines below it up one
                        for(int lineIndex=selectedLine+1; lineIndex<lineList.Count; lineIndex++)
                        {
                            ArrayList al = (ArrayList)lineList[lineIndex];
                                    
                            for(int item=0; item<al.Count; item++)
                            {
                                MultiRect itemEntry = (MultiRect)al[item];
                                itemEntry.Y -= lineIncrement;
                            }
                        }
                                    
                        // Move selected line to the bottom
                        ArrayList sl = (ArrayList)lineList[selectedLine];
                                    
                        for(int item=0; item<sl.Count; item++)
                        {
                            MultiRect itemEntry = (MultiRect)sl[item];
                            itemEntry.Y = lastOffset;
                        }
                    }
                }
                else
                {
                    // If the selected line is not the top line
                    if (selectedLine != 0)
                    {
                        ArrayList topLine = (ArrayList)(lineList[0]);
                                    
                        // Find y offset of top line
                        int topOffset = ((MultiRect)topLine[0]).Rect.Y;
                                
                        // Move all lines above it down one
                        for(int lineIndex=0; lineIndex<selectedLine; lineIndex++)
                        {
                            ArrayList al = (ArrayList)lineList[lineIndex];
                                    
                            for(int item=0; item<al.Count; item++)
                            {
                                MultiRect itemEntry = (MultiRect)al[item];
                                itemEntry.Y += lineIncrement;
                            }
                        }
                                    
                        // Move selected line to the top
                        ArrayList sl = (ArrayList)lineList[selectedLine];
                                    
                        for(int item=0; item<sl.Count; item++)
                        {
                            MultiRect itemEntry = (MultiRect)sl[item];
                            itemEntry.Y = topOffset;
                        }
                    }
                }

                // Now assignt each lines rectangle to the corresponding structure
                foreach(ArrayList al in lineList)
                {
                    foreach(MultiRect multiEntry in al)
                    {
                        Rectangle newRect = multiEntry.Rect;
                                    
                        // Make the vertical adjustment
                        newRect.Y += yAdjust;
                                    
                        _tabRects[multiEntry.Index] = newRect;
                    }
                }
            }
        }

        protected virtual void RecalculateSinglelineTabs(int xWidth, int xStartPos, Rectangle tabPosition)
        {
            using (Graphics g = this.CreateGraphics())
            {
                // Remember which lines are then first and last displayed
                _topYPos = tabPosition.Y;
                _bottomYPos = _topYPos;
            
                // Set the minimum size for each tab page
                for(int i=0; i<_tabPages.Count; i++)
                {
                    // Is this page before those displayed?
                    if (i < _startPage)
                        _tabRects[i] = (object)_nullPosition;  // Yes, position off screen
                    else
                        _tabRects[i]= (object)tabPosition;	 // No, create minimum size
                }

                // Subtract the minimum tab sizes already allocated
                xWidth -= _tabPages.Count * (tabPosition.Width + 1);

                // Is there any more space left to allocate
                if (xWidth > 0)
                {
                    ArrayList listNew = new ArrayList();
                    ArrayList listOld = new ArrayList();

                    // Add all pages to those that need space allocating
                    for(int i=_startPage; i<_tabPages.Count; i++)
                        listNew.Add(_tabPages[i]);
            		
                    // Each tab can have an allowance
                    int xAllowance;
            		
                    do 
                    {
                        // The list generated in the last iteration becomes 
                        // the to be processed in this iteration
                        listOld = listNew;
            	
                        // List of pages that still need more space allocating
                        listNew = new ArrayList();

                        if (_shrinkPagesToFit)
                        {
                            // Each page is allowed a maximum allowance of space
                            // during this iteration. 
                            xAllowance = xWidth / _tabPages.Count;
                        }
                        else
                        {
                            // Allow each page as much space as it wants
                            xAllowance = 999;
                        }

                        // Assign space to each page that is requesting space
                        foreach(TabPage page in listOld)
                        {
                            int index = _tabPages.IndexOf(page);

                            Rectangle rectPos = (Rectangle)_tabRects[index];

                            // Find out how much extra space this page is requesting
                            int xSpace = GetTabPageSpace(g, page) - rectPos.Width;

                            // Does it want more space than its currently allowed to have?
                            if (xSpace > xAllowance)
                            {
                                // Restrict allowed space
                                xSpace = xAllowance;

                                // Add page to ensure it gets processed next time around
                                listNew.Add(page);
                            }

                            // Give space to tab
                            rectPos.Width += xSpace;

                            _tabRects[index] = (object)rectPos;

                            // Reduce extra left for remaining tabs
                            xWidth -= xSpace;
                        }
                    } while ((listNew.Count > 0) && (xAllowance > 0) && (xWidth > 0));
                }

                // Assign the final positions to each tab now we known their sizes
                for(int i=_startPage; i<_tabPages.Count; i++)
                {
                    Rectangle rectPos = (Rectangle)_tabRects[i];

                    // Define position of tab page
                    rectPos.X = xStartPos;

                    _tabRects[i] = (object)rectPos;

                    // Next button must be the width of this one across
                    xStartPos += rectPos.Width + 1;
                }
            }
        }

        protected virtual void RecalculateButtons()
        {
            int buttonTopGap = 0;
            
            if (_multiline)
            {
                // The height of a tab row is
                int tabButtonHeight = _position[_styleIndex, (int)PositionIndex.ImageGapTop] + 
                                      _imageGapTopExtra +
                                      _imageHeight + 
                                      _imageGapBottomExtra + 
                                      _position[_styleIndex, (int)PositionIndex.ImageGapBottom] +
                                      _position[_styleIndex, (int)PositionIndex.BorderBottom]; 
        
                // The height of the tabs area is...
                int tabsAreaHeight = _position[_styleIndex, (int)PositionIndex.BorderTop] + 
                                      tabButtonHeight + _position[_styleIndex, (int)PositionIndex.TabsBottomGap];
                
                // Find offset to place button halfway down the tabs area rectangle
                buttonTopGap = _position[_styleIndex, (int)PositionIndex.ButtonOffset]	+ 
                               (tabsAreaHeight - _buttonHeight) / 2;

                // Invert gap position when at bottom
                if (!_positionAtTop)
                    buttonTopGap = _tabsAreaRect.Height - buttonTopGap - _buttonHeight;
            }
            else
            {
                // Find offset to place button halfway down the tabs area rectangle
                buttonTopGap = _position[_styleIndex, (int)PositionIndex.ButtonOffset]	+ 
                                (_tabsAreaRect.Height - _buttonHeight) / 2;
            }
        
            // Position to place next button
            int xStart = _tabsAreaRect.Right - _buttonWidth - _buttonGap;

            // Close button should be shown?
            if (_showClose)
            {
                // Define the location
                _closeButton.Location = new Point(xStart, _tabsAreaRect.Top + buttonTopGap);

                if (xStart < 1)
                    _closeButton.Hide();
                else
                    _closeButton.Show();

                xStart -= _buttonWidth;
            }
            else
                _closeButton.Hide();

            // Arrows should be shown?
            if (_showArrows)
            {
                // Position the right arrow first as its more the right hand side
                _rightArrow.Location = new Point(xStart, _tabsAreaRect.Top + buttonTopGap);

                if (xStart < 1)
                    _rightArrow.Hide();
                else
                    _rightArrow.Show();
                    
                xStart -= _buttonWidth;

                _leftArrow.Location = new Point(xStart, _tabsAreaRect.Top + buttonTopGap);

                if (xStart < 1)
                    _leftArrow.Hide();
                else
                    _leftArrow.Show();
                    
                xStart -= _buttonWidth;

                // Define then enabled state of buttons
                _leftArrow.Enabled = _leftScroll;
                _rightArrow.Enabled = _rightScroll;
            }
            else
            {
                _leftArrow.Hide();
                _rightArrow.Hide();
            }

            if ((_appearance == VisualAppearance.MultiBox) || (_style == VisualStyle.Plain))
                _closeButton.BackColor = _leftArrow.BackColor = _rightArrow.BackColor = this.BackColor;
            else
                _closeButton.BackColor = _leftArrow.BackColor = _rightArrow.BackColor = _backIDE;
        }

        protected virtual int GetMaximumDrawPos()
        {
            int xEndPos = _tabsAreaRect.Right - _tabsAreaEndInset;

            // Showing the close button reduces available space
            if (_showClose)
                xEndPos -= _buttonWidth + _buttonGap;

            // If showing arrows then reduce space for both
            if (_showArrows)
                xEndPos -= _buttonWidth * 2;
				
            return xEndPos;
        }

        protected virtual int GetTabPageSpace(Graphics g, TabPage page)
        {
            // Find the fixed elements of required space
            int width = _position[_styleIndex, (int)PositionIndex.BorderLeft] + 
                        _position[_styleIndex, (int)PositionIndex.BorderRight];

            // Any icon or image provided?
            if ((page.Icon != null) || (((_imageList != null) || (page.ImageList != null)) && (page.ImageIndex != -1)))
            {
                width += _position[_styleIndex, (int)PositionIndex.ImageGapLeft] +
                         _imageWidth + 
                         _position[_styleIndex, (int)PositionIndex.ImageGapRight];
            }

            // Any text provided?
            if ((page.Title != null) && (page.Title.Length > 0))
            {
                if (!_selectedTextOnly || (_selectedTextOnly && (_pageSelected == _tabPages.IndexOf(page))))
                {
                    Font drawFont = base.Font;

                    if (_boldSelected && page.Selected)
                        drawFont = new Font(drawFont, FontStyle.Bold);

                    // Find width of the requested text
                    SizeF dimension = g.MeasureString(page.Title, drawFont);			

                    // Convert to integral
                    width += _position[_styleIndex, (int)PositionIndex.TextGapLeft] +
                            (int)dimension.Width + 1; 
                }						 
            }

            return width;
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            // Does the state need recalculating before paint can occur?
            if (_recalculate)
                Recalculate();

            using (SolidBrush pageAreaBrush = new SolidBrush(this.BackColor))
            {
                // Fill backgrounds of the page and tabs areas
                e.Graphics.FillRectangle(pageAreaBrush, _pageAreaRect);

                if ((_style == VisualStyle.Plain) || (_appearance == VisualAppearance.MultiBox))
                {
                    e.Graphics.FillRectangle(pageAreaBrush, _tabsAreaRect);
                }
                else
                {
                    using(SolidBrush tabsAreaBrush = new SolidBrush(_backIDE))
                        e.Graphics.FillRectangle(tabsAreaBrush, _tabsAreaRect);
                }
            }

            // MultiBox appearance does not have any borders
            if (_appearance != VisualAppearance.MultiBox)
            {
                bool hiddenPages = HideTabsCalculation();

                // Draw the borders
                switch(_style)
                {
                    case VisualStyle.Plain:
                        // Height for drawing the border is size of the page area extended 
                        // down to draw the bottom border inside the tabs area
                        int pageHeight = _pageAreaRect.Height + _plainBorderDouble;
	
                        int xDraw = _pageAreaRect.Top;

                        // Should the tabs area be hidden?
                        if (hiddenPages)
                        {
                            // Then need to readjust pageHeight
                            pageHeight -= _plainBorderDouble;
                        }
                        else
                        {
                            // If drawing at top then overdraw upwards and not down
                            if (_positionAtTop)
                                xDraw -= _plainBorderDouble;
                        }

                        if (_insetBorderPagesOnly)
                        {
                            if (!hiddenPages)
                            {
                                // Draw the outer border around the page area			
                                DrawHelper.DrawPlainRaisedBorderTopOrBottom(e.Graphics, new Rectangle(0, xDraw, this.Width, pageHeight),
                                                                            _backLightLight, base.BackColor, _backDark, _backDarkDark, _positionAtTop);
                            }
                        }
                        else
                        {
                            // Draw the outer border around the page area			
                            DrawHelper.DrawPlainRaisedBorder(e.Graphics, new Rectangle(_pageAreaRect.Left, xDraw, _pageAreaRect.Width, pageHeight),
                                                             _backLightLight, base.BackColor, _backDark, _backDarkDark);
                        }

                        // Do we have any tabs?
                        if ((_tabPages.Count > 0) && _insetPlain)
                        {
                            // Draw the inner border around the page area
                            Rectangle inner = new Rectangle(_pageAreaRect.Left + _plainBorder,
                                                            xDraw + _plainBorder,
                                                            _pageAreaRect.Width - _plainBorderDouble,
                                                            pageHeight - _plainBorderDouble);

                            if (_insetBorderPagesOnly)
                            {
                                if (!hiddenPages)
                                {
                                    DrawHelper.DrawPlainSunkenBorderTopOrBottom(e.Graphics, new Rectangle(0, inner.Top, this.Width, inner.Height),
                                                                                _backLightLight, base.BackColor, _backDark, _backDarkDark, _positionAtTop);
                                }                                                                       
                            }
                            else
                            {
                                DrawHelper.DrawPlainSunkenBorder(e.Graphics, new Rectangle(inner.Left, inner.Top, inner.Width, inner.Height),
                                                                 _backLightLight, base.BackColor, _backDark, _backDarkDark);
                            }

                        }						
                        break;

                    case VisualStyle.IDE:
                        // Draw the top and bottom borders to the tabs area
                        using (Pen darkdark = new Pen(_backDarkDark),
                                   dark = new Pen(_backDark),
                                   lightlight = new Pen(_backLightLight),
                                   backColor = new Pen(base.BackColor))
                        {
                            int borderGap = _position[_styleIndex, (int)PositionIndex.BorderTop];

                            if (_positionAtTop)
                            {
                                // Fill the border between the tabs and the embedded controls
                                using(SolidBrush backBrush = new SolidBrush(base.BackColor))
                                    e.Graphics.FillRectangle(backBrush, 0, _tabsAreaRect.Bottom - borderGap, _tabsAreaRect.Width, borderGap);

                                int indent = 0;

                                // Is a single pixel border required around whole area?                            
                                if (_idePixelBorder)
                                {
                                    using(Pen llFore = new Pen(ControlPaint.LightLight(this.ForeColor)))
                                        e.Graphics.DrawRectangle(dark, 0, 0, this.Width - 1, this.Height - 1);
                                        
                                    indent++;
                                }
                                else
                                {
                                    if (_idePixelArea)
                                    {
                                        // Draw top border
                                        e.Graphics.DrawLine(dark, 0, _tabsAreaRect.Top, _tabsAreaRect.Width, _tabsAreaRect.Top);
                                    }
                                }
                                
                                // Draw bottom border
                                if (!hiddenPages)
                                    e.Graphics.DrawLine(lightlight, indent, 
                                                                    _tabsAreaRect.Bottom - borderGap, 
                                                                    _tabsAreaRect.Width - (indent * 2), 
                                                                    _tabsAreaRect.Bottom - borderGap);
                            }
                            else
                            {
                                // Fill the border between the tabs and the embedded controls
                                using(SolidBrush backBrush = new SolidBrush(base.BackColor))
                                    e.Graphics.FillRectangle(backBrush, 0, _tabsAreaRect.Top, _tabsAreaRect.Width, borderGap);

                                int indent = 0;

                                // Is a single pixel border required around whole area?                            
                                if (_idePixelBorder)
                                {
                                    using(Pen llFore = new Pen(ControlPaint.LightLight(this.ForeColor)))
                                        e.Graphics.DrawRectangle(dark, 0, 0, this.Width - 1, this.Height - 1);
                                        
                                    indent++;
                                }
                                else
                                {
                                    if (_idePixelArea)
                                    {
                                        // Draw bottom border
                                        e.Graphics.DrawLine(backColor, 0, _tabsAreaRect.Bottom - 1, _tabsAreaRect.Width, _tabsAreaRect.Bottom - 1);
                                    }
                                }
                                
                                // Draw top border
                                if (!hiddenPages)
                                    e.Graphics.DrawLine(darkdark, indent, 
                                                                _tabsAreaRect.Top + 2, 
                                                                _tabsAreaRect.Width - (indent * 2), 
                                                                _tabsAreaRect.Top + 2);
                            }
                        }
                        break;
                }
            }

            // Clip the drawing to prevent drawing in unwanted areas
            ClipDrawingTabs(e.Graphics);

            // Paint each tab page
            foreach(TabPage page in _tabPages)
                DrawTab(page, e.Graphics, false);
        }

        protected virtual Rectangle ClippingRectangle()
        {
            // Calculate how much to reduce width by for clipping rectangle
            int xReduce = _tabsAreaRect.Width - GetMaximumDrawPos();

            // Create clipping rect
            return new Rectangle(_tabsAreaRect.Left, 
                                 _tabsAreaRect.Top,
                                 _tabsAreaRect.Width - xReduce,
                                 _tabsAreaRect.Height);
        }

        protected virtual void ClipDrawingTabs(Graphics g)
        {
            Rectangle clipRect = ClippingRectangle();

            // Restrict drawing to this clipping rectangle
            g.Clip = new Region(clipRect);
        }

        protected virtual void DrawTab(TabPage page, Graphics g, bool highlightText)
        {
            Rectangle rectTab = (Rectangle)_tabRects[_tabPages.IndexOf(page)];

            DrawTabBorder(ref rectTab, page, g);

            int xDraw = rectTab.Left + _position[_styleIndex, (int)PositionIndex.BorderLeft];
            int xMax = rectTab.Right - _position[_styleIndex, (int)PositionIndex.BorderRight];

            DrawTabImage(rectTab, page, g, xMax, ref xDraw);            
            DrawTabText(rectTab, page, g, highlightText, xMax, xDraw);
        }

        protected virtual void DrawTabImage(Rectangle rectTab, 
                                            TabPage page, 
                                            Graphics g, 
                                            int xMax, 
                                            ref int xDraw)
        {
            // Default to using the Icon from the page
            Icon drawIcon = page.Icon;
            Image drawImage = null;
			
            // If there is no valid Icon and the page is requested an image list index...
            if ((drawIcon == null) && (page.ImageIndex != -1))
            {
                try
                {
                    // Default to using an image from the TabPage
                    ImageList imageList = page.ImageList;

                    // If page does not have an ImageList...
                    if (imageList == null)
                        imageList = _imageList;   // ...then use the TabControl one

                    // Do we have an ImageList to select from?
                    if (imageList != null)
                    {
                        // Grab the requested image
                        drawImage = imageList.Images[page.ImageIndex];
                    }
                }
                catch(Exception)
                {
                    // User supplied ImageList/ImageIndex are invalid, use an error image instead
                    drawImage = _internalImages.Images[(int)ImageStrip.Error];
                }
            }

            // Draw any image required
            if ((drawImage != null) || (drawIcon != null))
            {
                // Enough room to draw any of the image?
                if ((xDraw + _position[_styleIndex, (int)PositionIndex.ImageGapLeft]) <= xMax)
                {
                    // Move past the left image gap
                    xDraw += _position[_styleIndex, (int)PositionIndex.ImageGapLeft];

                    // Find down position for drawing the image
                    int yDraw = rectTab.Top + 
                                _position[_styleIndex, (int)PositionIndex.ImageGapTop] + 
                                _imageGapTopExtra;

                    // If there is enough room for all of the image?
                    if ((xDraw + _imageWidth) <= xMax)
                    {
                        if (drawImage != null)
                            g.DrawImage(drawImage, new Rectangle(xDraw, yDraw, _imageWidth, _imageHeight));
                        else
                            g.DrawIcon(drawIcon, new Rectangle(xDraw, yDraw, _imageWidth, _imageHeight));

                        // Move past the image and the image gap to the right
                        xDraw += _imageWidth + _position[_styleIndex, (int)PositionIndex.ImageGapRight];
                    }
                    else
                    {
                        // Calculate how much room there is
                        int xSpace = xMax - xDraw;

                        // Any room at all?
                        if (xSpace > 0)
                        {
                            if (drawImage != null)
                            {
                                // Draw only part of the image
                                g.DrawImage(drawImage, 
                                            new Point[]{new Point(xDraw, yDraw), 
                                                        new Point(xDraw + xSpace, yDraw), 
                                                        new Point(xDraw, yDraw + _imageHeight)},
                                            new Rectangle(0, 0, xSpace, 
                                            _imageHeight),
                                            GraphicsUnit.Pixel);
                            }
                            
                            // All space has been used up, nothing left for text
                            xDraw = xMax;
                        }
                    }
                }
            }
        }

        protected virtual void DrawTabText(Rectangle rectTab, 
                                           TabPage page, 
                                           Graphics g, 
                                           bool highlightText,
                                           int xMax, 
                                           int xDraw)
        {
            if (!_selectedTextOnly || (_selectedTextOnly && page.Selected))
            {
                // Any space for drawing text?
                if (xDraw < xMax)
                {
                    Color drawColor;
                    SolidBrush drawBrush;
                    Font drawFont = base.Font;

                    // Decide which base color to use
                    if (highlightText)
                        drawColor = _hotTextColor;
                    else
				    {
					    // Do we modify base color depending on selection?
					    if (_dimUnselected && !page.Selected)
					    {
						    // Reduce the intensity of the color
						    drawColor = _textInactiveColor;
					    }
					    else
						    drawColor = _textColor;
				    }


                    // Should selected items be drawn in bold?
                    if (_boldSelected && page.Selected)
                        drawFont = new Font(drawFont, FontStyle.Bold);

                    // Now the color is determined, create solid brush
                    drawBrush = new SolidBrush(drawColor);

                    // Ensure only a single line is draw from then left hand side of the
                    // rectangle and if to large for line to shows ellipsis for us
                    StringFormat drawFormat = new StringFormat();
                    drawFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
                    drawFormat.Trimming = StringTrimming.EllipsisCharacter;
                    drawFormat.Alignment = StringAlignment.Center;
                    drawFormat.HotkeyPrefix = HotkeyPrefix.Show;

                    // Find the vertical drawing limits for text
                    int yStart = rectTab.Top + _position[_styleIndex, (int)PositionIndex.ImageGapTop];

                    int yEnd = rectTab.Bottom - 
                            _position[_styleIndex, (int)PositionIndex.ImageGapBottom] - 
                            _position[_styleIndex, (int)PositionIndex.BorderBottom];

                    // Use text offset to adjust position of text
                    yStart += _position[_styleIndex, (int)PositionIndex.TextOffset];

                    // Across the text left gap
                    xDraw += _position[_styleIndex, (int)PositionIndex.TextGapLeft];

                    // Need at least 1 pixel width before trying to draw
                    if (xDraw < xMax)
                    {
                        // Find drawing rectangle
                        Rectangle drawRect = new Rectangle(xDraw, yStart, xMax - xDraw, yEnd - yStart);

                        // Finally....draw the string!
                        g.DrawString(page.Title, drawFont, drawBrush, drawRect, drawFormat);
                    }
                        	
                    // Cleanup resources!
                    drawBrush.Dispose();
                }
            }
        }

        protected virtual void DrawTabBorder(ref Rectangle rectTab, TabPage page, Graphics g)
        {
            if (_appearance == VisualAppearance.MultiBox)
            {
                // Adjust the drawing upwards two pixels to 'look pretty'
                rectTab.Y -= _multiBoxAdjust;

                // Draw the same regardless of style
                DrawMultiBoxBorder(page, g, rectTab);
            }
            else
            {
                // Drawing the border is style specific
                switch(_style)
                {
                    case VisualStyle.Plain:
                        DrawPlainTabBorder(page, g, rectTab);
                        break;				
                    case VisualStyle.IDE:
                        DrawIDETabBorder(page, g, rectTab);
                        break;				
                }
            }
        }
		
        protected virtual void DrawMultiBoxBorder(TabPage page, Graphics g, Rectangle rectPage)
        {
            if (page.Selected)
            {
                using (SolidBrush lightlight = new SolidBrush(_backLightLight))
                    g.FillRectangle(lightlight, rectPage);

                using (Pen darkdark = new Pen(_backDarkDark))
                    g.DrawRectangle(darkdark, rectPage);
            }
            else
            {
                using (SolidBrush backBrush = new SolidBrush(this.BackColor))
                    g.FillRectangle(backBrush, rectPage.X + 1, rectPage.Y, rectPage.Width - 1, rectPage.Height);
            
                // Find the index into TabPage collection for this page
                int index = _tabPages.IndexOf(page);

                // Decide if the separator should be drawn
                bool drawSeparator = (index == _tabPages.Count - 1) ||
                    (index < (_tabPages.Count - 1)) && 
                    (_tabPages[index+1].Selected != true);
                                         
                // MultiLine mode is slighty more complex
                if (_multiline && !drawSeparator)
                {
                    // By default always draw separator
                    drawSeparator = true;
                    
                    // If we are not the last item
                    if (index < (_tabPages.Count - 1))
                    {
                        // If the next item is selected
                        if (_tabPages[index+1].Selected == true)
                        {
                            Rectangle thisRect = (Rectangle)_tabRects[index];
                            Rectangle nextRect = (Rectangle)_tabRects[index+1];

                            // If we are on the same drawing line then do not draw separator
                            if (thisRect.Y == nextRect.Y)
                                drawSeparator = false;
                        }
                    }
                }

                // Draw tab separator unless the next page after us is selected
                if (drawSeparator)
                {
                    using(Pen lightlight = new Pen(_backLightLight),
                              dark = new Pen(_backDark))
                    {
                        g.DrawLine(dark, rectPage.Right, rectPage.Top + 2, rectPage.Right, 
                                   rectPage.Bottom - _position[_styleIndex, (int)PositionIndex.TabsBottomGap] - 1);
                        g.DrawLine(lightlight, rectPage.Right+1, rectPage.Top + 2, rectPage.Right+1, 
                                   rectPage.Bottom - _position[_styleIndex, (int)PositionIndex.TabsBottomGap] - 1);
                    }
                }
            }
        }

        protected virtual void DrawPlainTabBorder(TabPage page, Graphics g, Rectangle rectPage)
        {
            using(Pen light = new Pen(_backLightLight),
                      dark = new Pen(_backDark),
                      darkdark = new Pen(_backDarkDark))
            {
                int yLeftOffset = 0;
                int yRightOffset = 0;

                using (SolidBrush backBrush = new SolidBrush(base.BackColor))
                {
                    if (page.Selected)
                    {
                        // Calculate the rectangle that covers half the top border area
                        int yBorder;
						
                        if (_positionAtTop)
                            yBorder = rectPage.Top + (_position[_styleIndex, (int)PositionIndex.BorderTop] / 2);
                        else
                            yBorder = rectPage.Top - (_position[_styleIndex, (int)PositionIndex.BorderTop] / 2);

                        // Construct rectangle that covers the outer part of the border
                        Rectangle rectBorder = new Rectangle(rectPage.Left, yBorder, rectPage.Width - 1, rectPage.Height);

                        // Blank out area 
                        g.FillRectangle(backBrush, rectBorder);

                        // Make the left and right border lines extend higher up
                        yLeftOffset = -2;
                        yRightOffset = -1;
                    }
                }

                if (_positionAtTop)
                {
                    // Draw the left border
                    g.DrawLine(light, rectPage.Left, rectPage.Bottom, rectPage.Left, rectPage.Top + 2); 
                    g.DrawLine(light, rectPage.Left + 1 , rectPage.Top + 1, rectPage.Left + 1, rectPage.Top + 2); 

                    // Draw the top border
                    g.DrawLine(light, rectPage.Left + 2, rectPage.Top + 1, rectPage.Right - 2, rectPage.Top + 1); 

                    // Draw the right border
                    g.DrawLine(darkdark, rectPage.Right, rectPage.Bottom - yRightOffset, rectPage.Right, rectPage.Top + 2); 
                    g.DrawLine(dark, rectPage.Right - 1, rectPage.Bottom - yRightOffset, rectPage.Right - 1, rectPage.Top + 2); 
                    g.DrawLine(dark, rectPage.Right - 2, rectPage.Top + 1, rectPage.Right - 2, rectPage.Top + 2); 
                    g.DrawLine(darkdark, rectPage.Right - 2, rectPage.Top, rectPage.Right, rectPage.Top + 2);
                }
                else
                {
                    // Draw the left border
                    g.DrawLine(light, rectPage.Left, rectPage.Top + yLeftOffset, rectPage.Left, rectPage.Bottom - 2); 
                    g.DrawLine(dark, rectPage.Left + 1 , rectPage.Bottom - 1, rectPage.Left + 1, rectPage.Bottom - 2); 

                    // Draw the bottom border
                    g.DrawLine(dark, rectPage.Left + 2, rectPage.Bottom - 1, rectPage.Right - 2, rectPage.Bottom - 1); 
                    g.DrawLine(darkdark, rectPage.Left + 2, rectPage.Bottom, rectPage.Right - 2, rectPage.Bottom); 

                    // Draw the right border
                    g.DrawLine(darkdark, rectPage.Right, rectPage.Top, rectPage.Right, rectPage.Bottom - 2); 
                    g.DrawLine(dark, rectPage.Right - 1, rectPage.Top + yRightOffset, rectPage.Right - 1, rectPage.Bottom - 2); 
                    g.DrawLine(dark, rectPage.Right - 2, rectPage.Bottom - 1, rectPage.Right - 2, rectPage.Bottom - 2); 
                    g.DrawLine(darkdark, rectPage.Right - 2, rectPage.Bottom, rectPage.Right, rectPage.Bottom - 2);
                }
            }
        }

        protected virtual void DrawIDETabBorder(TabPage page, Graphics g, Rectangle rectPage)
        {
            using(Pen lightlight = new Pen(_backLightLight),
                      backColor = new Pen(base.BackColor),
                      dark = new Pen(_backDark),
                      darkdark = new Pen(_backDarkDark))
            {
                if (page.Selected)
                {
                    // Draw background in selected color
                    using (SolidBrush pageAreaBrush = new SolidBrush(this.BackColor))
                        g.FillRectangle(pageAreaBrush, rectPage);

                    if (_positionAtTop)
                    {
                        // Overdraw the bottom border
                        g.DrawLine(backColor, rectPage.Left, rectPage.Bottom, rectPage.Right - 1, rectPage.Bottom);

                        // Draw the right border
                        g.DrawLine(darkdark, rectPage.Right, rectPage.Top, rectPage.Right, rectPage.Bottom); 
                    }
                    else
                    {
                        // Draw the left border
                        g.DrawLine(lightlight, rectPage.Left, rectPage.Top - 1 , rectPage.Left, rectPage.Bottom); 

                        // Draw the bottom border
                        g.DrawLine(darkdark, rectPage.Left + 1, rectPage.Bottom, rectPage.Right, rectPage.Bottom); 

                        // Draw the right border
                        g.DrawLine(darkdark, rectPage.Right, rectPage.Top - 1 , rectPage.Right, rectPage.Bottom); 

                        // Overdraw the top border
                        g.DrawLine(backColor, rectPage.Left + 1, rectPage.Top - 1, rectPage.Right - 1, rectPage.Top - 1);
                    }
                }
                else
                {
                    // Draw background in unselected color
                    using(SolidBrush tabsAreaBrush = new SolidBrush(_backIDE))
                        g.FillRectangle(tabsAreaBrush, rectPage);

                    // Find the index into TabPage collection for this page
                    int index = _tabPages.IndexOf(page);

                    // Decide if the separator should be drawn
                    bool drawSeparator = (index == _tabPages.Count - 1) ||
                                         (index < (_tabPages.Count - 1)) && 
                                         (_tabPages[index+1].Selected != true);
                                         
                    // MultiLine mode is slighty more complex
                    if (_multiline && !drawSeparator)
                    {
                        // By default always draw separator
                        drawSeparator = true;
                    
                        // If we are not the last item
                        if (index < (_tabPages.Count - 1))
                        {
                            // If the next item is selected
                            if (_tabPages[index+1].Selected == true)
                            {
                                Rectangle thisRect = (Rectangle)_tabRects[index];
                                Rectangle nextRect = (Rectangle)_tabRects[index+1];

                                // If we are on the same drawing line then do not draw separator
                                if (thisRect.Y == nextRect.Y)
                                    drawSeparator = false;
                            }
                        }
                    }

                    // Draw tab separator unless the next page after us is selected
                    if (drawSeparator)
                    {
                        // Reduce the intensity of the color
                        using(Pen linePen = new Pen(_textInactiveColor))
                            g.DrawLine(linePen, rectPage.Right, rectPage.Top + 2, rectPage.Right, 
                                rectPage.Bottom - _position[_styleIndex, (int)PositionIndex.TabsBottomGap] - 1);
                    }
                }
            }
        }

        protected virtual void OnClearingPages()
        {
            // Is a page currently selected?
            if (_pageSelected != -1)
            {
                // Deselect the page
                DeselectPage(_tabPages[_pageSelected]);

                // Remember that nothing is selected
                _pageSelected = -1;
                _startPage = -1;
            }

            // Remove all the user controls 
            foreach(TabPage page in _tabPages)
				RemoveTabPage(page);

            // Remove all rectangles associated with TabPage's
            _tabRects.Clear();
        }

        protected virtual void OnClearedPages()
        {
            // Must recalculate after the pages have been removed and
            // not before as that would calculate based on pages still
            // being present in the list
            Recalculate();
            
            // Raise selection changing event
            OnSelectionChanging(EventArgs.Empty);

            // Must notify a change in selection
            OnSelectionChanged(EventArgs.Empty);
            
            Invalidate();
        }

        protected virtual void OnInsertingPage(int index, object value)
        {
            // If a page currently selected?
            if (_pageSelected != -1)
            {
                // Is the selected page going to be after this new one in the list
                if (_pageSelected >= index)
                    _pageSelected++;  // then need to update selection index to reflect this
            }
        }

        protected virtual void OnInsertedPage(int index, object value)
        {
            bool selectPage = false;

            TabPage page = value as TabPage;

            // Hookup to receive TabPage property changes
            page.PropertyChanged += new TabPage.PropChangeHandler(OnPagePropertyChanged);

            // Add the appropriate Control/Form/TabPage to the control
            AddTabPage(page);
			
            // Do we want to select this page?
            if ((_pageSelected == -1) || (page.Selected))
            {
                // Raise selection changing event
                OnSelectionChanging(EventArgs.Empty);

                // Any page currently selected
                if (_pageSelected != -1)
                    DeselectPage(_tabPages[_pageSelected]);

                // This becomes the newly selected page
                _pageSelected = _tabPages.IndexOf(page);

                // If no page is currently defined as the start page
                if (_startPage == -1)
                    _startPage = 0;	 // then must be added then first page

                // Request the page be selected
                selectPage = true;
            }

            // Add new rectangle to match new number of pages, this must be done before
            // the 'SelectPage' or 'OnSelectionChanged' to ensure the number of _tabRects 
            // entries matches the number of _tabPages entries.
            _tabRects.Add((object)new Rectangle());

            // Cause the new page to be the selected one
            if (selectPage)
            {
                // Must recalculate to ensure the new _tabRects entry above it correctly
                // filled in before the new page is selected, as a change in page selection
                // may cause the _tabRects values ot be interrogated.
                Recalculate();

                SelectPage(page);

                // Raise selection change event
                OnSelectionChanged(EventArgs.Empty);
            }

            Recalculate();
            Invalidate();
        }

        protected virtual void OnRemovingPage(int index, object value)
        {
            TabPage page = value as TabPage;

            page.PropertyChanged -= new TabPage.PropChangeHandler(OnPagePropertyChanged);

            // Remove the appropriate Control/Form/TabPage to the control
            RemoveTabPage(page);

            // Notice a change in selected page
            _changed = false;

            // Is this the currently selected page
            if (_pageSelected == index)
            {
                // Raise selection changing event
                OnSelectionChanging(EventArgs.Empty);

                _changed = true;
                DeselectPage(page);
            }
        }

        protected virtual void OnRemovedPage(int index, object value)
        {
            // Is first displayed page then one being removed?
            if (_startPage >= index)
            {
                // Decrement to use start displaying previous page
                _startPage--;

                // Have we tried to select off the left hand side?
                if (_startPage == -1)
                {
                    // Are there still some pages left?
                    if (_tabPages.Count > 0)
                        _startPage = 0;
                }
            }

            // Is the selected page equal to or after this new one in the list
            if (_pageSelected >= index)
            {
                // Decrement index to reflect this change
                _pageSelected--;

                // Have we tried to select off the left hand side?
                if (_pageSelected == -1)
                {
                    // Are there still some pages left?
                    if (_tabPages.Count > 0)
                        _pageSelected = 0;
                }

                // Is the new selection valid?
                if (_pageSelected != -1)
                    SelectPage(_tabPages[_pageSelected]);  // Select it
            }

            // Change in selection causes event generation
            if (_changed)
            {
                // Reset changed flag
                _changed = false;

                // Raise selection change event
                OnSelectionChanged(EventArgs.Empty);
            }

            // Remove a rectangle to match number of pages
            _tabRects.RemoveAt(0);

            Recalculate();
            Invalidate();
        }
			
        protected virtual void AddTabPage(TabPage page)
        {
            // Has not been shown for the first time yet
            page.Shown = false;
        
            // Add user supplied control 
            if (page.Control != null)
            {
                Form controlIsForm = page.Control as Form;

                page.Control.Hide();

                // Adding a Form takes extra effort
                if (controlIsForm == null)
                {
					// Monitor focus changes on the Control
                    page.Control.GotFocus += new EventHandler(OnPageEnter);
                    page.Control.LostFocus += new EventHandler(OnPageLeave);
                    page.Control.MouseEnter += new EventHandler(OnPageMouseEnter);
                    page.Control.MouseLeave += new EventHandler(OnPageMouseLeave);

					Controls.Add(page.Control);
				}
                else
                {
                    // Monitor activation changes on the TabPage
                    controlIsForm.Activated += new EventHandler(OnPageEnter);
                    controlIsForm.Deactivate += new EventHandler(OnPageLeave);
                    controlIsForm.MouseEnter += new EventHandler(OnPageMouseEnter);
                    controlIsForm.MouseLeave += new EventHandler(OnPageMouseLeave);
	
                    // Have to ensure the Form is not a top level form
                    controlIsForm.TopLevel = false;

                    // We are the new parent of this form
                    controlIsForm.Parent = this;

					// To prevent user resizing the form manually and prevent
                    // the caption bar appearing, we use the 'None' border style.
                    controlIsForm.FormBorderStyle = FormBorderStyle.None;
                }

				// Need to monitor when the Form/Panel is clicked
				if ((page.Control is Form) || (page.Control is Panel))
					page.Control.MouseDown += new MouseEventHandler(OnPageMouseDown);

				RecursiveMonitor(page.Control, true);
            }
            else
            {
                page.Hide();

                // Monitor focus changes on the TabPage
                page.GotFocus += new EventHandler(OnPageEnter);
                page.LostFocus += new EventHandler(OnPageLeave);
                page.MouseEnter += new EventHandler(OnPageMouseEnter);
                page.MouseLeave += new EventHandler(OnPageMouseLeave);

                // Must fill the entire hosting panel it is on
                page.Dock = DockStyle.None;

                // Need to monitor when the Panel is clicked
				page.MouseDown += new MouseEventHandler(OnPageMouseDown);

				RecursiveMonitor(page, true);

                // Add the TabPage itself instead
				Controls.Add(page);
            }
        }

		protected virtual void RemoveTabPage(TabPage page)
		{
			// Remove user supplied control 
			if (page.Control != null)
				RemoveTabPageControl(page.Control);
			else
				RemoveTabPagePanel(page);
		}

		protected virtual void RemoveTabPageControl(Control c)
        {
			RecursiveMonitor(c, false);

            Form controlIsForm = c as Form;

			// Need to unhook hooked up event
			if ((c is Form) || (c is Panel))
				c.MouseDown -= new MouseEventHandler(OnPageMouseDown);

            if (controlIsForm == null)
            {
                // Unhook event monitoring
                c.GotFocus -= new EventHandler(OnPageEnter);
                c.LostFocus -= new EventHandler(OnPageLeave);
                c.MouseEnter -= new EventHandler(OnPageMouseEnter);
                c.MouseLeave -= new EventHandler(OnPageMouseLeave);
	
				// Use helper method to circumvent form Close bug
				ControlHelper.Remove(this.Controls, c);
			}
            else
            {
                // Unhook activation monitoring
                controlIsForm.Activated -= new EventHandler(OnPageEnter);
                controlIsForm.Deactivate -= new EventHandler(OnPageLeave);
                controlIsForm.MouseEnter -= new EventHandler(OnPageMouseEnter);
                controlIsForm.MouseLeave -= new EventHandler(OnPageMouseLeave);

				// Remove Form but prevent the Form close bug
				ControlHelper.RemoveForm(this, controlIsForm);
            }
        }

		protected virtual void RemoveTabPagePanel(TabPage page)
		{
			RecursiveMonitor(page, false);

			// Need to unhook hooked up event
			page.MouseDown -= new MouseEventHandler(OnPageMouseDown);

			// Unhook event monitoring
			page.GotFocus -= new EventHandler(OnPageEnter);
			page.LostFocus -= new EventHandler(OnPageLeave);
			page.MouseEnter -= new EventHandler(OnPageMouseEnter);
			page.MouseLeave -= new EventHandler(OnPageMouseLeave);

			// Use helper method to circumvent form Close bug
			ControlHelper.Remove(this.Controls, page);
		}

		protected virtual void OnPageMouseDown(object sender, MouseEventArgs e)
		{
			Control c = sender as Control;

			// If the mouse has been clicked and it does not have 
			// focus then it should receive the focus immediately.
			if (!c.ContainsFocus)
				c.Focus();
		}

		protected virtual void RecursiveMonitor(Control top, bool monitor)
		{
			foreach(Control c in top.Controls)
			{
				if (monitor)
				{
					// Monitor focus changes on the Control
					c.GotFocus += new EventHandler(OnPageEnter);
					c.LostFocus += new EventHandler(OnPageLeave);
                    c.MouseEnter += new EventHandler(OnPageMouseEnter);
                    c.MouseLeave += new EventHandler(OnPageMouseLeave);
                }
				else
				{
					// Unmonitor focus changes on the Control
					c.GotFocus -= new EventHandler(OnPageEnter);
					c.LostFocus -= new EventHandler(OnPageLeave);
                    c.MouseEnter -= new EventHandler(OnPageMouseEnter);
                    c.MouseLeave -= new EventHandler(OnPageMouseLeave);
                }
				
				RecursiveMonitor(c, monitor);
			}
		}

        protected virtual void OnPageEnter(object sender, EventArgs e)
        {
            OnPageGotFocus(e);
        }

        protected virtual void OnPageLeave(object sender, EventArgs e)
        {
            OnPageLostFocus(e);
        }
    
        protected virtual void OnPageMouseEnter(object sender, EventArgs e)
        {
			try
			{
				_mouseOver = true;
				_overTimer.Stop();
	            
				if (_hideTabsMode == HideTabsModes.HideWithoutMouse)
				{
					Recalculate();
					Invalidate();
				}
			}
			catch(Exception)
			{
			}
		}
	
        protected virtual void OnPageMouseLeave(object sender, EventArgs e)
        {
			try
			{
				_overTimer.Start();
			}
			catch(Exception)
			{
			}
		}

        protected virtual void OnMouseTick(object sender, EventArgs e)
        {
			try
			{
				_mouseOver = false;
				_overTimer.Stop();
	            
				if (_hideTabsMode == HideTabsModes.HideWithoutMouse)
				{
					Recalculate();
					Invalidate();
				}
			}
			catch(Exception)
			{
			}
        }

		protected virtual void OnDragOverTick(object sender, EventArgs e)
		{
			try
			{
				_dragTimer.Stop();

				// Allow to select pages on drag over?
				if (_dragOverSelect)
				{
					// Find any tab header the mouse is over
					TabPage mouseTab = TabPageFromPoint(_mouseDragOver);

					// Was a tab found?
					if (mouseTab != null)
					{
						// Switch to this tab
						if (!mouseTab.Selected)
							mouseTab.Selected = true;
					}
				}
			}
			catch(Exception)
			{
			}
		}
	
        protected virtual void OnPagePropertyChanged(TabPage page, TabPage.Property prop, object oldValue)
        {
            switch(prop)
            {
                case TabPage.Property.Control:
                    Control pageControl = oldValue as Control;

                    // Remove the page or the page control as appropriate
                    if (pageControl != null)
						RemoveTabPageControl(pageControl);
                    else
						RemoveTabPagePanel(page);

                    // Add the appropriate Control/Form/TabPage to the control
                    AddTabPage(page);

                    // Is a page currently selected?
                    if (_pageSelected != -1)
                    {
                        // Is the change in Control for this page?
                        if (page == _tabPages[_pageSelected])
                            SelectPage(page);   // make Control visible
                    }

                    Recalculate();
                    Invalidate();
                    break;
                case TabPage.Property.Title:
                case TabPage.Property.ImageIndex:
                case TabPage.Property.ImageList:
                case TabPage.Property.Icon:
                    _recalculate = true;
                    Invalidate();
                    break;
                case TabPage.Property.Selected:
                    // Becoming selected?
                    if (page.Selected)
                    {
                        // Move selection to the new page and update page properties
                        MovePageSelection(page);
                        MakePageVisible(page);
                    }
                    break;
            }
        }

		protected virtual Control FindFocus(Control root)
		{
			// Does the root control has focus?
			if (root.Focused)
				return root;

			// Check for focus at each child control
			foreach(Control c in root.Controls)
			{
				Control child = FindFocus(c);

				if (child != null)
					return child;
			}

			return null;
		}

        protected virtual void DeselectPage(TabPage page)
        {
            page.Selected = false;

            // Hide any associated control
            if (page.Control != null)
			{
				// Should we remember which control had focus when leaving?
				if (_recordFocus)
				{
					// Record current focus location on Control
					if (page.Control.ContainsFocus)
						page.StartFocus = FindFocus(page.Control);
					else
						page.StartFocus = null;
				}

                page.Control.Hide();
			}
            else
			{
				// Should we remember which control had focus when leaving?
				if (_recordFocus)
				{
					// Record current focus location on Control
					if (page.ContainsFocus)
						page.StartFocus = FindFocus(page);
					else
						page.StartFocus = null;
				}

                page.Hide();
			}
        }

        protected virtual void SelectPage(TabPage page)
        {
            page.Selected = true;

            // Bring the control for this page to the front
            if (page.Control != null)
                HandleShowingTabPage(page, page.Control);
            else
                HandleShowingTabPage(page, page);
        }

        protected virtual void HandleShowingTabPage(TabPage page, Control c)
        {
            // First time this page has been displayed?
            if (!page.Shown)
            {
                // Special testing needed for Forms
                Form f = c as Form;
                
                // AutoScaling can cause the Control/Form to be
                if ((f != null) && (f.AutoScale))
                {
                    // Workaround the problem where a form has a defined 'AutoScaleBaseSize' value. The 
                    // first time it is shown it calculates the size of each contained control and scales 
                    // as needed. But if the contained control is Dock=DockStyle.Fill it scales up/down so 
                    // its not actually filling the space! Get around by hiding and showing to force correct 
                    // calculation.
                    c.Show();
                    c.Hide();
                }
                    
                // Only need extra logic first time around
                page.Shown = true;
            }

            // Finally, show it!
            c.Show();

            // Restore focus to last know control to have it
            if (page.StartFocus != null)
                page.StartFocus.Focus();
            else
                c.Focus();
        }
		
        protected virtual void MovePageSelection(TabPage page)
        {
            int pageIndex = _tabPages.IndexOf(page);

            if (pageIndex != _pageSelected)
            {
                // Raise selection changing event
                OnSelectionChanging(EventArgs.Empty);

                // Any page currently selected?
                if (_pageSelected != -1)
                    DeselectPage(_tabPages[_pageSelected]);

                _pageSelected = pageIndex;

				if (_pageSelected != -1)
                    SelectPage(_tabPages[_pageSelected]);

                // Change in selection causes tab pages sizes to change
                if (_boldSelected || _selectedTextOnly || !_shrinkPagesToFit || _multiline)
                {
                    Recalculate();
                    Invalidate();
                }

				// Raise selection change event
                OnSelectionChanged(EventArgs.Empty);

                Invalidate();
			}
        }

        // Used by the TabControlDesigner
        internal bool WantDoubleClick(IntPtr hWnd, Point mousePos)
        {
            return ControlWantDoubleClick(hWnd, mousePos, _leftArrow) ||
                   ControlWantDoubleClick(hWnd, mousePos, _rightArrow) ||
                   ControlWantDoubleClick(hWnd, mousePos, _closeButton);
        }

        // Used by the TabControlDesigner
        internal void ExternalMouseTest(IntPtr hWnd, Point mousePos)
        {
            if (!(ControlMouseTest(hWnd, mousePos, _leftArrow) ||
                  ControlMouseTest(hWnd, mousePos, _rightArrow) ||
                  ControlMouseTest(hWnd, mousePos, _closeButton)))
                InternalMouseDown(mousePos);
        }

        protected virtual bool ControlWantDoubleClick(IntPtr hWnd, Point mousePos, Control check)
        {
            // Cannot have double click if control not visible
            if (check.Visible)
            {
                // Is double click for this control?
                if (check.Enabled && (hWnd == check.Handle))
                {
                    if (check == _leftArrow)
                        OnLeftArrow(null, EventArgs.Empty);
	
                    if (check == _rightArrow)
                        OnRightArrow(null, EventArgs.Empty);
					
                    return true;
                }
                else
                {
                    // Create rectangle for control position
                    Rectangle checkRect = new Rectangle(check.Location.X,
                                                        check.Location.Y,
                                                        check.Width,
                                                        check.Height);

                    // Was double click over a disabled button?
                    if (checkRect.Contains(mousePos))
                        return true;
                }
            }

            return false;
        }

        protected virtual bool ControlMouseTest(IntPtr hWnd, Point mousePos, Control check)
        {
            // Is the mouse down for the left arrow window and is it valid to click?
            if ((hWnd == check.Handle) && check.Visible && check.Enabled)
            {
                // Check if the mouse click is over the left arrow
                if (check.ClientRectangle.Contains(mousePos))
                {
                    if (check == _leftArrow)
                        OnLeftArrow(null, EventArgs.Empty);
	
                    if (check == _rightArrow)
                        OnRightArrow(null, EventArgs.Empty);

                    return true;
                }
            }

            return false;
        }

		protected override void OnDoubleClick(EventArgs e)
		{
			Point pos = TabControl.MousePosition;

			int count = _tabRects.Count;

			for(int index=0; index<count; index++)
			{
				// Get tab drawing rectangle
				Rectangle local = (Rectangle)_tabRects[index];
				
				// If drawing on the control
				if (local != _nullPosition)
				{
					// Convert from Control to screen coordinates
					Rectangle screen = this.RectangleToScreen(local);

					if (screen.Contains(pos))
					{
						// Generate appropriate event
						OnDoubleClickTab(_tabPages[index]);
						break;
					}
				}
			}

			base.OnDoubleClick(e);
		}

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (_leftMouseDownDrag)
            {
                // Generate event for interested parties
                if (e.Button == MouseButtons.Left)
                    OnPageDragEnd(e);
                else
                    OnPageDragQuit(e);
                
                _leftMouseDownDrag = false;
                _ignoreDownDrag = true;
            }

            if (e.Button == MouseButtons.Left)
            {
                // Exit any page dragging attempt
                _leftMouseDown = false;
            }
            else
            {
                // Is it the button that causes context menu to show?
                if (e.Button == MouseButtons.Right)
                {
                    Point mousePos = new Point(e.X, e.Y);
                
                    // Is the mouse in the tab area
                    if (_tabsAreaRect.Contains(mousePos))
                    {
                        CancelEventArgs ce = new CancelEventArgs();
                        
                        // Generate event giving handlers cancel to update/cancel menu
                        OnPopupMenuDisplay(ce);
                        
                        // Still want the popup?
                        if (!ce.Cancel)
                        {
                            // Is there any attached menu to show
                            if (_contextMenu != null)
                                _contextMenu.TrackPopup(this.PointToScreen(new Point(e.X, e.Y)));
                        }
                    }
                }
            }

            base.OnMouseUp(e);
        }

		protected override void OnMouseDown(MouseEventArgs e)
        {
			// Any mouse press when dragging should quit it
			if (_leftMouseDownDrag)
			{
				OnPageDragQuit(e);
                
				_leftMouseDownDrag = false;
				_ignoreDownDrag = true;
			}
			else
			{
				// Only select a button or page when using left mouse button
				InternalMouseDown(new Point(e.X, e.Y));
			}

			base.OnMouseDown(e);
        }
        
        protected virtual void InternalMouseDown(Point mousePos)
        {
			int process = 0;

            // Clicked on a tab page?
            for(int i=0; i<_tabPages.Count; i++)
            {
                Rectangle rect = (Rectangle)_tabRects[i];

				if (rect.Contains(mousePos))
				{
					// Are the scroll buttons being shown?
					if (_leftArrow.Visible)
					{
						// Ignore mouse down over then buttons area
						if (mousePos.X >= _leftArrow.Left)
							return;
					}
					else
					{
						// No, is the close button visible?
						if (_closeButton.Visible)
						{
							// Ignore mouse down over then close button area
							if (mousePos.X >= _closeButton.Left)
								return;
						}
					}

					process = i;
					break;
				}
				else
				{
					// Remember the last tab to the left of mouse
					if (rect.Right < mousePos.X)
						process = i;
				}
            }

			// Is there any page to select?
			if (process < _tabPages.Count)
			{
				// Remember where the left mouse was initially pressed
				if (Control.MouseButtons == MouseButtons.Left)
				{
					_leftMouseDown = true;
					_ignoreDownDrag = false;
					_leftMouseDownDrag = false;
					_leftMouseDownPos = mousePos;
				}
	                    
				MovePageSelection(_tabPages[process]);
				MakePageVisible(_tabPages[process]);

				// Set focus into the selected page contents
				if (_tabPages[process].Control != null)
                    _tabPages[process].Control.Focus();
                else
				    _tabPages[process].Focus();
			}
		}		

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_leftMouseDown)
            {
                if (!_leftMouseDownDrag)
                {
                    Point thisPosition = new Point(e.X, e.Y);
        
                    bool startDrag = false;

                    if (_dragFromControl)
                        startDrag = !this.ClientRectangle.Contains(thisPosition);
                    else
                    {
                        // Create starting mouse down position
                        Rectangle dragRect = new Rectangle(_leftMouseDownPos, new Size(0,0));
                        
                        // Expand by size of the double click area
                        dragRect.Inflate(SystemInformation.DoubleClickSize);
                        
                        // Drag when mouse moves outside the double click area
                        startDrag = !dragRect.Contains(thisPosition);
                    }

                    if (startDrag && !_ignoreDownDrag)
                    {
                        // Generate event for interested parties
                        OnPageDragStart(e);
		
                        // Enter dragging mode
                        _leftMouseDownDrag = true;	
                    }
                }
                else
                {
                    // Generate event for interested parties
                    OnPageDragMove(e);
                }
            }
            else
            {
                if (_hotTrack || _hoverSelect)
                {
                    int mousePage = -1;
                    bool pageChanged = false;

                    // Create a point representing current mouse position
                    Point mousePos = new Point(e.X, e.Y);

                    // Find the page this mouse point is inside
                    for(int pos=0; pos<_tabPages.Count; pos++)
                    {
                        Rectangle rect = (Rectangle)_tabRects[pos];

                        if (rect.Contains(mousePos))
                        {
                            mousePage = pos;
                            break;
                        }
                    }

                    // Should moving over a tab cause selection changes?
                    if (_hoverSelect && !_multiline && (mousePage != -1))
                    {
                        // Has the selected page changed?
                        if (mousePage != _pageSelected)
                        {
                            // Move selection to new page
                            MovePageSelection(_tabPages[mousePage]);

                            pageChanged = true;
                        }
                    }

                    if (_hotTrack && !pageChanged && (mousePage != _hotTrackPage))
                    {
                        Graphics g = this.CreateGraphics();

                        // Clip the drawing to prevent drawing in unwanted areas
                        ClipDrawingTabs(g);

                        // Remove highlight of old page
                        if (_hotTrackPage != -1)
                            DrawTab(_tabPages[_hotTrackPage], g, false);

                        _hotTrackPage = mousePage;

                        // Add highlight to new page
                        if (_hotTrackPage != -1)
                            DrawTab(_tabPages[_hotTrackPage], g, true);

                        // Must correctly release resource
                        g.Dispose();
                    }
                }
            }
            
            base.OnMouseMove(e);
        }

        protected override void OnMouseEnter(EventArgs e)
        {
			try
			{
				_mouseOver = true;
				_overTimer.Stop();
	            
				base.OnMouseEnter(e);
			}
			catch(Exception)
			{
			}
		}

        protected override void OnMouseLeave(EventArgs e)
        {
            if (_hotTrack)
            {
                int newTrackPage = -1;

                if (newTrackPage != _hotTrackPage)
                {
                    Graphics g = this.CreateGraphics();

                    // Clip the drawing to prevent drawing in unwanted areas
                    ClipDrawingTabs(g);

                    // Remove highlight of old page
                    if (_hotTrackPage != -1)
                        DrawTab(_tabPages[_hotTrackPage], g, false);

                    _hotTrackPage = newTrackPage;

                    // Must correctly release resource
                    g.Dispose();
                }
            }

			try
			{
				_overTimer.Start();
			}
			catch(Exception)
			{
			}

            base.OnMouseLeave(e);
        }		

		protected override void OnDragEnter(DragEventArgs e)
		{
			// Default to an impossible position
			_mouseDragOver = new Point(-1, -1);

			base.OnDragEnter(e);
		}

		protected override void OnDragOver(DragEventArgs e)
		{
			// Allow to select pages on drag over?
			if (_dragOverSelect)
			{
				// Remember mouse position for when timer expires
				Point mouseDragOver = PointToClient(new Point(e.X, e.Y));

				// If a new position has been found then restart timer
				if (mouseDragOver != _mouseDragOver)
				{
					// Use the new position
					_mouseDragOver = mouseDragOver;

					// Stop if already running
					_dragTimer.Stop();

					// Start running from beginning
					_dragTimer.Start();
				}
			}

			base.OnDragOver(e);
		}

		protected override void OnDragLeave(EventArgs e)
		{
			// Stop if already running
			_dragTimer.Stop();

			base.OnDragLeave(e);
		}

		protected virtual void OnPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
		{
			// Are we using the default menu or a user defined value?
			if (_defaultFont)
			{
				DefineFont(SystemInformation.MenuFont);

                Recalculate();
                Invalidate();
			}
		}

		protected override void OnSystemColorsChanged(EventArgs e)
		{
			// If still using the Default color when we were created
			if (_defaultColor)
			{
				DefineBackColor(TabControl.DefaultBackColor);

                Recalculate();
                Invalidate();
			}

			base.OnSystemColorsChanged(e);
		}
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Austria Austria
My name is Daniel and I am from Austria.
When I was in High School, all I wanted to do was programming. After finishing High School, I joined a company for which I wrote a project management utility in Visual Basic 6. It was then that I realised that programming all day long was nothing I wanted to do for the rest of my life.

I quit the job and started to study computer and media security at polytechnical university in Hagenberg/Austria.

As of now, I'm still studying. I still like to program, as long as I can do something else too. Recently I switched my favorite programming language to c#. Together with a friend from university, we started a project where we implement OpenPGP (RFC2440) in c#.

Comments and Discussions