File: winforms\Managed\System\WinForms\ToolStripDropDownMenu.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)

namespace System.Windows.Forms {
 
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.ComponentModel.Design.Serialization;
    using System.Diagnostics;
    using System.Drawing;
    using System.Globalization;
    using System.Windows.Forms.Layout;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;    
    using System.Windows.Forms.Internal;
    using System.Collections.Specialized;
    using System.Diagnostics.CodeAnalysis;
    
    /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu"]/*' />
    [Designer("System.Windows.Forms.Design.ToolStripDropDownDesigner, " + AssemblyRef.SystemDesign)]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    public class ToolStripDropDownMenu : ToolStripDropDown {
 
            private static readonly Padding ImagePadding    = new Padding(2);
            private static readonly Padding TextPadding     = new Padding(8,1,9,1);
            private static readonly Padding CheckPadding    = new Padding(5,2,2,2);
            private static readonly Padding ArrowPadding    = new Padding(0,0,8,0);
 
            // This is totally a UI Fudge - if we have an image or check margin with 
            // no image or checks in it use this - which is consistent with office 
            // and an image margin with a 16x16 icon in it.
            private static int DefaultImageMarginWidth = 24; // 24+1px border - with scaling we add this 1px to new, scaled, field value
            private static int DefaultImageAndCheckMarginWidth = 46;  // 46+1px border - with scaling we add this 1px to new, scaled, field value    
 
            private static int ArrowSize = 10;
 
            private Size maxItemSize            = Size.Empty;
            private Rectangle checkRectangle    = Rectangle.Empty;
            private Rectangle imageRectangle    = Rectangle.Empty;
            private Rectangle arrowRectangle    = Rectangle.Empty;
            private Rectangle textRectangle     = Rectangle.Empty;
            private Rectangle imageMarginBounds = Rectangle.Empty;
            private int paddingToTrim = 0;
            private int tabWidth = -1;
 
            private ToolStripScrollButton upScrollButton = null;
            private ToolStripScrollButton downScrollButton = null;
            private int scrollAmount = 0;
            private int indexOfFirstDisplayedItem = -1;
 
            private BitVector32                    state                               = new BitVector32();
 
            private static readonly int stateShowImageMargin          = BitVector32.CreateMask();
            private static readonly int stateShowCheckMargin          = BitVector32.CreateMask(stateShowImageMargin);
            private static readonly int stateMaxItemSizeValid         = BitVector32.CreateMask(stateShowCheckMargin);
 
            private static readonly Size DefaultImageSize = new Size(16, 16);
 
            private Size scaledDefaultImageSize                = DefaultImageSize;
            private int scaledDefaultImageMarginWidth          = DefaultImageMarginWidth + 1; // 1px for border
            private int scaledDefaultImageAndCheckMarginWidth  = DefaultImageAndCheckMarginWidth + 1; // 1px for border
            private Padding scaledImagePadding                 = ImagePadding;
            private Padding scaledTextPadding                  = TextPadding;
            private Padding scaledCheckPadding                 = CheckPadding;
            private Padding scaledArrowPadding                 = ArrowPadding;
            private int scaledArrowSize                        = ArrowSize;
 
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.ToolStripDropDownMenu"]/*' />
            /// <devdoc>
            /// Summary of ToolStripDropDown.
            /// </devdoc>
            public ToolStripDropDownMenu(){
            }
 
        /// <devdoc>
        /// Constructor to autogenerate
        /// </devdoc>
        internal ToolStripDropDownMenu(ToolStripItem ownerItem, bool isAutoGenerated) : base(ownerItem, isAutoGenerated) {
            if (DpiHelper.IsScalingRequired) {
                scaledDefaultImageSize = DpiHelper.LogicalToDeviceUnits(DefaultImageSize);
                scaledDefaultImageMarginWidth = DpiHelper.LogicalToDeviceUnitsX(DefaultImageMarginWidth) + 1; // 1px for border
                scaledDefaultImageAndCheckMarginWidth = DpiHelper.LogicalToDeviceUnitsX(DefaultImageAndCheckMarginWidth) + 1; // 1px for border
 
                if (DpiHelper.EnableToolStripHighDpiImprovements) {
                    scaledImagePadding = DpiHelper.LogicalToDeviceUnits(ImagePadding);
                    scaledTextPadding = DpiHelper.LogicalToDeviceUnits(TextPadding);
                    scaledCheckPadding = DpiHelper.LogicalToDeviceUnits(CheckPadding);
                    scaledArrowPadding = DpiHelper.LogicalToDeviceUnits(ArrowPadding);
                    scaledArrowSize = DpiHelper.LogicalToDeviceUnitsX(ArrowSize);
                }
            }
        }
 
        internal override bool AllItemsVisible {
                get {
                    return !RequiresScrollButtons;
                }
                set {
                    RequiresScrollButtons = !value;
                }
            }
 
            internal Rectangle ArrowRectangle {
                get {
                    return arrowRectangle;
                }
            }
            
            internal Rectangle CheckRectangle {
                get {
                    return checkRectangle;
                }
            }
            
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.DefaultPadding"]/*' />
            protected override Padding DefaultPadding {
                get { 
                    RightToLeft rightToLeft = RightToLeft;
                    
                    int textPadding = (rightToLeft == RightToLeft.Yes) ? scaledTextPadding.Right : scaledTextPadding.Left;
                    int padding = (ShowCheckMargin || ShowImageMargin) ? textPadding + ImageMargin.Width : textPadding;
 
                    // scooch in all the items by the margin.
                    if (rightToLeft == RightToLeft.Yes) {
                         return new Padding(1,2,padding,2);
                    }
                    return new Padding(padding,2,1,2);
                }
            }
 
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.DisplayRectangle"]/*' />
            public override Rectangle DisplayRectangle {
                get {
                    Rectangle rect = base.DisplayRectangle;
                    if (GetToolStripState(STATE_SCROLLBUTTONS)) {
 
                        rect.Y += UpScrollButton.Height + UpScrollButton.Margin.Vertical;
                        rect.Height -= UpScrollButton.Height + UpScrollButton.Margin.Vertical + DownScrollButton.Height + DownScrollButton.Margin.Vertical;
                        // Because we're going to draw the scroll buttons on top of the padding, we need to add it back in here.
                        rect = LayoutUtils.InflateRect(rect, new Padding(0, this.Padding.Top, 0, this.Padding.Bottom));
                    }
                    return rect;
                }
            }
 
            private ToolStripScrollButton DownScrollButton {
                get {
                    if (downScrollButton == null) {
                        downScrollButton = new ToolStripScrollButton(false);
                        downScrollButton.ParentInternal = this;
                    }
                    return downScrollButton;
                }
            }
            /// <devdoc>
            ///  the rectangle representing 
            /// </devdoc>
            internal Rectangle ImageRectangle {
                get {
                    return imageRectangle;
                }
            }
 
            internal int PaddingToTrim {
                get {
                    return paddingToTrim;
                }
                set {
                    if (paddingToTrim != value) {
                        paddingToTrim = value;
                        AdjustSize();
                    }
                    
                }
            }
            
            /// <devdoc>
            ///  the rectangle representing the color stripe in the menu - this will appear as AffectedBounds
            ///  in the ToolStripRenderEventArgs
            /// </devdoc>
            internal Rectangle ImageMargin {
                get {
                    imageMarginBounds.Height =this.Height; 
                    return imageMarginBounds;
                }
            }
            
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.LayoutEngine"]/*' />
            public override LayoutEngine LayoutEngine {
                get { 
                     return ToolStripDropDownLayoutEngine.LayoutInstance;
                }
            }
 
            [
            DefaultValue(ToolStripLayoutStyle.Flow)
            ]
            public new ToolStripLayoutStyle LayoutStyle {
                get { return base.LayoutStyle; }
                set { base.LayoutStyle = value; }
            }
 
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.MaxItemSize"]/*' />
            protected internal override Size MaxItemSize {
                get {
                    if (!state[stateMaxItemSizeValid]) {
                        CalculateInternalLayoutMetrics();
                    }
                    return maxItemSize;
                }
            }
            
            /// <include file='doc\WinBarDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.ShowImageMargin"]/*' />
            [
            DefaultValue(true),
            SRDescription(SR.ToolStripDropDownMenuShowImageMarginDescr),
            SRCategory(SR.CatAppearance)
            ]
            public bool ShowImageMargin {
                get {
                    return state[stateShowImageMargin];
                }
                set {
                    if (value != state[stateShowImageMargin]) {
                        state[stateShowImageMargin] = value;
                        LayoutTransaction.DoLayout(this, this, PropertyNames.ShowImageMargin);
                    }
                }
            }
 
            /// <include file='doc\WinBarDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.ShowCheckMargin"]/*' />
            [
            DefaultValue(false),
            SRDescription(SR.ToolStripDropDownMenuShowCheckMarginDescr),
            SRCategory(SR.CatAppearance)
            ]
            public bool ShowCheckMargin {
                get {
                    return state[stateShowCheckMargin];
                }
                set {
                    if (value != state[stateShowCheckMargin]) {
                        state[stateShowCheckMargin] = value;
                        LayoutTransaction.DoLayout(this, this, PropertyNames.ShowCheckMargin);
                    }
                }
            }
    
 
            internal Rectangle TextRectangle {
                get {
                    return textRectangle;
                }
            }
 
            private ToolStripScrollButton UpScrollButton {
                get {
                    if (upScrollButton == null) {
                        upScrollButton = new ToolStripScrollButton(true);
                        upScrollButton.ParentInternal = this;
                    }
                    return upScrollButton;
                }
            }
 
            /// <devdoc> this takes a native menu and builds up a managed toolstrip around it.
            ///          Scenario: showing the items from the SystemMenu.
            ///          targetWindow is the window to send WM_COMMAND, WM_SYSCOMMAND to
            ///          hmenu is a handle to the native menu
            ///
            ///  
 
            internal static ToolStripDropDownMenu FromHMenu(IntPtr hmenu, IWin32Window targetWindow) {
                ToolStripDropDownMenu managedDropDown = new ToolStripDropDownMenu();
                managedDropDown.SuspendLayout();
             
 
                HandleRef menuHandle = new HandleRef(null, hmenu);
                int count = UnsafeNativeMethods.GetMenuItemCount(menuHandle);
 
                ToolStripItem itemToAdd;
 
                // surf through the items in the collection, building up TSMenuItems and TSSeparators
                // corresponding to the native menu.
                for (int i = 0; i < count; i++) {
                    // peek at the i'th item.
                    NativeMethods.MENUITEMINFO_T_RW info = new NativeMethods.MENUITEMINFO_T_RW();
                    info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T_RW));
                    info.fMask = NativeMethods.MIIM_FTYPE;
                    info.fType = NativeMethods.MIIM_FTYPE;
                    UnsafeNativeMethods.GetMenuItemInfo(menuHandle, i, /*fByPosition=*/ true, info);
                    
                    if (info.fType == NativeMethods.MFT_SEPARATOR){
                        // its a separator.
                    	itemToAdd = new ToolStripSeparator();
                    }
                    else {
                        // its a menu item... lets fish out the command id
                     	info = new NativeMethods.MENUITEMINFO_T_RW();
                    	info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T_RW));
                    	info.fMask = NativeMethods.MIIM_ID;
                    	info.fType = NativeMethods.MIIM_ID;
                    	UnsafeNativeMethods.GetMenuItemInfo(menuHandle, i, /*fByPosition=*/ true, info);
 
