File: winforms\Managed\System\WinForms\ToolStripSplitButton.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="ToolStripSplitButton.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
 
namespace System.Windows.Forms {
    using System;
    using System.Security;
    using System.Security.Permissions;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Drawing.Imaging;
    using System.ComponentModel;
    using System.Windows.Forms.Design; 
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Diagnostics;
    using System.Windows.Forms.Layout;
    using System.Runtime.Versioning;
    
    /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton"]/*' />
    /// <devdoc/>
    [
    ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip),
    DefaultEvent("ButtonClick")
    ]
    public class ToolStripSplitButton : ToolStripDropDownItem {
        
        private ToolStripItem                    defaultItem                =  null;
        private ToolStripSplitButtonButton       splitButtonButton          =  null;      
        private Rectangle                     dropDownButtonBounds       =  Rectangle.Empty;
        private ToolStripSplitButtonButtonLayout splitButtonButtonLayout    =  null;
        private int                           dropDownButtonWidth        =  0;
        private int                           splitterWidth              =  1;
        private Rectangle                     splitterBounds             =  Rectangle.Empty;
        private byte                          openMouseId                =  0;
        private long lastClickTime = 0;
 
        private const int DEFAULT_DROPDOWN_WIDTH = 11;
 
        private static readonly object EventDefaultItemChanged                         = new object();
        private static readonly object EventButtonClick                                = new object();
        private static readonly object EventButtonDoubleClick                          = new object();
        private static readonly object EventDropDownOpened                             = new object();
        private static readonly object EventDropDownClosed                             = new object();
 
        private static bool isScalingInitialized = false;
        private static int scaledDropDownButtonWidth = DEFAULT_DROPDOWN_WIDTH;
       
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.ToolStripSplitButton"]/*' />
        /// <devdoc>
        /// Summary of ToolStripSplitButton.
        /// </devdoc>
        public ToolStripSplitButton() {
            Initialize(); // all additional work should be done in Initialize
        }
        public ToolStripSplitButton(string text):base(text,null,(EventHandler)null) {
            Initialize(); 
        }
        public ToolStripSplitButton(Image image):base(null,image,(EventHandler)null) {
            Initialize(); 
        }
        public ToolStripSplitButton(string text, Image image):base(text,image,(EventHandler)null) {
            Initialize(); 
        }
        public ToolStripSplitButton(string text, Image image, EventHandler onClick):base(text,image,onClick) {
            Initialize(); 
        }
        public ToolStripSplitButton(string text, Image image, EventHandler onClick, string name) :base(text,image,onClick,name){
            Initialize(); 
        }
        public ToolStripSplitButton(string text, Image image, params ToolStripItem[] dropDownItems):base(text,image,dropDownItems) {
            Initialize(); 
        }
 
        [DefaultValue(true)]
        public new bool AutoToolTip {
            get { 
                return base.AutoToolTip;
            }
            set {
                base.AutoToolTip = value;
            }
        }
 
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.ButtonBounds"]/*' />
        /// <devdoc>
        /// Summary of ToolStripSplitButton.
        /// </devdoc>
        [Browsable(false)]
        public Rectangle ButtonBounds {
            get {
                //Rectangle bounds = SplitButtonButton.Bounds;
                //bounds.Offset(this.Bounds.Location);
                return SplitButtonButton.Bounds;
            }
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.ButtonPressed"]/*' />
        /// <devdoc>
        /// Summary of ButtonPressed.
        /// </devdoc>
        [Browsable(false)]
        public bool ButtonPressed {
            get {
                return SplitButtonButton.Pressed;
 
            }
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.ButtonSelected"]/*' />
        /// <devdoc>
        /// Summary of ButtonPressed.
        /// </devdoc>
        [Browsable(false)]
        public bool ButtonSelected {
            get {
                return SplitButtonButton.Selected || DropDownButtonPressed;
            }
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.ButtonClick"]/*' />
        /// <devdoc>
        /// <para>Occurs when the button portion of a split button is clicked.</para>
        /// </devdoc>
        [
        SRCategory(SR.CatAction),
        SRDescription(SR.ToolStripSplitButtonOnButtonClickDescr)
        ]
        public event EventHandler ButtonClick {
            add { 
                Events.AddHandler(EventButtonClick, value); 
            }
            remove {
                Events.RemoveHandler(EventButtonClick, value);
            }
        }
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.ButtonDoubleClick"]/*' />
        /// <devdoc>
        /// <para>Occurs when the utton portion of a split button  is double clicked.</para>
        /// </devdoc>
        [
        SRCategory(SR.CatAction),
        SRDescription(SR.ToolStripSplitButtonOnButtonDoubleClickDescr)
        ]
        public event EventHandler ButtonDoubleClick {
            add {
                Events.AddHandler(EventButtonDoubleClick, value);
            }
            remove {
                Events.RemoveHandler(EventButtonDoubleClick, value);
            }
        }
 
 
        protected override bool DefaultAutoToolTip {
            get { 
               return true; 
            }
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.DefaultItem"]/*' />
        /// <devdoc>
        /// Summary of DefaultItem.
        /// </devdoc>
        [DefaultValue(null), Browsable(false)]
        public ToolStripItem DefaultItem {
            get { 
                return defaultItem; 
            }
            set { 
                if (defaultItem != value) {
                    OnDefaultItemChanged(new EventArgs()); 
                    defaultItem = value; 
                }
            }
        }
     
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.DefaultItemChanged"]/*' />
        /// <devdoc>
        /// <para>Occurs when the default item has changed</para>
        /// </devdoc>
        [
        SRCategory(SR.CatAction),
        SRDescription(SR.ToolStripSplitButtonOnDefaultItemChangedDescr)
        ]
        public event EventHandler DefaultItemChanged {
            add { 
                Events.AddHandler(EventDefaultItemChanged, value); 
            }
            remove {
                Events.RemoveHandler(EventDefaultItemChanged, value);
            }
        }
	
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.DismissWhenClicked"]/*' />
        /// <devdoc>
        /// specifies the default behavior of these items on ToolStripDropDowns when clicked.
        /// </devdoc>
        internal protected override bool DismissWhenClicked {
            get {
                return DropDown.Visible != true;
            }
 
        }
        
        internal override Rectangle DropDownButtonArea {
               get { return this.DropDownButtonBounds; }
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.DropDownButtonBounds"]/*' />
        /// <devdoc>
        /// The bounds of the DropDown in ToolStrip coordinates.
        /// </devdoc>
        [Browsable(false)]
        public Rectangle DropDownButtonBounds {
            get {
                 return dropDownButtonBounds; 
            }
 
        }
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.DropDownButtonPressed"]/*' />
        /// <devdoc>
        /// Summary of DropDownButtonBounds.
        /// </devdoc>
        [Browsable(false)]
        public bool DropDownButtonPressed {
            get {
                // 
                return DropDown.Visible; 
            }
        }
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.DropDownButtonSelected"]/*' />
        /// <devdoc>
        /// Summary of DropDownButtonSelected.
        /// </devdoc>
        [Browsable(false)]
        public bool DropDownButtonSelected{
            get {
                return this.Selected; 
            }
        }
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.DropDownButtonWidth"]/*' />
        /// <devdoc>
        /// Summary of DropDownButtonWidth.
        /// </devdoc>
        [
        SRCategory(SR.CatLayout),
        SRDescription(SR.ToolStripSplitButtonDropDownButtonWidthDescr)
        ]
        public int DropDownButtonWidth {
            get{ 
                return dropDownButtonWidth;
            }
            set {
                if (value < 0) {
                    // throw if less than 0.
                    throw new ArgumentOutOfRangeException("DropDownButtonWidth", SR.GetString(SR.InvalidLowBoundArgumentEx, "DropDownButtonWidth", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture)));                
                }
            
                if (dropDownButtonWidth != value) {
                    dropDownButtonWidth = value;
                    InvalidateSplitButtonLayout();
                    InvalidateItemLayout(PropertyNames.DropDownButtonWidth, true);
                }
             }
        }
 
        /// <devdoc>
        /// This is here for serialization purposes.
        /// </devdoc>
        private int DefaultDropDownButtonWidth {
            get {
                // lets start off with a size roughly equivalent to a combobox dropdown
                if (!isScalingInitialized) {
                    if (DpiHelper.IsScalingRequired) {
                        scaledDropDownButtonWidth = DpiHelper.LogicalToDeviceUnitsX(DEFAULT_DROPDOWN_WIDTH);
                    }                   
                    isScalingInitialized = true;
                }
 
                return scaledDropDownButtonWidth;
            }
        }
 
     
                
        /// <summary>
        /// Just used as a convenience to help manage layout
        /// </summary>
        private ToolStripSplitButtonButton SplitButtonButton {
            get { 
                if (splitButtonButton == null) { 
                    splitButtonButton = new ToolStripSplitButtonButton(this);
                }
                splitButtonButton.Image = this.Image;
                splitButtonButton.Text = this.Text;
                splitButtonButton.BackColor = this.BackColor;
                splitButtonButton.ForeColor = this.ForeColor;
                splitButtonButton.Font = this.Font;
                splitButtonButton.ImageAlign = this.ImageAlign;
                splitButtonButton.TextAlign = this.TextAlign;
                splitButtonButton.TextImageRelation = this.TextImageRelation;
                return splitButtonButton;
            }
        }
        /// <devdoc>
        /// Summary of SplitButtonButtonLayout.
        /// </devdoc>	
        internal ToolStripItemInternalLayout SplitButtonButtonLayout {
            get { 
                // For preferred size caching reasons, we need to keep our two 
                // internal layouts (button, dropdown button) in sync. 
             
                if (InternalLayout != null /*if layout is invalid - calls CreateInternalLayout - which resets splitButtonButtonLayout to null*/
                    && splitButtonButtonLayout == null) {
                    splitButtonButtonLayout = new ToolStripSplitButtonButtonLayout(this);
                }
                return splitButtonButtonLayout;
            }
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.SplitterWidth"]/*' />
        /// <devdoc>
        /// the width of the separator between the default and drop down button
        /// </devdoc>
        [
        SRDescription(SR.ToolStripSplitButtonSplitterWidthDescr),
        SRCategory(SR.CatLayout),
        Browsable(false),
        EditorBrowsable(EditorBrowsableState.Advanced)
        ]
        internal int SplitterWidth {
            get {
                return splitterWidth;
            }
            set {
                if (value < 0) {
                    splitterWidth = 0;
                }
                else {
                    splitterWidth = value;
                }
                InvalidateSplitButtonLayout();
            }
        }
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.SplitterBounds"]/*' />
        /// <devdoc>
        /// the boundaries of the separator between the default and drop down button, exposed for custom
        /// painting purposes.
        /// </devdoc>
        [Browsable(false)]
        public Rectangle SplitterBounds {
            get {
                return splitterBounds;
            }
        }
        /// <devdoc>
        /// Summary of CalculateLayout.
        /// </devdoc>	
        private void CalculateLayout() {
 
            // Figure out where the DropDown image goes.
            Rectangle dropDownButtonBounds = new Rectangle(Point.Empty, this.Size);
            Rectangle splitButtonButtonBounds = Rectangle.Empty;
			
           
            dropDownButtonBounds = new Rectangle(Point.Empty, new Size(Math.Min(this.Width, DropDownButtonWidth), this.Height));
   			
            // Figure out the height and width of the selected item.
            int splitButtonButtonWidth = Math.Max(0, this.Width - dropDownButtonBounds.Width);
            int splitButtonButtonHeight = Math.Max(0, this.Height);
 
            splitButtonButtonBounds = new Rectangle(Point.Empty, new Size(splitButtonButtonWidth, splitButtonButtonHeight));
 
            // grow the selected item by one since we're overlapping the borders.
            splitButtonButtonBounds.Width -= splitterWidth; 
 
            if (this.RightToLeft == RightToLeft.No) {
                // the dropdown button goes on the right
                dropDownButtonBounds.Offset(splitButtonButtonBounds.Right+splitterWidth, 0);
                splitterBounds = new Rectangle(splitButtonButtonBounds.Right, splitButtonButtonBounds.Top, splitterWidth, splitButtonButtonBounds.Height);
            }
            else {
                // the split button goes on the right.
                splitButtonButtonBounds.Offset(DropDownButtonWidth+splitterWidth, 0);
                splitterBounds = new Rectangle(dropDownButtonBounds.Right, dropDownButtonBounds.Top, splitterWidth, dropDownButtonBounds.Height);
      
            }
            
            this.SplitButtonButton.SetBounds(splitButtonButtonBounds);
            this.SetDropDownButtonBounds(dropDownButtonBounds);
 
        }
 
        protected override AccessibleObject CreateAccessibilityInstance() {
            if (AccessibilityImprovements.Level3) {
                return new ToolStripSplitButtonUiaProvider(this);
            }
            else if (AccessibilityImprovements.Level1) {
                return new ToolStripSplitButtonExAccessibleObject(this);
            }
            else {
                return new ToolStripSplitButtonAccessibleObject(this);
            }
       }
 
        protected override ToolStripDropDown CreateDefaultDropDown() {
             // AutoGenerate a Winbar DropDown - set the property so we hook events
              return new ToolStripDropDownMenu(this, /*isAutoGenerated=*/true);
         }
 
 
        internal override ToolStripItemInternalLayout CreateInternalLayout() {
            // whenever the master layout is invalidated - invalidate the splitbuttonbutton layout.
            this.splitButtonButtonLayout = null;
            return new ToolStripItemInternalLayout(this);
            
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.GetPreferredSize"]/*' />
        public override Size GetPreferredSize(Size constrainingSize) {
            Size preferredSize = SplitButtonButtonLayout.GetPreferredSize(constrainingSize);
            preferredSize.Width += DropDownButtonWidth + SplitterWidth + Padding.Horizontal;
            return preferredSize;
        }
 
        /// <devdoc>
        /// Summary of InvalidateSplitButtonLayout.
        /// </devdoc>	
        private void InvalidateSplitButtonLayout() {
            this.splitButtonButtonLayout = null;	
            CalculateLayout();
        }
 
        private void Initialize() {           
            dropDownButtonWidth = DefaultDropDownButtonWidth;
            SupportsSpaceKey = true;
        }
 
        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
        protected internal override bool ProcessDialogKey(Keys keyData) {
            if (Enabled && (keyData == Keys.Enter || (SupportsSpaceKey && keyData == Keys.Space))) {
               PerformButtonClick();
               return true;
            }
            
            return base.ProcessDialogKey(keyData);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'charCode' matches control.cs
        [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
        protected internal override bool ProcessMnemonic(char charCode) {
           // checking IsMnemonic is not necessary - toolstrip does this for us          
           PerformButtonClick();
           return true;
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnButtonClick"]/*' />
        /// <devdoc>
        /// called when the button portion of a split button is clicked
        /// if there is a default item, this will route the click to the default item
        /// </devdoc>
        protected virtual void OnButtonClick(System.EventArgs e) {
 
            if (DefaultItem != null) {
                DefaultItem.FireEvent(ToolStripItemEventType.Click);
            }
 
            EventHandler handler = (EventHandler)Events[EventButtonClick];
            if (handler != null) handler(this, e);
 
        }
        
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnButtonDoubleClick"]/*' />
        /// <devdoc>
        /// called when the button portion of a split button is double clicked
        /// if there is a default item, this will route the double click to the default item
        /// </devdoc>
        public virtual void OnButtonDoubleClick(System.EventArgs e) {
            if (DefaultItem != null) {
                DefaultItem.FireEvent(ToolStripItemEventType.DoubleClick);
            }
           
            EventHandler handler = (EventHandler)Events[EventButtonDoubleClick];
            if (handler != null) handler(this,e);
        }
 
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnDefaultItemChanged"]/*' />
        /// <devdoc>
        /// Inheriting classes should override this method to handle this event.
        /// </devdoc>
        protected virtual void OnDefaultItemChanged(EventArgs e) {
            InvalidateSplitButtonLayout();
            if (CanRaiseEvents) {
                EventHandler eh = Events[EventDefaultItemChanged] as EventHandler;
                if (eh != null) {
                    eh(this, e);
                }
            }
 
        }        
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnMouseDown"]/*' />
        /// <devdoc>
        /// Summary of OnMouseDown.
        /// </devdoc>
        protected override void OnMouseDown(MouseEventArgs e) {
		    
            if (DropDownButtonBounds.Contains(e.Location)) {
                if (e.Button == MouseButtons.Left) {
                   
                    if (!DropDown.Visible) {
                        Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID");
                        openMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId();
                        this.ShowDropDown(/*mousePress = */true);
                    }
                }
            }
            else {
                SplitButtonButton.Push(true);
            }
 
        }
        
            
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnMouseUp"]/*' />
        /// <devdoc>
        /// Summary of OnMouseUp.
        /// </devdoc>
        protected override void OnMouseUp(MouseEventArgs e) {
            if (!Enabled) {
                return;
            }
           
 
            SplitButtonButton.Push(false);
 
            if (DropDownButtonBounds.Contains(e.Location)) {
                if (e.Button == MouseButtons.Left) {
                    if (DropDown.Visible) {
                        Debug.Assert(ParentInternal != null, "Parent is null here, not going to get accurate ID");
                        byte closeMouseId = (ParentInternal == null) ? (byte)0: ParentInternal.GetMouseId();
                        if (closeMouseId != openMouseId) {
                            openMouseId = 0;  // reset the mouse id, we should never get this value from toolstrip.
                            ToolStripManager.ModalMenuFilter.CloseActiveDropDown(DropDown, ToolStripDropDownCloseReason.AppClicked);
                            Select();
                       }
                    }
                }
            }
            Point clickPoint = new Point(e.X, e.Y);
            if ((e.Button == MouseButtons.Left) && this.SplitButtonButton.Bounds.Contains(clickPoint)) {
                bool shouldFireDoubleClick = false;
                if (DoubleClickEnabled) {
                    long newTime = DateTime.Now.Ticks;
                    long deltaTicks = newTime - lastClickTime;
                    lastClickTime = newTime;
                    // use >= for cases where the succession of click events is so fast it's not picked up by
                    // DateTime resolution.
                    Debug.Assert(deltaTicks >= 0, "why are deltaticks less than zero? thats some mighty fast clicking");
                    // if we've seen a mouse up less than the double click time ago, we should fire.
                    if (deltaTicks >= 0 && deltaTicks < DoubleClickTicks) {
                        shouldFireDoubleClick = true;
                    }
                }
                if (shouldFireDoubleClick) {
                    OnButtonDoubleClick(new System.EventArgs());
                    // VSWhidbey 486983: if we actually fired DoubleClick - reset the lastClickTime.
                    lastClickTime = 0;
                } 
                else {
                    OnButtonClick(new System.EventArgs());
                }           
            }
 
        }
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnMouseLeave"]/*' />
        protected override void OnMouseLeave(EventArgs e) {
            openMouseId = 0;  // reset the mouse id, we should never get this value from toolstrip.
            SplitButtonButton.Push(false);
            base.OnMouseLeave(e);
        }
      	
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnRightToLeftChanged"]/*' />
        /// <devdoc>
        /// Summary of OnRightToLeftChanged.
        /// </devdoc>
        protected override void OnRightToLeftChanged(EventArgs e) {
            base.OnRightToLeftChanged(e);
            InvalidateSplitButtonLayout();			
        }
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.OnPaint"]/*' />
        /// <devdoc>
        /// Summary of OnPaint.
        /// </devdoc>
        /// <param name=e></param>
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
 
             ToolStripRenderer renderer = this.Renderer;
             if (renderer != null) {
                 InvalidateSplitButtonLayout();
                 Graphics g = e.Graphics;
 
                 renderer.DrawSplitButton(new ToolStripItemRenderEventArgs(g, this));
 
                 if ((DisplayStyle & ToolStripItemDisplayStyle.Image) != ToolStripItemDisplayStyle.None)  {   
                     renderer.DrawItemImage(new ToolStripItemImageRenderEventArgs(g, this, SplitButtonButtonLayout.ImageRectangle));             
                 }
 
                 if ((DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.None) {                    
                      renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(g, this, SplitButtonButton.Text, SplitButtonButtonLayout.TextRectangle, this.ForeColor, this.Font, SplitButtonButtonLayout.TextFormat));
                 }
             }
        }  
 
        public void PerformButtonClick() {
            if (Enabled && Available) {
                PerformClick();
                OnButtonClick(EventArgs.Empty);
            }
        }
 
        /// <include file='doc\ToolStripComboButton.uex' path='docs/doc[@for="ToolStripSplitButton.ResetDropDownButtonWidth"]/*' />
        /// <devdoc>
        /// Resets the RightToLeft to be the default.
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public virtual void ResetDropDownButtonWidth() {
            DropDownButtonWidth = DefaultDropDownButtonWidth;
        }
 
        /// <devdoc>
        /// Summary of SetDropDownBounds.
        /// </devdoc>
        private void SetDropDownButtonBounds(Rectangle rect) {
            dropDownButtonBounds = rect; 
        }
        /// <devdoc>
        /// <para>Determines if the <see cref='System.Windows.Forms.ToolStripItem.Size'/> property needs to be persisted.</para>
        /// </devdoc>
        [EditorBrowsable(EditorBrowsableState.Never)]
        internal virtual bool ShouldSerializeDropDownButtonWidth() {
            return  (DropDownButtonWidth != DefaultDropDownButtonWidth);
        }
            
        /// <devdoc>
        ///  This class represents the item to the left of the dropdown [ A |v]  (e.g the "A")  
        ///  It exists so that we can use our existing methods for text and image layout
        ///  and have a place to stick certain state information like pushed and selected 
        ///  Note since this is NOT an actual item hosted on the Winbar - it wont get things
        ///  like MouseOver, wont be laid out by the ToolStrip, etc etc.  This is purely internal
        ///  convenience.
        /// </devdoc>
        private class ToolStripSplitButtonButton : ToolStripButton {
 
            private ToolStripSplitButton owner = null;
 
            public ToolStripSplitButtonButton(ToolStripSplitButton owner) {
                   this.owner = owner;
            }
 
            public override bool Enabled {
                get {
                    return owner.Enabled;
                }
                set {
                    // do nothing
                }
            }
            
 
            public override ToolStripItemDisplayStyle DisplayStyle {
                get {
                    return owner.DisplayStyle;
                }
                set {
                    // do nothing
                }
            }
 
            public override Padding Padding {
                get {
                    return this.owner.Padding;
                }
                set {
                    // do nothing
                }
            }
 
          
            public override ToolStripTextDirection TextDirection {
                get {
                    return owner.TextDirection;
                }
            }
            
            
            public override Image Image {
                [ResourceExposure(ResourceScope.Machine)]
                [ResourceConsumption(ResourceScope.Machine)]
                get {
                    if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Image) == ToolStripItemDisplayStyle.Image) {
                        return owner.Image;
                    }
                    else {
                        return null;
                    }
                }
                set {
                    // do nothing
                }
            }
 
            public override bool Selected {
                get {
                    
                    if (owner != null) {
                        return owner.Selected;
                    }
                    return base.Selected;
                }
            }
 
            public override string Text {
                get {
                    if ((owner.DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text) {
                        return owner.Text;
                    }
                    else {
                        return null;
                    }
                }
                set {
                    // do nothing
                }
            }
 
        }
 
        /// <devdoc>
        ///  This class performs internal layout for the "split button button" portion of a split button.
        ///  Its main job is to make sure the inner button has the same parent as the split button, so
        ///  that layout can be performed using the correct graphics context.
        /// </devdoc>
        private class ToolStripSplitButtonButtonLayout : ToolStripItemInternalLayout {
 
            ToolStripSplitButton owner;
 
            public ToolStripSplitButtonButtonLayout(ToolStripSplitButton owner) : base(owner.SplitButtonButton) {
                this.owner = owner;
            }
 
            protected override ToolStripItem Owner {
                get { return owner; }
            }
 
            protected override ToolStrip ParentInternal {
                get {
                    return owner.ParentInternal;
                }
            }
            public override Rectangle ImageRectangle {
                get {
                    Rectangle imageRect = base.ImageRectangle;
                    // translate to ToolStripItem coordinates
                    imageRect.Offset(owner.SplitButtonButton.Bounds.Location);
                    return imageRect;     
                }
            }
            
            public override Rectangle TextRectangle {
                get {
                    Rectangle textRect = base.TextRectangle;
                    // translate to ToolStripItem coordinates
                    textRect.Offset(owner.SplitButtonButton.Bounds.Location);
                    return textRect;     
                }
            }
        }
 
        /// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItemAccessibleObject"]/*' />        
        [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
        public class ToolStripSplitButtonAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject {
            private ToolStripSplitButton owner;
 
            public ToolStripSplitButtonAccessibleObject(ToolStripSplitButton item) : base(item) {
                owner = item;
            }
 
            /// <include file='doc\ToolStripItem.uex' path='docs/doc[@for="ToolStripItemAccessibleObject.DoDefaultAction"]/*' />
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void DoDefaultAction() {
                owner.PerformButtonClick();
            }
        }
 
        /// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripSplitButtonExAccessibleObject"]/*' /> 
        internal class ToolStripSplitButtonExAccessibleObject: ToolStripSplitButtonAccessibleObject {
 
            private ToolStripSplitButton ownerItem;
 
            public ToolStripSplitButtonExAccessibleObject(ToolStripSplitButton item)
                : base(item) {
                ownerItem = item;
            }
 
            internal override object GetPropertyValue(int propertyID) {
                if (propertyID == NativeMethods.UIA_ControlTypePropertyId) {
                    return NativeMethods.UIA_ButtonControlTypeId;
                }
                else {
                    return base.GetPropertyValue(propertyID);
                }
            }
 
            internal override bool IsIAccessibleExSupported() {
                if (ownerItem != null) {
                    return true;
                }
                else {
                    return base.IsIAccessibleExSupported();
                }
            }
 
            internal override bool IsPatternSupported(int patternId) {
                if (patternId == NativeMethods.UIA_ExpandCollapsePatternId && ownerItem.HasDropDownItems) {
                    return true;
                }
                else {
                    return base.IsPatternSupported(patternId);
                }
            }
 
            internal override void Expand() {
                DoDefaultAction();
            }
 
            internal override void Collapse() {
                if (ownerItem != null && ownerItem.DropDown != null && ownerItem.DropDown.Visible) {
                    ownerItem.DropDown.Close();
                }
            }
 
            internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState {
                get {
                    return ownerItem.DropDown.Visible ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed;
                }
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
                switch (direction) {
                    case UnsafeNativeMethods.NavigateDirection.FirstChild:
                        return DropDownItemsCount > 0 ? ownerItem.DropDown.Items[0].AccessibilityObject : null;
                    case UnsafeNativeMethods.NavigateDirection.LastChild:
                        return DropDownItemsCount > 0 ? ownerItem.DropDown.Items[ownerItem.DropDown.Items.Count - 1].AccessibilityObject : null;
                }
                return base.FragmentNavigate(direction);
            }
 
            private int DropDownItemsCount {
                get {
                    // Do not expose child items when the drop-down is collapsed to prevent Narrator from announcing
                    // invisible menu items when Narrator is in item's mode (CAPSLOCK + Arrow Left/Right) or
                    // in scan mode (CAPSLOCK + Space)
                    if (AccessibilityImprovements.Level3 && ExpandCollapseState == UnsafeNativeMethods.ExpandCollapseState.Collapsed) {
                        return 0;
                    }
 
                    return ownerItem.DropDownItems.Count;
                }
            }
        }
 
        internal class ToolStripSplitButtonUiaProvider : ToolStripDropDownItemAccessibleObject {
            private ToolStripSplitButton _owner;
            private ToolStripSplitButtonExAccessibleObject _accessibleObject;
 
            public ToolStripSplitButtonUiaProvider(ToolStripSplitButton owner) : base(owner) {
                _owner = owner;
                _accessibleObject = new ToolStripSplitButtonExAccessibleObject(owner);
            }
 
            public override void DoDefaultAction() {
                _accessibleObject.DoDefaultAction();
            }
 
            internal override object GetPropertyValue(int propertyID) {
                return _accessibleObject.GetPropertyValue(propertyID);
            }
 
            internal override bool IsIAccessibleExSupported() {
                return true;
            }
 
            internal override bool IsPatternSupported(int patternId) {
                return _accessibleObject.IsPatternSupported(patternId);
            }
 
            internal override void Expand() {
                DoDefaultAction();
            }
 
            internal override void Collapse() {
                _accessibleObject.Collapse();
            }
 
            internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState {
                get {
                    return _accessibleObject.ExpandCollapseState;
                }
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
                return _accessibleObject.FragmentNavigate(direction);
            }
        }
    }
}