                        // create the managed object - toolstripmenu item knows how to grok hmenu for information.
                    	itemToAdd = new ToolStripMenuItem(hmenu, info.wID, targetWindow);
 
 
                    	// if there is a submenu fetch it.
                    	info = new NativeMethods.MENUITEMINFO_T_RW();
                    	info.cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T_RW));
                    	info.fMask = NativeMethods.MIIM_SUBMENU;
                    	info.fType = NativeMethods.MIIM_SUBMENU;
                    	UnsafeNativeMethods.GetMenuItemInfo(menuHandle, i, /*fByPosition=*/ true, info);
 
                    	if (info.hSubMenu != IntPtr.Zero) {
                    	    // set the dropdown to be the items from the submenu
                    		((ToolStripMenuItem)itemToAdd).DropDown = FromHMenu(info.hSubMenu, targetWindow);
                    	}
                    }
 
                    managedDropDown.Items.Add(itemToAdd);
                }
                managedDropDown.ResumeLayout();
                return managedDropDown;
            }
 
            [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // using "\t" to figure out the width of tab
            private void  CalculateInternalLayoutMetrics() {
 
               
                Size maxTextSize    = Size.Empty;
                Size maxImageSize   = Size.Empty;
                Size maxCheckSize   = scaledDefaultImageSize;
                Size maxArrowSize   = Size.Empty;
                Size maxNonMenuItemSize = Size.Empty;
            
                // determine Text Metrics
                for (int i = 0; i < Items.Count; i++)  {
                    ToolStripItem item = Items[i];
                    ToolStripMenuItem menuItem = item as ToolStripMenuItem;
 
                    if (menuItem != null) {
                        
                        Size menuItemTextSize = menuItem.GetTextSize();
 
                        if (menuItem.ShowShortcutKeys)  {
                            Size shortcutTextSize = menuItem.GetShortcutTextSize();
                            if (tabWidth == -1) {
                                tabWidth = TextRenderer.MeasureText("\t", this.Font).Width;
                            }
                            menuItemTextSize.Width += tabWidth + shortcutTextSize.Width; 
                            menuItemTextSize.Height = Math.Max(menuItemTextSize.Height, shortcutTextSize.Height);
                        }
 
                        // we truly only care about the maximum size we find. 
                        maxTextSize.Width = Math.Max(maxTextSize.Width, menuItemTextSize.Width);
                        maxTextSize.Height = Math.Max(maxTextSize.Height, menuItemTextSize.Height);
 
                        // determine Image Metrics
                        Size imageSize = Size.Empty;
                        if (menuItem.Image != null) {
                            imageSize = (menuItem.ImageScaling == ToolStripItemImageScaling.SizeToFit) ? ImageScalingSize : menuItem.Image.Size;
                        }
                            
                        maxImageSize.Width = Math.Max(maxImageSize.Width, imageSize.Width);
                        maxImageSize.Height = Math.Max(maxImageSize.Height, imageSize.Height);
 
                        if (menuItem.CheckedImage != null) {
                            Size checkedImageSize = menuItem.CheckedImage.Size;
                            maxCheckSize.Width = Math.Max(checkedImageSize.Width, maxCheckSize.Width);
                            maxCheckSize.Height = Math.Max(checkedImageSize.Height, maxCheckSize.Height);
                        }
                    }
                    else if (!(item is ToolStripSeparator)) {
                        maxNonMenuItemSize.Height = Math.Max(item.Bounds.Height, maxNonMenuItemSize.Height);
                        maxNonMenuItemSize.Width = Math.Max(item.Bounds.Width, maxNonMenuItemSize.Width);
                    }
                }
        
                this.maxItemSize.Height =  Math.Max(maxTextSize.Height + scaledTextPadding.Vertical, Math.Max(maxCheckSize.Height + scaledCheckPadding.Vertical, maxArrowSize.Height + scaledArrowPadding.Vertical));
 
                if (ShowImageMargin) {
                    // only add in the image into the calculation if we're going to render it.
                    this.maxItemSize.Height = Math.Max(maxImageSize.Height + scaledImagePadding.Vertical, maxItemSize.Height);
                }
       
                bool  useDefaultCheckMarginWidth = (ShowCheckMargin && (maxCheckSize.Width == 0));
                bool  useDefaultImageMarginWidth = (ShowImageMargin && (maxImageSize.Width == 0));
                // Always save space for an arrow
                maxArrowSize = new Size(scaledArrowSize, maxItemSize.Height);
                
                maxTextSize.Height = maxItemSize.Height -  scaledTextPadding.Vertical;                
                maxImageSize.Height = maxItemSize.Height - scaledImagePadding.Vertical;
                maxCheckSize.Height = maxItemSize.Height - scaledCheckPadding.Vertical;
 
               // fixup if there are non-menu items that are larger than our normal menu items
               maxTextSize.Width = Math.Max(maxTextSize.Width, maxNonMenuItemSize.Width);
            
               Point nextPoint = Point.Empty;
               int checkAndImageMarginWidth = 0;
 
               int extraImageWidth = Math.Max(0, maxImageSize.Width - scaledDefaultImageSize.Width);
         
               if (ShowCheckMargin && ShowImageMargin) {
                    // double column - check margin then image margin
                    // default to 46px - grow if necessary.
                    checkAndImageMarginWidth = scaledDefaultImageAndCheckMarginWidth;
              
                    // add in the extra space for the image... since the check size is locked down to 16x16.
                    checkAndImageMarginWidth += extraImageWidth;
 
                    // align the checkmark
                    nextPoint = new Point(scaledCheckPadding.Left, scaledCheckPadding.Top);
               		checkRectangle = LayoutUtils.Align(maxCheckSize, new Rectangle(nextPoint.X, nextPoint.Y, maxCheckSize.Width, maxItemSize.Height), ContentAlignment.MiddleCenter);
 
                    // align the image rectangle
                    nextPoint.X = checkRectangle.Right + scaledCheckPadding.Right + scaledImagePadding.Left;
                    nextPoint.Y = scaledImagePadding.Top;
                    imageRectangle = LayoutUtils.Align(maxImageSize, new Rectangle(nextPoint.X, nextPoint.Y, maxImageSize.Width, maxItemSize.Height), ContentAlignment.MiddleCenter);
                    
               }
               else if (ShowCheckMargin) {
                   // no images should be shown in a ShowCheckMargin only scenario.
                   // default to 24px - grow if necessary.
                   checkAndImageMarginWidth = scaledDefaultImageMarginWidth;
                   
                   // align the checkmark
                    nextPoint = new Point(1,scaledCheckPadding.Top);
               //    nextPoint = new Point(scaledCheckPadding.Left, scaledCheckPadding.Top);
                   checkRectangle = LayoutUtils.Align(maxCheckSize, new Rectangle(nextPoint.X, nextPoint.Y, checkAndImageMarginWidth, maxItemSize.Height), ContentAlignment.MiddleCenter);
                   
                   imageRectangle = Rectangle.Empty;
 
               }
               else if (ShowImageMargin) {
                   // checks and images render in the same area.
                   
                   // default to 24px - grow if necessary.
                   checkAndImageMarginWidth = scaledDefaultImageMarginWidth;
                   
                   // add in the extra space for the image... since the check size is locked down to 16x16.
                   checkAndImageMarginWidth += extraImageWidth;
 
                   // NOTE due to the Padding property, we're going to have to recalc the vertical alignment in ToolStripMenuItemInternalLayout.
                   // Dont fuss here over the Y, X is what's critical.
                   
                   // check and image rect are the same - take the max of the image size and the check size and align 
                   nextPoint = new Point(1, scaledCheckPadding.Top);
                   checkRectangle = LayoutUtils.Align(LayoutUtils.UnionSizes(maxCheckSize,maxImageSize), new Rectangle(nextPoint.X, nextPoint.Y, checkAndImageMarginWidth-1, maxItemSize.Height), ContentAlignment.MiddleCenter);
 
                   // align the image
                   imageRectangle = checkRectangle;
 
               }
               else {
                    checkAndImageMarginWidth = 0;
               }
               nextPoint.X = checkAndImageMarginWidth+1;
               
           
                // calculate space for image
                // if we didnt have a check - make sure to ignore check padding
               
                // consider: should we constrain to a reasonable width?
                //imageMarginBounds = new Rectangle(0, 0, Math.Max(imageMarginWidth,DefaultImageMarginWidth), this.Height);
                imageMarginBounds = new Rectangle(0,0,checkAndImageMarginWidth, this.Height);
                                
                // calculate space for shortcut and text
                nextPoint.X = imageMarginBounds.Right+ scaledTextPadding.Left;
                nextPoint.Y = scaledTextPadding.Top;
                textRectangle = new Rectangle(nextPoint, maxTextSize);
 
                // calculate space for arrow
                nextPoint.X = textRectangle.Right+ scaledTextPadding.Right + scaledArrowPadding.Left;
                nextPoint.Y = scaledArrowPadding.Top;
                arrowRectangle = new Rectangle(nextPoint, maxArrowSize);
           
                // calculate space required for all of these pieces
                this.maxItemSize.Width =  (arrowRectangle.Right + scaledArrowPadding.Right) - imageMarginBounds.Left;
 
                this.Padding = DefaultPadding;
                int trimPadding = imageMarginBounds.Width;
            
                if (RightToLeft == RightToLeft.Yes) {
                    // reverse the rectangle alignment in RightToLeft.Yes
                    trimPadding += scaledTextPadding.Right;
                    int width = maxItemSize.Width;
                    checkRectangle.X        = width - checkRectangle.Right;
                    imageRectangle.X        = width - imageRectangle.Right;
                    textRectangle.X         = width - textRectangle.Right;
                    arrowRectangle.X        = width - arrowRectangle.Right;
                    imageMarginBounds.X     = width - imageMarginBounds.Right;
                    
                }
                else {
                    trimPadding += scaledTextPadding.Left;
                }
 
 
                
                // VSWhidbey 339274 - we need to make sure that the text really appears vertically centered - this can be a problem in 
                // systems which force the text rectangle to be odd.
 
                // force this to be an even height.
                this.maxItemSize.Height += this.maxItemSize.Height%2;
 
                textRectangle.Y = LayoutUtils.VAlign(textRectangle.Size, new Rectangle(Point.Empty, maxItemSize), ContentAlignment.MiddleCenter).Y;
                textRectangle.Y += (textRectangle.Height %2); // if the height is odd, push down by one px, see 339274 for picture
                state[stateMaxItemSizeValid]=true;
                this.PaddingToTrim = trimPadding;
                
            }
 
            internal override void ChangeSelection(ToolStripItem nextItem) {
                if (nextItem != null) {
                    Rectangle displayRect = DisplayRectangle;
                    if (!displayRect.Contains(displayRect.X, nextItem.Bounds.Top)
                        || !displayRect.Contains(displayRect.X, nextItem.Bounds.Bottom)) {
 
                        int delta;
                        if (displayRect.Y > nextItem.Bounds.Top) {
                            delta = nextItem.Bounds.Top - displayRect.Y;
                        }
                        else {
                            delta = nextItem.Bounds.Bottom - (displayRect.Y + displayRect.Height);
                            // Now adjust so that the item at the top isn't truncated.
                            int index = this.Items.IndexOf(nextItem);
                            while (index >= 0) {
                                // we need to roll back to the index which is visible
                                if ( (this.Items[index].Visible && displayRect.Contains(displayRect.X, this.Items[index].Bounds.Top - delta))
                                    || !this.Items[index].Visible) {
                                    --index;
                                }
                                else {
                                    break;
                                }
                            }
 
                            if (index >= 0) {
                                if (displayRect.Contains(displayRect.X, this.Items[index].Bounds.Bottom - delta)) {
                                    // We found an item which is truncated at the top.
                                    delta += (this.Items[index].Bounds.Bottom - delta) - displayRect.Top;
                                }
                            }
                        }
                        this.ScrollInternal(delta);
                        this.UpdateScrollButtonStatus();
                    }
                }
                base.ChangeSelection(nextItem);
            }
 
            protected internal override ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) {
                if (text == "-") {
                    return new ToolStripSeparator();
                }
                else {
                    return new ToolStripMenuItem(text,image,onClick);
                }
            }
 
            internal override ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction, bool rtlAware) {
                  // for up/down we dont care about flipping left/right tab should still take you down.
                return GetNextItem(start, direction);
            }
 
            internal override void Initialize() {
                base.Initialize();
                this.Padding = DefaultPadding;
                FlowLayoutSettings settings = FlowLayout.CreateSettings(this);
                settings.FlowDirection = FlowDirection.TopDown;
                state[stateShowImageMargin] = true;
            }
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.OnLayout"]/*' />
            protected override void OnLayout(LayoutEventArgs e) {
                if (!this.IsDisposed)
                {
                    // We always layout as if we don't need scroll buttons.
                    // If we do, then we'll adjust the positions to match.
                    this.RequiresScrollButtons = false;
                    CalculateInternalLayoutMetrics();
                    base.OnLayout(e);
                    if (!this.RequiresScrollButtons) {
                        this.ResetScrollPosition();
                    }
                }
            }
 
            protected override void OnFontChanged(EventArgs e) {
                tabWidth = -1;
                base.OnFontChanged(e);
                
            }
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.OnPaintBackground"]/*' />
            protected override void OnPaintBackground(PaintEventArgs e) {
                base.OnPaintBackground(e);
                if (ShowCheckMargin || ShowImageMargin) {
                    Renderer.DrawImageMargin(new ToolStripRenderEventArgs(e.Graphics, this, this.ImageMargin, SystemColors.Control));
                }
            }
 
            internal override void ResetScaling(int newDpi) {
                base.ResetScaling(newDpi);
                CommonProperties.xClearPreferredSizeCache(this);
                scaledDefaultImageSize = DpiHelper.LogicalToDeviceUnits(DefaultImageSize, newDpi);
                scaledDefaultImageMarginWidth = DpiHelper.LogicalToDeviceUnits(DefaultImageMarginWidth, newDpi) + 1; // 1px for border
                scaledDefaultImageAndCheckMarginWidth = DpiHelper.LogicalToDeviceUnits(DefaultImageAndCheckMarginWidth, newDpi) + 1; // 1px for border
 
                scaledImagePadding = DpiHelper.LogicalToDeviceUnits(ImagePadding, newDpi);
                scaledTextPadding = DpiHelper.LogicalToDeviceUnits(TextPadding, newDpi);
                scaledCheckPadding = DpiHelper.LogicalToDeviceUnits(CheckPadding, newDpi);
                scaledArrowPadding = DpiHelper.LogicalToDeviceUnits(ArrowPadding, newDpi);
                scaledArrowSize = DpiHelper.LogicalToDeviceUnits(ArrowSize, newDpi);
            }
 
            internal override bool RequiresScrollButtons {
                get {
                    return GetToolStripState(STATE_SCROLLBUTTONS);
                }
                set {
                    bool changed = (RequiresScrollButtons != value);
                    SetToolStripState(STATE_SCROLLBUTTONS, value);
                    if (changed) {
                        UpdateScrollButtonLocations();
                        if (this.Items.Count > 0) {
                            int delta = this.Items[0].Bounds.Top - this.DisplayRectangle.Top;
                            this.ScrollInternal(delta);
                            this.scrollAmount -= delta;
                            if (value) {
                                RestoreScrollPosition();
                            }
                        }
                        else {
                            this.scrollAmount = 0;
                        }
                    }
                }
            }
 
            internal void ResetScrollPosition() {
                this.scrollAmount = 0;
            }
 
            internal void RestoreScrollPosition() {
                if (!RequiresScrollButtons || this.Items.Count == 0) {
                    return;
                }
 
                // We don't just scroll by the amount, because that might 
                // cause the bottom of the menu to be blank if some items have
                // been removed/hidden since the last time we were displayed.
                // This also deals with items of different height, so that we don't truncate
                // and items under the top scrollbar.
 
                Rectangle displayRectangle = this.DisplayRectangle;
                int alreadyScrolled = displayRectangle.Top - this.Items[0].Bounds.Top;
 
                int requiredScrollAmount = this.scrollAmount - alreadyScrolled;
 
                int deltaToScroll = 0;
                if (requiredScrollAmount > 0) {
                    for (int i = 0; i < this.Items.Count && deltaToScroll < requiredScrollAmount; ++i) {
                        if (this.Items[i].Available) {
                            Rectangle adjustedLastItemBounds = this.Items[this.Items.Count - 1].Bounds;
                            adjustedLastItemBounds.Y -= deltaToScroll;
                            if (displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Top)
                                && displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Bottom)) {
                                // Scrolling this amount would make the last item visible, so don't scroll any more.
                                break;
                            }
 
                            // We use a delta between the tops, since it takes margin's and padding into account.
                            if (i < this.Items.Count - 1) {
                                deltaToScroll += this.Items[i + 1].Bounds.Top - this.Items[i].Bounds.Top;
                            }
                            else {
                                deltaToScroll += this.Items[i].Bounds.Height;
                            }
                        }
                    }
                }
                else {
                    for (int i = this.Items.Count - 1; i >= 0 && deltaToScroll > requiredScrollAmount; --i) {
                        if (this.Items[i].Available) {
                            Rectangle adjustedLastItemBounds = this.Items[0].Bounds;
                            adjustedLastItemBounds.Y -= deltaToScroll;
                            if (displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Top)
                                && displayRectangle.Contains(displayRectangle.X, adjustedLastItemBounds.Bottom)) {
                                // Scrolling this amount would make the last item visible, so don't scroll any more.
                                break;
                            }
 
                            // We use a delta between the tops, since it takes margin's and padding into account.
                            if (i > 0) {
                                deltaToScroll -= this.Items[i].Bounds.Top - this.Items[i - 1].Bounds.Top;
                            }
                            else {
                                deltaToScroll -= this.Items[i].Bounds.Height;
                            }
                        }
                    }
                }
                this.ScrollInternal(deltaToScroll);
                this.scrollAmount = this.DisplayRectangle.Top - this.Items[0].Bounds.Top;
                UpdateScrollButtonLocations();
            }
 
 
            internal override void ScrollInternal(int delta) {
                base.ScrollInternal(delta);
                this.scrollAmount += delta;
            }
 
            internal void ScrollInternal(bool up) {
                UpdateScrollButtonStatus();
 
                // calling this to get ScrollWindowEx.  In actuality it does nothing
                // to change the display rect!
                int delta;
                if (this.indexOfFirstDisplayedItem == -1 || this.indexOfFirstDisplayedItem >= this.Items.Count) {
                    Debug.Fail("Why wasn't 'UpdateScrollButtonStatus called'? We don't have the item to scroll by");
                    int menuHeight = SystemInformation.MenuHeight;
 
                    delta = up ? -menuHeight : menuHeight;
                }
                else {
                    if (up) {
                        if (this.indexOfFirstDisplayedItem == 0) {
                            Debug.Fail("We're trying to scroll up, but the top item is displayed!!!");
                            delta = 0;
                        }
                        else {
                            ToolStripItem itemTop = this.Items[this.indexOfFirstDisplayedItem - 1];
                            ToolStripItem itemBottom = this.Items[this.indexOfFirstDisplayedItem];
                            // We use a delta between the tops, since it takes margin's and padding into account.
                            delta = itemTop.Bounds.Top - itemBottom.Bounds.Top;
                        }
                    }
                    else {
                        if (this.indexOfFirstDisplayedItem == this.Items.Count - 1) {
                            Debug.Fail("We're trying to scroll down, but the top item is displayed!!!");
                            delta = 0;
                        }
 
                        ToolStripItem itemTop = this.Items[this.indexOfFirstDisplayedItem];
                        ToolStripItem itemBottom = this.Items[this.indexOfFirstDisplayedItem + 1];
                        // We use a delta between the tops, since it takes margin's and padding into account.
                        delta = itemBottom.Bounds.Top - itemTop.Bounds.Top;
                    }
                }
                ScrollInternal(delta);
                UpdateScrollButtonLocations();
            }
 
            /// <include file='doc\ToolStripDropDownMenu.uex' path='docs/doc[@for="ToolStripDropDownMenu.SetDisplayedItems"]/*' />
            protected override void SetDisplayedItems() {
 
                base.SetDisplayedItems();
                if (RequiresScrollButtons) {
 
                    this.DisplayedItems.Add(UpScrollButton);
                    this.DisplayedItems.Add(DownScrollButton);
                    UpdateScrollButtonLocations();
                    UpScrollButton.Visible = true;
                    DownScrollButton.Visible = true;
                }
                else {
                    UpScrollButton.Visible = false;
                    DownScrollButton.Visible = false;
                }
            }
 
            private void UpdateScrollButtonLocations() {
 
                if (GetToolStripState(STATE_SCROLLBUTTONS)) {
                    Size upSize = UpScrollButton.GetPreferredSize(Size.Empty);
                    // 
                    Point upLocation = new Point(1, 0);
 
                    UpScrollButton.SetBounds(new Rectangle(upLocation, upSize));
 
                    Size downSize = DownScrollButton.GetPreferredSize(Size.Empty);
                    int height = GetDropDownBounds(this.Bounds).Height;
 
                    Point downLocation = new Point(1, height - downSize.Height);
                    DownScrollButton.SetBounds(new Rectangle(downLocation, downSize));
                    UpdateScrollButtonStatus();
                }
 
            }
 
            private void UpdateScrollButtonStatus() {
                Rectangle displayRectangle = this.DisplayRectangle;
 
                this.indexOfFirstDisplayedItem = -1;
                int minY = int.MaxValue, maxY = 0;
 
                for (int i = 0; i < this.Items.Count; ++i) {
                    ToolStripItem item = this.Items[i];
                    if (UpScrollButton == item) {
                        continue;
                    }
                    if (DownScrollButton == item) {
                        continue;
                    }
 
                    if (!item.Available) {
                        continue;
                    }
 
                    if (this.indexOfFirstDisplayedItem == -1 && displayRectangle.Contains(displayRectangle.X, item.Bounds.Top)) {
                        this.indexOfFirstDisplayedItem = i;
                    }
 
                    minY = Math.Min(minY, item.Bounds.Top);
                    maxY = Math.Max(maxY, item.Bounds.Bottom);
                }
 
                UpScrollButton.Enabled = !displayRectangle.Contains(displayRectangle.X, minY);
                DownScrollButton.Enabled = !displayRectangle.Contains(displayRectangle.X, maxY);
            }
 
            internal sealed class ToolStripDropDownLayoutEngine : FlowLayout {
 
                public static ToolStripDropDownLayoutEngine LayoutInstance = new ToolStripDropDownLayoutEngine();
                
                internal override Size GetPreferredSize(IArrangedElement container, Size proposedConstraints) {
                        Size preferredSize = base.GetPreferredSize(container, proposedConstraints);
                        ToolStripDropDownMenu dropDownMenu = container as ToolStripDropDownMenu;
                        if (dropDownMenu != null) {
                            preferredSize.Width = dropDownMenu.MaxItemSize.Width - dropDownMenu.PaddingToTrim;
                        }
                        return preferredSize;
                }
 
           }
       }
    }