|
//------------------------------------------------------------------------------
// <copyright file="ToolStrip.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Threading;
using System.Windows.Forms.Layout;
using System.ComponentModel.Design.Serialization;
using System.Drawing.Drawing2D;
using System.Text.RegularExpressions;
using System.Text;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Windows.Forms.Internal;
using Microsoft.Win32;
using System.Runtime.Versioning;
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip"]/*' />
/// <devdoc>
/// Summary of ToolStrip.
/// </devdoc>
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[DesignerSerializer("System.Windows.Forms.Design.ToolStripCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)]
[Designer("System.Windows.Forms.Design.ToolStripDesigner, " + AssemblyRef.SystemDesign)]
[DefaultProperty("Items")]
[SRDescription(SR.DescriptionToolStrip)]
[DefaultEvent("ItemClicked")]
public class ToolStrip : System.Windows.Forms.ScrollableControl,
IArrangedElement,
ISupportToolStripPanel
{
private static Size onePixel = new Size(1,1);
internal static Point InvalidMouseEnter = new Point(Int32.MaxValue, Int32.MaxValue);
private ToolStripItemCollection toolStripItemCollection = null;
private ToolStripOverflowButton toolStripOverflowButton = null;
private ToolStripGrip toolStripGrip = null;
private ToolStripItemCollection displayedItems = null;
private ToolStripItemCollection overflowItems = null;
private ToolStripDropTargetManager dropTargetManager = null;
private IntPtr hwndThatLostFocus = IntPtr.Zero;
private ToolStripItem lastMouseActiveItem = null;
private ToolStripItem lastMouseDownedItem = null;
private LayoutEngine layoutEngine = null;
private ToolStripLayoutStyle layoutStyle = ToolStripLayoutStyle.StackWithOverflow;
private LayoutSettings layoutSettings = null;
private Rectangle lastInsertionMarkRect = Rectangle.Empty;
private ImageList imageList = null;
private ToolStripGripStyle toolStripGripStyle = ToolStripGripStyle.Visible;
private ISupportOleDropSource itemReorderDropSource = null;
private IDropTarget itemReorderDropTarget = null;
private int toolStripState = 0;
private bool showItemToolTips = false;
private MouseHoverTimer mouseHoverTimer = null;
private ToolStripItem currentlyActiveTooltipItem;
private NativeWindow dropDownOwnerWindow;
private byte mouseDownID = 0; // NEVER use this directly from another class, 0 should never be returned to another class.
private Orientation orientation = Orientation.Horizontal;
private ArrayList activeDropDowns = new ArrayList(1);
private ToolStripRenderer renderer = null;
private Type currentRendererType = typeof(System.Type);
private Hashtable shortcuts = null;
private Stack<MergeHistory> mergeHistoryStack = null;
private ToolStripDropDownDirection toolStripDropDownDirection = ToolStripDropDownDirection.Default;
private Size largestDisplayedItemSize = Size.Empty;
private CachedItemHdcInfo cachedItemHdcInfo = null;
private bool alreadyHooked = false;
private Size imageScalingSize;
private const int ICON_DIMENSION = 16;
private static int iconWidth = ICON_DIMENSION;
private static int iconHeight = ICON_DIMENSION;
private Font defaultFont = null;
private RestoreFocusMessageFilter restoreFocusFilter;
private bool layoutRequired = false;
private static readonly Padding defaultPadding = new Padding(0, 0, 1, 0);
private static readonly Padding defaultGripMargin = new Padding(2);
private Padding scaledDefaultPadding = defaultPadding;
private Padding scaledDefaultGripMargin = defaultGripMargin;
private Point mouseEnterWhenShown = InvalidMouseEnter;
private const int INSERTION_BEAM_WIDTH = 6;
internal static int insertionBeamWidth = INSERTION_BEAM_WIDTH;
private static readonly object EventPaintGrip = new object();
private static readonly object EventLayoutCompleted = new object();
private static readonly object EventItemAdded = new object();
private static readonly object EventItemRemoved = new object();
private static readonly object EventLayoutStyleChanged = new object();
private static readonly object EventRendererChanged = new object();
private static readonly object EventItemClicked = new object();
private static readonly object EventLocationChanging = new object();
private static readonly object EventBeginDrag = new object();
private static readonly object EventEndDrag = new object();
private static readonly int PropBindingContext = PropertyStore.CreateKey();
private static readonly int PropTextDirection = PropertyStore.CreateKey();
private static readonly int PropToolTip = PropertyStore.CreateKey();
private static readonly int PropToolStripPanelCell = PropertyStore.CreateKey();
internal const int STATE_CANOVERFLOW = 0x00000001;
internal const int STATE_ALLOWITEMREORDER = 0x00000002;
internal const int STATE_DISPOSINGITEMS = 0x00000004;
internal const int STATE_MENUAUTOEXPAND = 0x00000008;
internal const int STATE_MENUAUTOEXPANDDEFAULT = 0x00000010;
internal const int STATE_SCROLLBUTTONS = 0x00000020;
internal const int STATE_USEDEFAULTRENDERER = 0x00000040;
internal const int STATE_ALLOWMERGE = 0x00000080;
internal const int STATE_RAFTING = 0x00000100;
internal const int STATE_STRETCH = 0x00000200;
internal const int STATE_LOCATIONCHANGING = 0x00000400;
internal const int STATE_DRAGGING = 0x00000800;
internal const int STATE_HASVISIBLEITEMS = 0x00001000;
internal const int STATE_SUSPENDCAPTURE = 0x00002000;
internal const int STATE_LASTMOUSEDOWNEDITEMCAPTURE = 0x00004000;
internal const int STATE_MENUACTIVE = 0x00008000;
#if DEBUG
internal static readonly TraceSwitch SelectionDebug = new TraceSwitch("SelectionDebug", "Debug ToolStrip Selection code");
internal static readonly TraceSwitch DropTargetDebug = new TraceSwitch("DropTargetDebug", "Debug ToolStrip Drop code");
internal static readonly TraceSwitch LayoutDebugSwitch = new TraceSwitch("Layout debug", "Debug ToolStrip layout code");
internal static readonly TraceSwitch MouseActivateDebug = new TraceSwitch("ToolStripMouseActivate", "Debug ToolStrip WM_MOUSEACTIVATE code");
internal static readonly TraceSwitch MergeDebug = new TraceSwitch("ToolStripMergeDebug", "Debug toolstrip merging");
internal static readonly TraceSwitch SnapFocusDebug = new TraceSwitch("SnapFocus", "Debug snapping/restoration of focus");
internal static readonly TraceSwitch FlickerDebug = new TraceSwitch("FlickerDebug", "Debug excessive calls to Invalidate()");
internal static readonly TraceSwitch ItemReorderDebug = new TraceSwitch("ItemReorderDebug", "Debug excessive calls to Invalidate()");
internal static readonly TraceSwitch MDIMergeDebug = new TraceSwitch("MDIMergeDebug", "Debug toolstrip MDI merging");
internal static readonly TraceSwitch MenuAutoExpandDebug = new TraceSwitch("MenuAutoExpand", "Debug menu auto expand");
internal static readonly TraceSwitch ControlTabDebug = new TraceSwitch("ControlTab", "Debug ToolStrip Control+Tab selection");
#else
internal static readonly TraceSwitch SelectionDebug;
internal static readonly TraceSwitch DropTargetDebug;
internal static readonly TraceSwitch LayoutDebugSwitch;
internal static readonly TraceSwitch MouseActivateDebug;
internal static readonly TraceSwitch MergeDebug;
internal static readonly TraceSwitch SnapFocusDebug;
internal static readonly TraceSwitch FlickerDebug;
internal static readonly TraceSwitch ItemReorderDebug;
internal static readonly TraceSwitch MDIMergeDebug;
internal static readonly TraceSwitch MenuAutoExpandDebug;
internal static readonly TraceSwitch ControlTabDebug;
#endif
private delegate void BooleanMethodInvoker(bool arg);
internal Action<int, int> rescaleConstsCallbackDelegate;
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ToolStrip"]/*' />
/// <devdoc>
/// Summary of ToolStrip.
/// </devdoc>
public ToolStrip() {
if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) {
ToolStripManager.CurrentDpi = DeviceDpi;
defaultFont = ToolStripManager.DefaultFont;
iconWidth = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, DeviceDpi);
iconHeight = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, DeviceDpi);
insertionBeamWidth = DpiHelper.LogicalToDeviceUnits(INSERTION_BEAM_WIDTH, DeviceDpi);
scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, DeviceDpi);
scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin, DeviceDpi);
}
else if (DpiHelper.IsScalingRequired) {
iconWidth = DpiHelper.LogicalToDeviceUnitsX(ICON_DIMENSION);
iconHeight = DpiHelper.LogicalToDeviceUnitsY(ICON_DIMENSION);
if (DpiHelper.EnableToolStripHighDpiImprovements) {
insertionBeamWidth = DpiHelper.LogicalToDeviceUnitsX(INSERTION_BEAM_WIDTH);
scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding);
scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin);
}
}
imageScalingSize = new Size(iconWidth, iconHeight);
SuspendLayout();
this.CanOverflow = true;
this.TabStop = false;
this.MenuAutoExpand = false;
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Selectable, false);
SetToolStripState(STATE_USEDEFAULTRENDERER | STATE_ALLOWMERGE, true);
SetState2(STATE2_MAINTAINSOWNCAPTUREMODE // VSWhidbey 458967: a toolstrip does not take capture on MouseDown.
| STATE2_USEPREFERREDSIZECACHE, // this class overrides GetPreferredSizeCore, let Control automatically cache the result
true);
//add a weak ref link in ToolstripManager
ToolStripManager.ToolStrips.Add(this);
layoutEngine = new ToolStripSplitStackLayout(this);
this.Dock = DefaultDock;
this.AutoSize = true;
this.CausesValidation = false;
Size defaultSize = DefaultSize;
SetAutoSizeMode(AutoSizeMode.GrowAndShrink);
this.ShowItemToolTips = DefaultShowItemToolTips;
ResumeLayout(true);
}
public ToolStrip(params ToolStripItem[] items) : this() {
Items.AddRange(items);
}
internal ArrayList ActiveDropDowns {
get { return activeDropDowns; }
}
// returns true when entered into menu mode through this toolstrip/menustrip
// this is only really supported for menustrip active event, but to prevent casting everywhere...
internal virtual bool KeyboardActive {
get { return GetToolStripState(STATE_MENUACTIVE); }
set { SetToolStripState(STATE_MENUACTIVE, value);}
}
// This is only for use in determining whether to show scroll bars on
// ToolStripDropDownMenus. No one else should be using it for anything.
internal virtual bool AllItemsVisible {
get {
return true;
}
set {
// we do nothing in repsonse to a set, since we calculate the value above.
}
}
[DefaultValue(true), Browsable(true), EditorBrowsable(EditorBrowsableState.Always),
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
]
public override bool AutoSize {
get {
return base.AutoSize;
}
set {
if (IsInToolStripPanel && base.AutoSize && !value) {
// VSWhidbey 351717 - restoring the bounds can change the location of the toolstrip -
// which would join it to a new row. Set the specified bounds to the new location to
// prevent this.
Rectangle bounds = CommonProperties.GetSpecifiedBounds(this);
bounds.Location = this.Location;
CommonProperties.UpdateSpecifiedBounds(this, bounds.X, bounds.Y, bounds.Width, bounds.Height, BoundsSpecified.Location);
}
base.AutoSize = value;
}
}
/// <include file='doc\GroupBox.uex' path='docs/doc[@for="GroupBox.AutoSizeChanged"]/*' />
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)]
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
new public event EventHandler AutoSizeChanged
{
add
{
base.AutoSizeChanged += value;
}
remove
{
base.AutoSizeChanged -= value;
}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public override bool AutoScroll {
get {
return base.AutoScroll;
}
set {
throw new NotSupportedException(SR.GetString(SR.ToolStripDoesntSupportAutoScroll));
}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public new Size AutoScrollMargin {
get {
return base.AutoScrollMargin;
}
set {
base.AutoScrollMargin = value;
}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public new Size AutoScrollMinSize {
get {
return base.AutoScrollMinSize;
}
set {
base.AutoScrollMinSize = value;
}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public new Point AutoScrollPosition {
get {
return base.AutoScrollPosition;
}
set {
base.AutoScrollPosition = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.AllowDrop"]/*' />
/// <devdoc>
/// Summary of AllowDrop.
/// </devdoc>
public override bool AllowDrop {
get {
return base.AllowDrop;
}
set {
if (value && AllowItemReorder) {
throw new ArgumentException(SR.GetString(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue));
}
base.AllowDrop = value;
// SECREVIEW: If we toggle between AllowDrop and AllowItemReorder
// make sure that we're demanding the Clipboard permission in
// ToolStripDropTargetManager.SetAcceptDrops
if (value) {
this.DropTargetManager.EnsureRegistered(this);
}
else {
this.DropTargetManager.EnsureUnRegistered(this);
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.AllowItemReorder"]/*' />
/// <devdoc>
///
/// </devdoc>
[
DefaultValue(false),
SRDescription(SR.ToolStripAllowItemReorderDescr),
SRCategory(SR.CatBehavior)
]
public bool AllowItemReorder {
get { return GetToolStripState(STATE_ALLOWITEMREORDER); }
set {
if (GetToolStripState(STATE_ALLOWITEMREORDER) != value) {
if (AllowDrop && value) {
throw new ArgumentException(SR.GetString(SR.ToolStripAllowItemReorderAndAllowDropCannotBeSetToTrue));
}
SetToolStripState(STATE_ALLOWITEMREORDER, value);
// SECREVIEW: If we toggle between AllowDrop and AllowItemReorder
// make sure that we're demanding the Clipboard permission in
// ToolStripDropTargetManager.SetAcceptDrops
if (value) {
ToolStripSplitStackDragDropHandler dragDropHandler = new ToolStripSplitStackDragDropHandler(this);
this.ItemReorderDropSource = dragDropHandler;
this.ItemReorderDropTarget = dragDropHandler;
this.DropTargetManager.EnsureRegistered(this);
}
else {
this.DropTargetManager.EnsureUnRegistered(this);
}
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.AllowItemReorder"]/*' />
/// <devdoc>
///
/// </devdoc>
[
DefaultValue(true),
SRDescription(SR.ToolStripAllowMergeDescr),
SRCategory(SR.CatBehavior)
]
public bool AllowMerge {
get { return GetToolStripState(STATE_ALLOWMERGE); }
set {
if (GetToolStripState(STATE_ALLOWMERGE) != value) {
SetToolStripState(STATE_ALLOWMERGE, value);
}
}
}
public override AnchorStyles Anchor {
get {
return base.Anchor;
}
set {
// the base calls SetDock, which causes an OnDockChanged to be called
// which forces two layouts of the parent.
using (new LayoutTransaction(this, this, PropertyNames.Anchor)) {
base.Anchor = value;
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.BackColor"]/*' />
/// <internalonly/>
/// <devdoc>
/// Just here so we can implement ShouldSerializeBackColor
/// </devdoc>
[
SRDescription(SR.ToolStripBackColorDescr),
SRCategory(SR.CatAppearance)
]
public new Color BackColor {
get {
return base.BackColor;
}
set {
base.BackColor = value;
}
}
[SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripOnBeginDrag)]
public event EventHandler BeginDrag {
add {
Events.AddHandler(EventBeginDrag, value);
}
remove {
Events.RemoveHandler(EventBeginDrag, value);
}
}
/// <include file='doc\SplitContainer.uex' path='docs/doc[@for="ToolStrip.BindingContext"]/*' />
public override BindingContext BindingContext {
get {
BindingContext bc = (BindingContext) this.Properties.GetObject(PropBindingContext);
if (bc != null)
return bc;
// try the parent
//
Control p = ParentInternal;
if (p != null && p.CanAccessProperties)
return p.BindingContext;
// we don't have a binding context
return null;
}
set {
if (this.Properties.GetObject(PropBindingContext) != value) {
this.Properties.SetObject(PropBindingContext, value);
// re-wire the bindings
OnBindingContextChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.CanOverflow"]/*' />
/// <devdoc>
/// Summary of CanOverflow.
/// </devdoc>
[
DefaultValue(true),
SRDescription(SR.ToolStripCanOverflowDescr),
SRCategory(SR.CatLayout)
]
public bool CanOverflow {
get {
return GetToolStripState(STATE_CANOVERFLOW);
}
set {
if (GetToolStripState(STATE_CANOVERFLOW) != value) {
SetToolStripState(STATE_CANOVERFLOW, value);
InvalidateLayout();
}
}
}
///<devdoc> we can only shift selection when we're not focused (someone mousing over us)
/// or we are focused and one of our toolstripcontrolhosts do not have focus.
/// SCENARIO: put focus in combo box, move the mouse over another item... selectioni
/// should not shift until the combobox relinquishes its focus.
///</devdoc>
internal bool CanHotTrack {
get {
if (!Focused) {
// if ContainsFocus in one of the children = false, someone is just mousing by, we can hot track
return (ContainsFocus == false);
}
else {
// if the toolstrip itself contains focus we can definately hottrack.
return true;
}
}
}
[
Browsable(false),
DefaultValue(false),
]
public new bool CausesValidation {
get {
// By default: CausesValidation is false for a ToolStrip
// we want people to be able to use menus without validating
// their controls.
return base.CausesValidation;
}
set {
base.CausesValidation = value;
}
}
[Browsable(false)]
public new event EventHandler CausesValidationChanged {
add {
base.CausesValidationChanged += value;
}
remove {
base.CausesValidationChanged -= value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.Controls"]/*' />
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Control.ControlCollection Controls {
get { return base.Controls; }
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ControlAdded"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event ControlEventHandler ControlAdded {
add {
base.ControlAdded += value;
}
remove {
base.ControlAdded -= value;
}
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Cursor Cursor {
get { return base.Cursor; }
set { base.Cursor = value; }
}
/// <devdoc>
/// <para>Hide browsable property</para>
/// </devdoc>
[Browsable(false)]
public new event EventHandler CursorChanged {
add {
base.CursorChanged += value;
}
remove {
base.CursorChanged -= value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ControlRemoved"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event ControlEventHandler ControlRemoved {
add {
base.ControlRemoved += value;
}
remove {
base.ControlRemoved -= value;
}
}
[SRCategory(SR.CatBehavior), SRDescription(SR.ToolStripOnEndDrag)]
public event EventHandler EndDrag {
add {
Events.AddHandler(EventEndDrag, value);
}
remove {
Events.RemoveHandler(EventEndDrag, value);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.Font"]/*' />
public override Font Font {
get {
if (this.IsFontSet()) {
return base.Font;
}
if (defaultFont == null) {
// since toolstrip manager default font is thread static, hold onto a copy of the
// pointer in an instance variable for perf so we dont have to keep fishing into
// thread local storage for it.
defaultFont = ToolStripManager.DefaultFont;
}
return defaultFont;
}
set {
base.Font = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.DefaultSize"]/*' />
/// <devdoc>
/// Deriving classes can override this to configure a default size for their control.
/// This is more efficient than setting the size in the control's constructor.
/// </devdoc>
protected override Size DefaultSize {
get {
return DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements ?
DpiHelper.LogicalToDeviceUnits(new Size(100, 25), DeviceDpi) :
new Size(100, 25);
}
}
protected override Padding DefaultPadding {
get {
// one pixel from the right edge to prevent the right border from painting over the
// aligned-right toolstrip item.
return scaledDefaultPadding;
}
}
protected override Padding DefaultMargin {
get { return Padding.Empty; }
}
protected virtual DockStyle DefaultDock {
get {
return DockStyle.Top;
}
}
protected virtual Padding DefaultGripMargin {
get {
if (toolStripGrip != null) {
return toolStripGrip.DefaultMargin;
}
else {
return scaledDefaultGripMargin;
}
}
}
protected virtual bool DefaultShowItemToolTips {
get {
return true;
}
}
[Browsable(false)]
[SRDescription(SR.ToolStripDefaultDropDownDirectionDescr)]
[SRCategory(SR.CatBehavior)]
public virtual ToolStripDropDownDirection DefaultDropDownDirection {
get {
ToolStripDropDownDirection direction = toolStripDropDownDirection;
if (direction == ToolStripDropDownDirection.Default) {
if (Orientation == Orientation.Vertical) {
if (IsInToolStripPanel) {
// parent can be null when we're swapping between ToolStripPanels.
DockStyle actualDock = (ParentInternal != null) ? ParentInternal.Dock : DockStyle.Left;
direction = (actualDock == DockStyle.Right) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right;
if (DesignMode && actualDock == DockStyle.Left)
{
direction = ToolStripDropDownDirection.Right ;
}
}
else {
direction = ((Dock == DockStyle.Right) && (RightToLeft == RightToLeft.No)) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right;
if (DesignMode && Dock == DockStyle.Left)
{
direction = ToolStripDropDownDirection.Right ;
}
}
}
else { // horizontal
DockStyle dock = this.Dock;
if (IsInToolStripPanel && ParentInternal != null) {
dock = ParentInternal.Dock; // we want the orientation of the ToolStripPanel;
}
if (dock == DockStyle.Bottom) {
direction = (RightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.AboveLeft : ToolStripDropDownDirection.AboveRight;
}
else {
// assume Dock.Top
direction = (RightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.BelowLeft : ToolStripDropDownDirection.BelowRight;
}
}
}
return direction;
}
set {
// cant use Enum.IsValid as its not sequential
switch (value) {
case ToolStripDropDownDirection.AboveLeft:
case ToolStripDropDownDirection.AboveRight:
case ToolStripDropDownDirection.BelowLeft:
case ToolStripDropDownDirection.BelowRight:
case ToolStripDropDownDirection.Left:
case ToolStripDropDownDirection.Right:
case ToolStripDropDownDirection.Default:
break;
default:
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripDropDownDirection));
}
toolStripDropDownDirection = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.Dock"]/*' />
/// <internalonly/>
/// <devdoc>
/// Just here so we can add the default value attribute
/// </devdoc>
[DefaultValue(DockStyle.Top)]
public override DockStyle Dock {
get {
return base.Dock;
}
set {
if (value != Dock) {
using (new LayoutTransaction(this, this, PropertyNames.Dock))
using (new LayoutTransaction(this.ParentInternal, this, PropertyNames.Dock)) {
// We don't call base.Dock = value, because that would cause us to get 2 LocationChanged events.
// The first is when the parent gets a Layout due to the DockChange, and the second comes from when we
// change the orientation. Instead we've duplicated the logic of Control.Dock.set here, but with a
// LayoutTransaction on the Parent as well.
// See VSWhidbey:489688 and VSWhidbey:474781 for more details.
DefaultLayout.SetDock(this, value);
UpdateLayoutStyle(Dock);
}
// This will cause the DockChanged event to fire.
OnDockChanged(EventArgs.Empty);
}
}
}
/// <devdoc>
/// Returns an owner window that can be used to
/// own a drop down.
/// </devdoc>
internal virtual NativeWindow DropDownOwnerWindow {
get {
if (dropDownOwnerWindow == null) {
dropDownOwnerWindow = new NativeWindow();
}
if (dropDownOwnerWindow.Handle == IntPtr.Zero) {
CreateParams cp = new CreateParams();
cp.ExStyle = NativeMethods.WS_EX_TOOLWINDOW;
dropDownOwnerWindow.CreateHandle(cp);
}
return dropDownOwnerWindow;
}
}
/// <devdoc>
/// Returns the drop target manager that all the hwndless
/// items and this winbar share. this is necessary as
/// RegisterDragDrop requires an HWND.
/// </devdoc>
internal ToolStripDropTargetManager DropTargetManager {
get {
if (dropTargetManager == null) {
dropTargetManager = new ToolStripDropTargetManager(this);
}
return dropTargetManager;
}
set {
dropTargetManager = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.DisplayedItems"]/*' />
/// <internalonly/>
/// <devdoc>
/// Just here so we can add the default value attribute
/// </devdoc>
protected internal virtual ToolStripItemCollection DisplayedItems {
get {
if (displayedItems == null) {
displayedItems = new ToolStripItemCollection(this, false);
}
return displayedItems;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.DisplayRectangle"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// Retreives the current display rectangle. The display rectangle
/// is the virtual display area that is used to layout components.
/// The position and dimensions of the Form's display rectangle
/// change during autoScroll.
/// </para>
/// </devdoc>
public override Rectangle DisplayRectangle {
[SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces")]
get {
Rectangle rect = base.DisplayRectangle;
if ((LayoutEngine is ToolStripSplitStackLayout) && (GripStyle == ToolStripGripStyle.Visible)){
if (Orientation == Orientation.Horizontal) {
int gripwidth = Grip.GripThickness + Grip.Margin.Horizontal;
rect.Width -= gripwidth;
// in RTL.No we need to shift the rectangle
rect.X += (RightToLeft == RightToLeft.No) ? gripwidth : 0;
}
else { // Vertical Grip placement
int gripheight = Grip.GripThickness + Grip.Margin.Vertical;
rect.Y += gripheight;
rect.Height -= gripheight;
}
}
return rect;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ForeColor"]/*' />
/// <internalonly/>
/// <devdoc>
/// Forecolor really has no meaning for winbars - so lets hide it
/// </devdoc>
[Browsable(false)]
public new Color ForeColor {
get {
return base.ForeColor;
}
set {
base.ForeColor = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ForeColorChanged"]/*' />
/// <devdoc>
/// <para>[ToolStrip ForeColorChanged event, overriden to turn browsing off.]</para>
/// </devdoc>
[
Browsable(false)
]
public new event EventHandler ForeColorChanged
{
add
{
base.ForeColorChanged += value;
}
remove
{
base.ForeColorChanged -= value;
}
}
private bool HasKeyboardInput {
get {
return (ContainsFocus || (ToolStripManager.ModalMenuFilter.InMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this));
}
}
/// <devdoc>
/// Summary of ToolStripGrip.
/// </devdoc>
/// <internalonly/>
internal ToolStripGrip Grip {
get {
if (toolStripGrip == null) {
toolStripGrip = new ToolStripGrip();
toolStripGrip.Overflow = ToolStripItemOverflow.Never;
toolStripGrip.Visible = toolStripGripStyle ==ToolStripGripStyle.Visible;
toolStripGrip.AutoSize = false;
toolStripGrip.ParentInternal = this;
toolStripGrip.Margin = DefaultGripMargin;
}
return toolStripGrip;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.GripStyle"]/*' />
/// <devdoc>
/// Summary of GripStyle.
/// </devdoc>
[
SRCategory(SR.CatAppearance),
SRDescription(SR.ToolStripGripStyleDescr),
DefaultValue(ToolStripGripStyle.Visible)
]
public ToolStripGripStyle GripStyle {
get {
return toolStripGripStyle;
}
set {
//valid values are 0x0 to 0x1
if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripGripStyle.Hidden, (int)ToolStripGripStyle.Visible)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripGripStyle));
}
if (toolStripGripStyle != value) {
toolStripGripStyle = value;
Grip.Visible = toolStripGripStyle ==ToolStripGripStyle.Visible;
LayoutTransaction.DoLayout(this, this, PropertyNames.GripStyle);
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.GripDisplayStyle"]/*' />
/// <devdoc>
/// Summary of GripStyle.
/// </devdoc>
[
Browsable(false)
]
public ToolStripGripDisplayStyle GripDisplayStyle {
get {
return (LayoutStyle == ToolStripLayoutStyle.HorizontalStackWithOverflow) ? ToolStripGripDisplayStyle.Vertical
: ToolStripGripDisplayStyle.Horizontal;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.GripMargin"]/*' />
/// <devdoc>
/// The external spacing between the grip and the padding of the winbar and the first item in the collection
/// </devdoc>
[
SRCategory(SR.CatLayout),
SRDescription(SR.ToolStripGripDisplayStyleDescr)
]
public Padding GripMargin {
get {
return Grip.Margin;
}
set {
Grip.Margin = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.GripRectangle"]/*' />
/// <devdoc>
/// The boundaries of the grip on the winbar. If it is invisible - returns Rectangle.Empty.
/// </devdoc>
[
Browsable(false)
]
public Rectangle GripRectangle {
get {
return (GripStyle == ToolStripGripStyle.Visible) ? Grip.Bounds : Rectangle.Empty;
}
}
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public new bool HasChildren {
get {
return base.HasChildren;
}
}
internal bool HasVisibleItems {
get {
if (!IsHandleCreated) {
foreach(ToolStripItem item in Items) {
if (((IArrangedElement)item).ParticipatesInLayout) {
// set in the state so that when the handle is created, we're accurate.
SetToolStripState(STATE_HASVISIBLEITEMS, true);
return true;
}
}
SetToolStripState(STATE_HASVISIBLEITEMS, false);
return false;
}
// after the handle is created, we start layout... so this state is cached.
return GetToolStripState(STATE_HASVISIBLEITEMS);
}
set {
SetToolStripState(STATE_HASVISIBLEITEMS, value);
}
}
/// <include file='doc\ScrollableControl.uex' path='docs/doc[@for="ScrollableControl.HorizontalScroll"]/*' />
/// <devdoc>
/// <para>Gets the Horizontal Scroll bar for this ScrollableControl.</para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never)
]
new public HScrollProperties HorizontalScroll
{
get
{
return base.HorizontalScroll;
}
}
[
DefaultValue(typeof(Size), "16,16"),
SRCategory(SR.CatAppearance),
SRDescription(SR.ToolStripImageScalingSizeDescr),
]
public Size ImageScalingSize {
get {
return ImageScalingSizeInternal;
}
set {
ImageScalingSizeInternal = value;
}
}
internal virtual Size ImageScalingSizeInternal {
get {
return imageScalingSize;
}
set {
if (imageScalingSize != value) {
imageScalingSize = value;
LayoutTransaction.DoLayoutIf((Items.Count > 0), this, this, PropertyNames.ImageScalingSize);
foreach (ToolStripItem item in this.Items) {
item.OnImageScalingSizeChanged(EventArgs.Empty);
}
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ImageList"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the <see cref='System.Windows.Forms.ImageList'/> that contains the <see cref='System.Drawing.Image'/> displayed on a label control.
/// </para>
/// </devdoc>
[
DefaultValue(null),
SRCategory(SR.CatAppearance),
SRDescription(SR.ToolStripImageListDescr),
Browsable(false)
]
public ImageList ImageList {
get {
return imageList;
}
set {
if (imageList != value) {
EventHandler handler = new EventHandler(ImageListRecreateHandle);
// Remove the previous imagelist handle recreate handler
//
if (imageList != null) {
imageList.RecreateHandle -= handler;
}
imageList = value;
// Add the new imagelist handle recreate handler
//
if (value != null) {
value.RecreateHandle += handler;
}
foreach (ToolStripItem item in Items) {
item.InvalidateImageListImage();
}
Invalidate();
}
}
}
/// <devdoc>
/// Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing).
/// </devdoc>
internal override bool IsMnemonicsListenerAxSourced
{
get{
return true;
}
}
internal bool IsInToolStripPanel {
get {
return ToolStripPanelRow != null;
}
}
/// <devdoc> indicates whether the user is currently
/// moving the toolstrip from one toolstrip container
/// to another
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
public bool IsCurrentlyDragging {
get {
return GetToolStripState(STATE_DRAGGING);
}
}
/// <devdoc>
/// indicates if the SetBoundsCore is called thru Locationchanging.
/// </devdoc>
private bool IsLocationChanging {
get {
return GetToolStripState(STATE_LOCATIONCHANGING);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.Items"]/*' />
/// <devdoc>
/// The items that belong to this ToolStrip.
/// Note - depending on space and layout preferences, not all items
/// in this collection will be displayed. They may not even be displayed
/// on this winbar (say in the case where we're overflowing the item).
/// The collection of _Displayed_ items is the DisplayedItems collection.
/// The displayed items collection also includes things like the OverflowButton
/// and the Grip.
/// </devdoc>
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
SRCategory(SR.CatData),
SRDescription(SR.ToolStripItemsDescr),
MergableProperty(false)
]
public virtual ToolStripItemCollection Items {
get {
if (toolStripItemCollection == null) {
toolStripItemCollection = new ToolStripItemCollection(this, true);
}
return toolStripItemCollection;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ItemAdded"]/*' />
[SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripItemAddedDescr)]
public event ToolStripItemEventHandler ItemAdded {
add {
Events.AddHandler(EventItemAdded, value);
}
remove {
Events.RemoveHandler(EventItemAdded, value);
}
}
/// <include file='doc\ToolStripDropDown.uex' path='docs/doc[@for="ToolStripDropDown.ItemClicked"]/*' />
/// <devdoc>
/// <para>Occurs when the control is clicked.</para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.ToolStripItemOnClickDescr)]
public event ToolStripItemClickedEventHandler ItemClicked {
add {
Events.AddHandler(EventItemClicked, value);
}
remove {
Events.RemoveHandler(EventItemClicked, value);
}
}
/// <devdoc>
/// we have a backbuffer for painting items... this is cached to be the size of the largest
/// item in the collection - and is cached in OnPaint, and disposed when the toolstrip
/// is no longer visible.
///
/// [: toolstrip - main hdc ] <-- visible to user
/// [ toolstrip double buffer hdc ] <-- onpaint hands us this buffer, after we're done DBuf is copied to "main hdc"/
/// [tsi dc] <-- we copy the background from the DBuf, then paint the item into this DC, then BitBlt back up to DBuf
///
/// This is done because GDI wont honor GDI+ TranslateTransform. We used to use DCMapping to change the viewport
/// origin and clipping rect of the toolstrip double buffer hdc to paint each item, but this proves costly
/// because you need to allocate GDI+ Graphics objects for every single item. This method allows us to only
/// allocate 1 Graphics object and share it between all the items in OnPaint.
/// </devdoc>
private CachedItemHdcInfo ItemHdcInfo {
get {
if (cachedItemHdcInfo == null) {
cachedItemHdcInfo = new CachedItemHdcInfo();
}
return cachedItemHdcInfo;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ItemRemoved"]/*' />
[SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripItemRemovedDescr)]
public event ToolStripItemEventHandler ItemRemoved {
add {
Events.AddHandler(EventItemRemoved, value);
}
remove {
Events.RemoveHandler(EventItemRemoved, value);
}
}
/// <include file='doc\WinBar.uex' path='docs/doc[@for="ToolStrip.IsDropDown"]/*' />
/// <devdoc> handy check for painting and sizing </devdoc>
[Browsable(false)]
public bool IsDropDown {
get { return (this is ToolStripDropDown); }
}
internal bool IsDisposingItems {
get {
return GetToolStripState(STATE_DISPOSINGITEMS);
}
}
/// <devdoc>
/// The OnDrag[blah] methods that will be called if AllowItemReorder is true.
///
/// This allows us to have methods that handle drag/drop of the winbar items
/// without calling back on the user's code
/// </devdoc>
internal IDropTarget ItemReorderDropTarget {
get {
return itemReorderDropTarget;
}
set {
itemReorderDropTarget = value;
}
}
/// <devdoc>
/// The OnQueryContinueDrag and OnGiveFeedback methods that will be called if
/// AllowItemReorder is true.
///
/// This allows us to have methods that handle drag/drop of the winbar items
/// without calling back on the user's code
/// </devdoc>
internal ISupportOleDropSource ItemReorderDropSource {
get {
return itemReorderDropSource;
}
set {
itemReorderDropSource = value;
}
}
internal bool IsInDesignMode {
get {
return DesignMode;
}
}
internal bool IsTopInDesignMode {
get {
var topLevelToolStrip = GetToplevelOwnerToolStrip();
return topLevelToolStrip != null && topLevelToolStrip.IsInDesignMode;
}
}
internal bool IsSelectionSuspended {
get { return GetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE); }
}
internal ToolStripItem LastMouseDownedItem {
get {
if (lastMouseDownedItem != null &&
(lastMouseDownedItem.IsDisposed || lastMouseDownedItem.ParentInternal != this)){
// handle disposal, parent changed since we last mouse downed.
lastMouseDownedItem = null;
}
return lastMouseDownedItem;
}
}
[
DefaultValue(null),
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public LayoutSettings LayoutSettings {
get {
return layoutSettings;
}
set {
layoutSettings = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.LayoutStyle"]/*' />
/// <devdoc>
/// Specifies whether we're horizontal or vertical
/// </devdoc>
[
SRDescription(SR.ToolStripLayoutStyle),
SRCategory(SR.CatLayout),
AmbientValue(ToolStripLayoutStyle.StackWithOverflow)
]
public ToolStripLayoutStyle LayoutStyle {
get {
if (layoutStyle == ToolStripLayoutStyle.StackWithOverflow) {
switch (this.Orientation) {
case Orientation.Horizontal:
return ToolStripLayoutStyle.HorizontalStackWithOverflow;
case Orientation.Vertical:
return ToolStripLayoutStyle.VerticalStackWithOverflow;
}
}
return layoutStyle;
}
set {
//valid values are 0x0 to 0x4
if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripLayoutStyle.StackWithOverflow, (int)ToolStripLayoutStyle.Table)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripLayoutStyle));
}
if (layoutStyle != value) {
layoutStyle = value;
switch (value) {
case ToolStripLayoutStyle.Flow:
if (!(layoutEngine is FlowLayout)) {
layoutEngine = FlowLayout.Instance;
}
// Orientation really only applies to split stack layout (which swaps based on Dock, ToolStripPanel location)
UpdateOrientation(Orientation.Horizontal);
break;
case ToolStripLayoutStyle.Table:
if (!(layoutEngine is TableLayout)) {
layoutEngine = TableLayout.Instance;
}
// Orientation really only applies to split stack layout (which swaps based on Dock, ToolStripPanel location)
UpdateOrientation(Orientation.Horizontal);
break;
case ToolStripLayoutStyle.StackWithOverflow:
case ToolStripLayoutStyle.HorizontalStackWithOverflow:
case ToolStripLayoutStyle.VerticalStackWithOverflow:
default:
if (value != ToolStripLayoutStyle.StackWithOverflow) {
UpdateOrientation((value == ToolStripLayoutStyle.VerticalStackWithOverflow) ? Orientation.Vertical : Orientation.Horizontal);
}
else {
if (IsInToolStripPanel) {
UpdateLayoutStyle(ToolStripPanelRow.Orientation);
}
else {
UpdateLayoutStyle(this.Dock);
}
}
if (!(layoutEngine is ToolStripSplitStackLayout)) {
layoutEngine = new ToolStripSplitStackLayout(this);
}
break;
}
using (LayoutTransaction.CreateTransactionIf(IsHandleCreated, this, this, PropertyNames.LayoutStyle)) {
LayoutSettings = CreateLayoutSettings(layoutStyle);
}
OnLayoutStyleChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.LayoutCompleted"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripLayoutCompleteDescr)]
public event EventHandler LayoutCompleted {
add {
Events.AddHandler(EventLayoutCompleted, value);
}
remove {
Events.RemoveHandler(EventLayoutCompleted, value);
}
}
internal bool LayoutRequired {
get {
return this.layoutRequired;
}
set {
this.layoutRequired = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.LayoutStyleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripLayoutStyleChangedDescr)]
public event EventHandler LayoutStyleChanged {
add {
Events.AddHandler(EventLayoutStyleChanged, value);
}
remove {
Events.RemoveHandler(EventLayoutStyleChanged, value);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.LayoutEngine"]/*' />
public override LayoutEngine LayoutEngine {
get {
//
return layoutEngine;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.LocationChanging"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
internal event ToolStripLocationCancelEventHandler LocationChanging {
add {
Events.AddHandler(EventLocationChanging, value);
}
remove {
Events.RemoveHandler(EventLocationChanging, value);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.MaxItemSize"]/*' />
protected internal virtual Size MaxItemSize {
get {
return this.DisplayRectangle.Size;
}
}
internal bool MenuAutoExpand {
get {
if (!DesignMode) {
if (GetToolStripState(STATE_MENUAUTOEXPAND)) {
if (!IsDropDown && !ToolStripManager.ModalMenuFilter.InMenuMode) {
SetToolStripState(STATE_MENUAUTOEXPAND, false);
return false;
}
return true;
}
}
return false;
}
set {
if (!DesignMode) {
SetToolStripState(STATE_MENUAUTOEXPAND, value);
}
}
}
internal Stack<MergeHistory> MergeHistoryStack {
get {
if(mergeHistoryStack == null) {
mergeHistoryStack = new Stack<MergeHistory>();
}
return mergeHistoryStack;
}
}
private MouseHoverTimer MouseHoverTimer {
get {
if (mouseHoverTimer == null) {
mouseHoverTimer = new MouseHoverTimer();
}
return mouseHoverTimer;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OverflowButton"]/*' />
/// <devdoc>
/// Summary of OverflowButton.
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
public ToolStripOverflowButton OverflowButton {
get {
if (toolStripOverflowButton == null) {
toolStripOverflowButton = new ToolStripOverflowButton(this);
toolStripOverflowButton.Overflow = ToolStripItemOverflow.Never;
toolStripOverflowButton.ParentInternal = this;
toolStripOverflowButton.Alignment = ToolStripItemAlignment.Right;
toolStripOverflowButton.Size = toolStripOverflowButton.GetPreferredSize(this.DisplayRectangle.Size - this.Padding.Size);
}
return toolStripOverflowButton;
}
}
//
// SECREVIEW VSWhidbey 436973: adding control host items to this collection in
// the internet zone will throw security exceptions
//
internal ToolStripItemCollection OverflowItems {
get {
if (overflowItems == null) {
overflowItems = new ToolStripItemCollection(this, false);
}
return overflowItems;
}
}
[Browsable(false)]
public Orientation Orientation {
get {
return orientation;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.PaintGrip"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripPaintGripDescr)]
public event PaintEventHandler PaintGrip {
add {
Events.AddHandler(EventPaintGrip, value);
}
remove {
Events.RemoveHandler(EventPaintGrip, value);
}
}
internal RestoreFocusMessageFilter RestoreFocusFilter {
get {
if (restoreFocusFilter == null) {
restoreFocusFilter = new RestoreFocusMessageFilter(this);
}
return restoreFocusFilter;
}
}
internal ToolStripPanelCell ToolStripPanelCell {
get { return ((ISupportToolStripPanel)this).ToolStripPanelCell; }
}
internal ToolStripPanelRow ToolStripPanelRow {
get { return ((ISupportToolStripPanel)this).ToolStripPanelRow; }
}
// fetches the Cell associated with this toolstrip.
ToolStripPanelCell ISupportToolStripPanel.ToolStripPanelCell {
get {
ToolStripPanelCell toolStripPanelCell = null;
if (!IsDropDown && !IsDisposed) {
if (Properties.ContainsObject(ToolStrip.PropToolStripPanelCell)) {
toolStripPanelCell = (ToolStripPanelCell)Properties.GetObject(ToolStrip.PropToolStripPanelCell);
}
else {
toolStripPanelCell = new ToolStripPanelCell(this);
Properties.SetObject(ToolStrip.PropToolStripPanelCell, toolStripPanelCell);
}
}
return toolStripPanelCell;
}
}
ToolStripPanelRow ISupportToolStripPanel.ToolStripPanelRow {
get {
ToolStripPanelCell cell = ToolStripPanelCell;
if (cell == null) {
return null;
}
return ToolStripPanelCell.ToolStripPanelRow;
}
set {
ToolStripPanelRow oldToolStripPanelRow = ToolStripPanelRow;
if (oldToolStripPanelRow != value) {
ToolStripPanelCell cell = ToolStripPanelCell;
if (cell == null) {
return;
}
cell.ToolStripPanelRow = value;
if (value != null) {
if (oldToolStripPanelRow == null || oldToolStripPanelRow.Orientation != value.Orientation) {
if (layoutStyle == ToolStripLayoutStyle.StackWithOverflow)
{
UpdateLayoutStyle(value.Orientation);
}
else
{
UpdateOrientation(value.Orientation);
}
}
}
else {
if (oldToolStripPanelRow != null && oldToolStripPanelRow.ControlsInternal.Contains(this)) {
oldToolStripPanelRow.ControlsInternal.Remove(this);
}
UpdateLayoutStyle(Dock);
}
}
}
}
[DefaultValue(false)]
[SRCategory(SR.CatLayout)]
[SRDescription(SR.ToolStripStretchDescr)]
public bool Stretch {
get {
return GetToolStripState(STATE_STRETCH);
}
set {
if (Stretch != value) {
SetToolStripState(STATE_STRETCH,value);
}
}
}
internal override bool SupportsUiaProviders {
get {
return AccessibilityImprovements.Level3 && !DesignMode && !IsTopInDesignMode;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.Renderer"]/*' />
/// <devdoc>
/// The renderer is used to paint the hwndless winbar items. If someone wanted to
/// change the "Hot" look of all of their buttons to be a green triangle, they should
/// create a class that derives from ToolStripRenderer, assign it to this property and call
/// invalidate.
/// </devdoc>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public ToolStripRenderer Renderer {
get {
if (IsDropDown) {
// PERF: since this is called a lot we dont want to make it virtual
ToolStripDropDown dropDown = this as ToolStripDropDown;
if (dropDown is ToolStripOverflow || dropDown.IsAutoGenerated) {
if (dropDown.OwnerToolStrip != null) {
return dropDown.OwnerToolStrip.Renderer;
}
}
}
if (RenderMode == ToolStripRenderMode.ManagerRenderMode) {
return ToolStripManager.Renderer;
}
// always return a valid renderer so our paint code
// doesn't have to be bogged down by checks for null.
SetToolStripState(STATE_USEDEFAULTRENDERER, false);
if (renderer == null) {
Renderer = ToolStripManager.CreateRenderer(RenderMode);
}
return renderer;
}
set {
// if the value happens to be null, the next get
// will autogenerate a new ToolStripRenderer.
if (renderer != value) {
SetToolStripState(STATE_USEDEFAULTRENDERER, (value == null));
renderer = value;
currentRendererType = (renderer != null) ? renderer.GetType() : typeof(System.Type);
OnRendererChanged(EventArgs.Empty);
}
}
}
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public event EventHandler RendererChanged {
add {
Events.AddHandler(EventRendererChanged, value);
}
remove {
Events.RemoveHandler(EventRendererChanged, value);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.DrawMode"]/*' />
[
SRDescription(SR.ToolStripRenderModeDescr),
SRCategory(SR.CatAppearance),
]
public ToolStripRenderMode RenderMode {
get {
if (GetToolStripState(STATE_USEDEFAULTRENDERER)) {
return ToolStripRenderMode.ManagerRenderMode;
}
if (renderer != null && !renderer.IsAutoGenerated) {
return ToolStripRenderMode.Custom;
}
// check the type of the currently set renderer.
// types are cached as this may be called frequently.
if (currentRendererType == ToolStripManager.ProfessionalRendererType) {
return ToolStripRenderMode.Professional;
}
if (currentRendererType == ToolStripManager.SystemRendererType) {
return ToolStripRenderMode.System;
}
return ToolStripRenderMode.Custom;
}
set {
//valid values are 0x0 to 0x3
if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripRenderMode.Custom, (int)ToolStripRenderMode.ManagerRenderMode)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripRenderMode));
}
if (value == ToolStripRenderMode.Custom) {
throw new NotSupportedException(SR.GetString(SR.ToolStripRenderModeUseRendererPropertyInstead));
}
if (value == ToolStripRenderMode.ManagerRenderMode) {
if (!GetToolStripState(STATE_USEDEFAULTRENDERER)) {
SetToolStripState(STATE_USEDEFAULTRENDERER, true);
OnRendererChanged(EventArgs.Empty);
}
}
else {
SetToolStripState(STATE_USEDEFAULTRENDERER, false);
Renderer = ToolStripManager.CreateRenderer(value);
}
}
}
/// <summary>
/// ToolStripItems need to access this to determine if they should be showing underlines
/// for their accelerators. Since they are not HWNDs, and this method is protected on control
/// we need a way for them to get at it.
/// </summary>
internal bool ShowKeyboardCuesInternal {
get {
return this.ShowKeyboardCues;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ShowItemToolTips"]/*' />
[DefaultValue(true)]
[SRDescription(SR.ToolStripShowItemToolTipsDescr)]
[SRCategory(SR.CatBehavior)]
public bool ShowItemToolTips {
get {
return showItemToolTips;
}
set {
if (showItemToolTips != value) {
showItemToolTips = value;
if (!showItemToolTips) {
UpdateToolTip(null);
}
if (!AccessibilityImprovements.UseLegacyToolTipDisplay) {
ToolTip internalToolTip = this.ToolTip;
foreach (ToolStripItem item in this.Items) {
if (showItemToolTips) {
KeyboardToolTipStateMachine.Instance.Hook(item, internalToolTip);
}
else {
KeyboardToolTipStateMachine.Instance.Unhook(item, internalToolTip);
}
}
}
// Fix for Dev10 889523
// If the overflow button has not been created, don't check its properties
// since this will force its creating and cause a re-layout of the control
if (toolStripOverflowButton != null && this.OverflowButton.HasDropDownItems) {
this.OverflowButton.DropDown.ShowItemToolTips = value;
}
}
}
}
/// <devdoc> internal lookup table for shortcuts... intended to speed search time </devdoc>
internal Hashtable Shortcuts {
get {
if (shortcuts == null) {
shortcuts = new Hashtable(1);
}
return shortcuts;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.TabStop"]/*' />
/// <devdoc>
/// <para>Indicates whether the user can give the focus to this control using the TAB
/// key. This property is read-only.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(false),
DispId(NativeMethods.ActiveX.DISPID_TABSTOP),
SRDescription(SR.ControlTabStopDescr)
]
public new bool TabStop {
get {
return base.TabStop;
}
set {
base.TabStop = value;
}
}
/// <devdoc> this is the ToolTip used for the individual items
/// it only works if ShowItemToolTips = true
/// </devdoc>
internal ToolTip ToolTip {
get {
ToolTip toolTip;
if (!Properties.ContainsObject(ToolStrip.PropToolTip)) {
toolTip = new ToolTip();
Properties.SetObject(ToolStrip.PropToolTip,toolTip );
}
else {
toolTip = (ToolTip)Properties.GetObject(ToolStrip.PropToolTip);
}
return toolTip;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.TextDirection"]/*' />
[
DefaultValue(ToolStripTextDirection.Horizontal),
SRDescription(SR.ToolStripTextDirectionDescr),
SRCategory(SR.CatAppearance)
]
public virtual ToolStripTextDirection TextDirection {
get {
ToolStripTextDirection textDirection = ToolStripTextDirection.Inherit;
if (Properties.ContainsObject(ToolStrip.PropTextDirection)) {
textDirection= (ToolStripTextDirection)Properties.GetObject(ToolStrip.PropTextDirection);
}
if (textDirection == ToolStripTextDirection.Inherit) {
textDirection = ToolStripTextDirection.Horizontal;
}
return textDirection;
}
set {
//valid values are 0x0 to 0x3
if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolStripTextDirection.Inherit, (int)ToolStripTextDirection.Vertical270)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripTextDirection));
}
Properties.SetObject(ToolStrip.PropTextDirection, value);
using(new LayoutTransaction(this, this, "TextDirection")) {
for (int i = 0; i < Items.Count; i++) {
Items[i].OnOwnerTextDirectionChanged();
}
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.VerticalScroll"]/*' />
/// <devdoc>
/// <para>Gets the Vertical Scroll bar for this ScrollableControl.</para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never)
]
new public VScrollProperties VerticalScroll
{
get
{
return base.VerticalScroll;
}
}
void ISupportToolStripPanel.BeginDrag() {
OnBeginDrag(EventArgs.Empty);
}
// Internal so that it's not a public API.
internal virtual void ChangeSelection(ToolStripItem nextItem) {
if (nextItem != null) {
ToolStripControlHost controlHost = nextItem as ToolStripControlHost;
// if we contain focus, we should set focus to ourselves
// so we get the focus off the thing that's currently focused
// e.g. go from a text box to a toolstrip button
if (ContainsFocus && !Focused) {
this.FocusInternal();
if (controlHost == null) {
// if nextItem IS a toolstripcontrolhost, we're going to focus it anyways
// we only fire KeyboardActive when "focusing" a non-hwnd backed item
KeyboardActive = true;
}
}
if (controlHost != null) {
if (hwndThatLostFocus == IntPtr.Zero) {
SnapFocus(UnsafeNativeMethods.GetFocus());
}
controlHost.Control.Select();
controlHost.Control.FocusInternal();
}
nextItem.Select();
ToolStripMenuItem tsNextItem = nextItem as ToolStripMenuItem;
if (tsNextItem != null && !IsDropDown) {
// only toplevel menus auto expand when the selection changes.
tsNextItem.HandleAutoExpansion();
}
}
}
protected virtual LayoutSettings CreateLayoutSettings(ToolStripLayoutStyle layoutStyle) {
switch (layoutStyle) {
case ToolStripLayoutStyle.Flow:
return new FlowLayoutSettings(this);
case ToolStripLayoutStyle.Table:
return new TableLayoutSettings(this);
default:
return null;
}
}
protected internal virtual ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) {
if (text == "-") {
return new ToolStripSeparator();
}
else {
return new ToolStripButton(text,image,onClick);
}
}
/// <devdoc>
/// Summary of ClearAllSelections.
/// </devdoc>
private void ClearAllSelections() {
ClearAllSelectionsExcept(null);
}
/// <devdoc>
/// Summary of ClearAllSelectionsExcept.
/// </devdoc>
/// <param name=item></param>
private void ClearAllSelectionsExcept(ToolStripItem item) {
Rectangle regionRect = (item == null) ? Rectangle.Empty : item.Bounds;
Region region = null;
try {
for (int i = 0; i < DisplayedItems.Count; i++) {
if (DisplayedItems[i] == item) {
continue;
}
else if (item != null && DisplayedItems[i].Pressed) {
//
ToolStripDropDownItem dropDownItem = DisplayedItems[i] as ToolStripDropDownItem;
if (dropDownItem != null && dropDownItem.HasDropDownItems) {
dropDownItem.AutoHide(item);
}
}
bool invalidate = false;
if (DisplayedItems[i].Selected) {
DisplayedItems[i].Unselect();
Debug.WriteLineIf(SelectionDebug.TraceVerbose,"[SelectDBG ClearAllSelectionsExcept] Unselecting " + DisplayedItems[i].Text);
invalidate = true;
}
if (invalidate) {
// since regions are heavy weight - only use if we need it.
if (region == null) {
region = new Region(regionRect);
}
region.Union(DisplayedItems[i].Bounds);
}
}
// force an WM_PAINT to happen now to instantly reflect the selection change.
if (region != null) {
Invalidate(region, true);
Update();
}
else if (regionRect != Rectangle.Empty) {
Invalidate(regionRect, true);
Update();
}
}
finally {
if (region != null) {
region.Dispose();
}
}
// fire accessibility
if (IsHandleCreated && item != null) {
int focusIndex = DisplayedItems.IndexOf(item);
AccessibilityNotifyClients(AccessibleEvents.Focus, focusIndex);
}
}
internal void ClearInsertionMark() {
if (lastInsertionMarkRect != Rectangle.Empty) {
// stuff away the lastInsertionMarkRect
// and clear it out _before_ we call paint OW
// the call to invalidate wont help as it will get
// repainted.
Rectangle invalidate = lastInsertionMarkRect;
lastInsertionMarkRect = Rectangle.Empty;
this.Invalidate(invalidate);
}
}
private void ClearLastMouseDownedItem() {
ToolStripItem lastItem = lastMouseDownedItem;
lastMouseDownedItem = null;
if (IsSelectionSuspended) {
SetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE, false);
if (lastItem != null) {
lastItem.Invalidate();
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.Dispose"]/*' />
/// <devdoc>
/// Clean up any resources being used.
/// </devdoc>
protected override void Dispose( bool disposing ) {
if(disposing) {
ToolStripOverflow overflow = GetOverflow();
try {
this.SuspendLayout();
if (overflow != null) {
overflow.SuspendLayout();
}
// if there's a problem in config, dont be a leaker.
SetToolStripState(STATE_DISPOSINGITEMS, true);
lastMouseDownedItem = null;
HookStaticEvents(/*hook=*/false);
ToolStripPanelCell toolStripPanelCell = Properties.GetObject(ToolStrip.PropToolStripPanelCell) as ToolStripPanelCell;
if (toolStripPanelCell != null) {
toolStripPanelCell.Dispose();
}
if (cachedItemHdcInfo != null) {
cachedItemHdcInfo.Dispose();
}
if (mouseHoverTimer != null) {
mouseHoverTimer.Dispose();
}
ToolTip toolTip = (ToolTip)Properties.GetObject(ToolStrip.PropToolTip);
if (toolTip != null) {
toolTip.Dispose ();
}
if (!Items.IsReadOnly) {
// only dispose the items we actually own.
for (int i = Items.Count - 1; i >= 0; i--) {
Items[i].Dispose();
}
Items.Clear();
}
// clean up items not in the Items list
if (toolStripGrip != null) {
toolStripGrip.Dispose();
}
if (toolStripOverflowButton != null) {
toolStripOverflowButton.Dispose();
}
// remove the restore focus filter
if (restoreFocusFilter != null) {
// PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could
// get called a lot and we want to have to assert AWP.
Application.ThreadContext.FromCurrent().RemoveMessageFilter(restoreFocusFilter);
restoreFocusFilter = null;
}
// exit menu mode if necessary.
bool exitMenuMode = false;
if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this) {
exitMenuMode = true;
}
ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this);
// if we were the last toolstrip in the queue, exit menu mode.
if (exitMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == null) {
Debug.WriteLineIf(ToolStrip.SnapFocusDebug.TraceVerbose, "Exiting menu mode because we're the last toolstrip in the queue, and we've disposed.");
ToolStripManager.ModalMenuFilter.ExitMenuMode();
}
ToolStripManager.ToolStrips.Remove(this);
}
finally {
this.ResumeLayout(false);
if (overflow != null) {
overflow.ResumeLayout(false);
}
SetToolStripState(STATE_DISPOSINGITEMS, false);
}
}
base.Dispose( disposing );
}
internal void DoLayoutIfHandleCreated(ToolStripItemEventArgs e) {
if (this.IsHandleCreated) {
LayoutTransaction.DoLayout(this, e.Item, PropertyNames.Items);
this.Invalidate();
// Adding this item may have added it to the overflow
// However, we can't check if it's in OverflowItems, because
// it gets added there in Layout, and layout might be suspended.
if (this.CanOverflow && this.OverflowButton.HasDropDown) {
if (DeferOverflowDropDownLayout()) {
CommonProperties.xClearPreferredSizeCache(this.OverflowButton.DropDown);
this.OverflowButton.DropDown.LayoutRequired = true;
}
else {
LayoutTransaction.DoLayout(this.OverflowButton.DropDown, e.Item, PropertyNames.Items);
this.OverflowButton.DropDown.Invalidate();
}
}
}
else {
// next time we fetch the preferred size, recalc it.
CommonProperties.xClearPreferredSizeCache(this);
this.LayoutRequired = true;
if (this.CanOverflow && this.OverflowButton.HasDropDown) {
this.OverflowButton.DropDown.LayoutRequired = true;
}
}
}
private bool DeferOverflowDropDownLayout() {
return this.IsLayoutSuspended
||!this.OverflowButton.DropDown.Visible
|| !this.OverflowButton.DropDown.IsHandleCreated;
}
void ISupportToolStripPanel.EndDrag() {
ToolStripPanel.ClearDragFeedback();
OnEndDrag(EventArgs.Empty);
}
internal ToolStripOverflow GetOverflow() {
return (toolStripOverflowButton == null || !toolStripOverflowButton.HasDropDown) ? null : toolStripOverflowButton.DropDown as ToolStripOverflow;
}
internal byte GetMouseId() {
// never return 0 as the mousedown ID, this is the "reset" value.
if (mouseDownID == 0) {
mouseDownID++;
}
return mouseDownID;
}
internal virtual ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction, bool rtlAware) {
if (rtlAware && RightToLeft == RightToLeft.Yes) {
if (direction == ArrowDirection.Right) {
direction = ArrowDirection.Left;
}
else if (direction == ArrowDirection.Left) {
direction = ArrowDirection.Right;
}
}
return GetNextItem(start, direction);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.GetNextItem"]/*' />
/// <devdoc>
/// Gets the next item from the given start item in the direction specified.
/// - This function wraps if at the end
/// - This function will only surf the items in the current container
/// - Overriding this function will change the tab ordering and accessible child ordering.
/// </devdoc>
public virtual ToolStripItem GetNextItem(ToolStripItem start, ArrowDirection direction)
{
if (!WindowsFormsUtils.EnumValidator.IsValidArrowDirection(direction)) {
throw new InvalidEnumArgumentException("direction", (int)direction, typeof(ArrowDirection));
}
switch (direction) {
case ArrowDirection.Right:
return GetNextItemHorizontal(start, /*forward = */true);
case ArrowDirection.Left:
return GetNextItemHorizontal(start, /*forward = */false);
case ArrowDirection.Down:
return GetNextItemVertical(start, /*forward = */true);
case ArrowDirection.Up:
return GetNextItemVertical(start, /*forward = */false);
}
return null;
}
// <devdoc>
// Helper function for GetNextItem - do not directly call this.
// </devdoc>
private ToolStripItem GetNextItemHorizontal(ToolStripItem start, bool forward) {
if (DisplayedItems.Count <= 0)
return null;
if (start == null) {
// The navigation should be consistent when navigating in forward and
// backward direction entering the toolstrip, it means that for AI.Level3
// the first toolstrip item should be selected irrespectively TAB or SHIFT+TAB
// is pressed.
start = GetStartItem(forward);
}
int current = DisplayedItems.IndexOf(start);
if (current == -1) {
Debug.WriteLineIf(SelectionDebug.TraceVerbose, "Started from a visible = false item");
return null;
}
Debug.WriteLineIf(SelectionDebug.TraceVerbose && (current != -1), "[SelectDBG GetNextToolStripItem] Last selected item was " + ((current != -1) ? DisplayedItems[current].Text : ""));
Debug.WriteLineIf(SelectionDebug.TraceVerbose && (current == -1), "[SelectDBG GetNextToolStripItem] Last selected item was null");
int count = DisplayedItems.Count;
do {
if (forward) {
current = ++current % count;
}
else { // provide negative wrap if necessary
current = (--current < 0) ? count + current : current;
}
ToolStripDropDown dropDown = this as ToolStripDropDown;
if (dropDown!= null)
{
if (dropDown.OwnerItem != null && dropDown.OwnerItem.IsInDesignMode) {
return DisplayedItems[current];
}
}
if (DisplayedItems[current].CanKeyboardSelect) {
Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG GetNextToolStripItem] selecting " + DisplayedItems[current].Text);
//ClearAllSelectionsExcept(Items[current]);
return DisplayedItems[current];
}
} while (DisplayedItems[current] != start);
return null;
}
private ToolStripItem GetStartItem(bool forward) {
if (forward) {
return DisplayedItems[DisplayedItems.Count - 1];
}
else if (AccessibilityImprovements.Level3 && !(this is ToolStripDropDown)) {
// For the drop-down up-directed loop should be preserved.
// So if the current item is topmost, then the bottom item should be selected on up-key press.
return DisplayedItems[DisplayedItems.Count > 1 ? 1 : 0];
}
return DisplayedItems[0];
}
// <devdoc>
// Helper function for GetNextItem - do not directly call this.
// </devdoc>
[SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")]
private ToolStripItem GetNextItemVertical(ToolStripItem selectedItem, bool down) {
ToolStripItem tanWinner = null;
ToolStripItem hypotenuseWinner = null;
double minHypotenuse = Double.MaxValue;
double minTan = Double.MaxValue;
double hypotenuseOfTanWinner = Double.MaxValue;
double tanOfHypotenuseWinner = Double.MaxValue;
if (selectedItem == null) {
ToolStripItem item = GetNextItemHorizontal(selectedItem, down);
return item;
}
ToolStripDropDown dropDown = this as ToolStripDropDown;
if (dropDown != null)
{
if (dropDown.OwnerItem != null && (dropDown.OwnerItem.IsInDesignMode || (dropDown.OwnerItem.Owner != null && dropDown.OwnerItem.Owner.IsInDesignMode))) {
ToolStripItem item = GetNextItemHorizontal(selectedItem, down);
return item;
}
}
Point midPointOfCurrent = new Point(selectedItem.Bounds.X + selectedItem.Width / 2,
selectedItem.Bounds.Y + selectedItem.Height / 2);
for(int i = 0; i < DisplayedItems.Count; i++) {
ToolStripItem otherItem = DisplayedItems[i];
if (otherItem == selectedItem || !otherItem.CanKeyboardSelect) {
continue;
}
if (!down && otherItem.Bounds.Bottom > selectedItem.Bounds.Top) {
// if we are going up the other control has to be above
continue;
}
else if (down && otherItem.Bounds.Top < selectedItem.Bounds.Bottom) {
// if we are going down the other control has to be below
continue;
}
//[ otherControl ]
// *
Point otherItemMidLocation = new Point(otherItem.Bounds.X + otherItem.Width/2, (down)? otherItem.Bounds.Top : otherItem.Bounds.Bottom);
#if DEBUG_UPDOWN
Graphics g = Graphics.FromHwnd(this.Handle);
using (Pen p = new Pen(Color.FromKnownColor((KnownColor)i))) {
g.DrawLine(p,otherItemMidLocation, midPointOfCurrent);
}
System.Threading.Thread.Sleep(100);
g.Dispose();
#endif
int oppositeSide = otherItemMidLocation.X - midPointOfCurrent.X;
int adjacentSide = otherItemMidLocation.Y - midPointOfCurrent.Y;
// use pythagrian theorem to calculate the length of the distance
// between the middle of the current control in question and it's adjacent
// objects.
double hypotenuse = Math.Sqrt(adjacentSide*adjacentSide + oppositeSide*oppositeSide);
if (adjacentSide != 0) { // avoid divide by zero - we dont do layered controls
// _[o]
// |/
// [s]
// get the angle between s and o by taking the arctan.
// PERF consider using approximation instead
double tan = Math.Abs(Math.Atan(oppositeSide/adjacentSide));
// we want the thing with the smallest angle and smallest distance between midpoints
minTan = Math.Min(minTan, tan);
minHypotenuse = Math.Min(minHypotenuse, hypotenuse);
if (minTan == tan && minTan != Double.NaN) {
tanWinner = otherItem;
hypotenuseOfTanWinner = hypotenuse;
}
if (minHypotenuse == hypotenuse) {
hypotenuseWinner = otherItem;
tanOfHypotenuseWinner = tan;
}
}
}
#if DEBUG_UPDOWN
string tanWinnerString = (tanWinner == null) ? "null" : tanWinner.ToString();
string hypWinnerString = (hypotenuseWinner == null) ? "null": hypotenuseWinner.ToString();
Debug.WriteLine(String.Format("Tangent winner is {0} Hyp winner is {1}", tanWinnerString, hypWinnerString));
#endif
if ((tanWinner == null) || (hypotenuseWinner == null)) {
return (GetNextItemHorizontal(null,down));
}
else {
// often times the guy with the best angle will be the guy with the closest hypotenuse.
// however in layouts where things are more randomly spaced, this is not necessarily the case.
if (tanOfHypotenuseWinner == minTan) {
// if the angles match up, such as in the case of items of the same width in vertical flow
// then pick the closest one.
return hypotenuseWinner;
}
else if ((!down && tanWinner.Bounds.Bottom <= hypotenuseWinner.Bounds.Top)
||(down && tanWinner.Bounds.Top > hypotenuseWinner.Bounds.Bottom)) {
// we prefer the case where the angle is smaller than
// the case where the hypotenuse is smaller. The only
// scenarios where that is not the case is when the hypoteneuse
// winner is clearly closer than the angle winner.
// [a.winner] | [s]
// | [h.winner]
// [h.winner] |
// [s] | [a.winner]
return hypotenuseWinner;
}
else {
return tanWinner;
}
}
}
internal override Size GetPreferredSizeCore(Size proposedSize) {
// We act like a container control
// Translating 0,0 from ClientSize to actual Size tells us how much space
// is required for the borders.
if (proposedSize.Width == 1) {
proposedSize.Width = Int32.MaxValue;
}
if (proposedSize.Height == 1) {
proposedSize.Height = Int32.MaxValue;
}
Padding padding = Padding;
Size prefSize = LayoutEngine.GetPreferredSize(this, proposedSize - padding.Size);
Padding newPadding = Padding;
// VSWhidbey 471860:
// as a side effect of some of the layouts, we can change the padding.
// if this happens, we need to clear the cache.
if (padding != newPadding) {
CommonProperties.xClearPreferredSizeCache(this);
}
return prefSize + newPadding.Size;
}
#region GetPreferredSizeHelpers
//
// These are here so they can be shared between splitstack layout and StatusStrip
//
internal static Size GetPreferredSizeHorizontal(IArrangedElement container, Size proposedConstraints) {
Size maxSize = Size.Empty;
ToolStrip toolStrip = container as ToolStrip;
// ensure preferred size respects default size as a minimum.
Size defaultSize = toolStrip.DefaultSize - toolStrip.Padding.Size;
maxSize.Height = Math.Max(0, defaultSize.Height);
bool requiresOverflow = false;
bool foundItemParticipatingInLayout = false;
for (int j = 0; j < toolStrip.Items.Count; j++) {
ToolStripItem item = toolStrip.Items[j];
if (((IArrangedElement)item).ParticipatesInLayout) {
foundItemParticipatingInLayout =true;
if (item.Overflow != ToolStripItemOverflow.Always) {
Padding itemMargin = item.Margin;
Size prefItemSize = GetPreferredItemSize(item);
maxSize.Width += itemMargin.Horizontal + prefItemSize.Width;
maxSize.Height = Math.Max(maxSize.Height, itemMargin.Vertical + prefItemSize.Height);
}
else {
requiresOverflow = true;
}
}
}
if (toolStrip.Items.Count == 0 || (!foundItemParticipatingInLayout)) {
// if there are no items there, create something anyways.
maxSize = defaultSize;
}
if (requiresOverflow) {
// add in the width of the overflow button
ToolStripOverflowButton overflowItem = toolStrip.OverflowButton;
Padding overflowItemMargin = overflowItem.Margin;
maxSize.Width += overflowItemMargin.Horizontal + overflowItem.Bounds.Width;
}
else {
maxSize.Width += 2; //add Padding of 2 Pixels to the right if not Overflow.
}
if (toolStrip.GripStyle == ToolStripGripStyle.Visible) {
// add in the grip width
Padding gripMargin = toolStrip.GripMargin;
maxSize.Width += gripMargin.Horizontal + toolStrip.Grip.GripThickness;
}
maxSize = LayoutUtils.IntersectSizes(maxSize, proposedConstraints);
return maxSize;
}
[SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")]
internal static Size GetPreferredSizeVertical(IArrangedElement container, Size proposedConstraints) {
Size maxSize = Size.Empty;
bool requiresOverflow = false;
ToolStrip toolStrip = container as ToolStrip;
bool foundItemParticipatingInLayout = false;
for (int j = 0; j < toolStrip.Items.Count; j++) {
ToolStripItem item = toolStrip.Items[j];
if (((IArrangedElement)item).ParticipatesInLayout) {
foundItemParticipatingInLayout = true;
if (item.Overflow != ToolStripItemOverflow.Always) {
Size preferredSize = GetPreferredItemSize(item);
Padding itemMargin = item.Margin;
maxSize.Height += itemMargin.Vertical + preferredSize.Height;
maxSize.Width = Math.Max(maxSize.Width, itemMargin.Horizontal + preferredSize.Width);
}
else {
requiresOverflow = true;
}
}
}
if (toolStrip.Items.Count == 0 || !foundItemParticipatingInLayout) {
// if there are no items there, create something anyways.
maxSize = LayoutUtils.FlipSize( toolStrip.DefaultSize);
}
if (requiresOverflow) {
// add in the width of the overflow button
ToolStripOverflowButton overflowItem = toolStrip.OverflowButton;
Padding overflowItemMargin = overflowItem.Margin;
maxSize.Height += overflowItemMargin.Vertical + overflowItem.Bounds.Height;
}
else {
maxSize.Height += 2; //add Padding to the bottom if not Overflow.
}
if (toolStrip.GripStyle == ToolStripGripStyle.Visible) {
// add in the grip width
Padding gripMargin = toolStrip.GripMargin;
maxSize.Height += gripMargin.Vertical + toolStrip.Grip.GripThickness;
}
// note here the difference in vertical - we want the strings to fit perfectly so we're not going to constrain by the specified size.
if (toolStrip.Size != maxSize)
{
CommonProperties.xClearPreferredSizeCache(toolStrip);
}
return maxSize;
}
private static Size GetPreferredItemSize(ToolStripItem item) {
return item.AutoSize ? item.GetPreferredSize(Size.Empty) : item.Size;
}
#endregion
#region MeasurementGraphics
//
internal static Graphics GetMeasurementGraphics() {
return WindowsFormsUtils.CreateMeasurementGraphics();
}
#endregion
/// <devdoc>
/// Summary of GetSelectedItem.
/// </devdoc>
internal ToolStripItem GetSelectedItem() {
ToolStripItem selectedItem = null;
for (int i = 0; i < DisplayedItems.Count; i++) {
if (DisplayedItems[i].Selected) {
selectedItem = DisplayedItems[i];
}
}
return selectedItem;
}
/// <devdoc>
/// Retrieves the current value of the specified bit in the control's state.
/// </devdoc>
internal bool GetToolStripState(int flag) {
return (toolStripState & flag) != 0;
}
internal virtual ToolStrip GetToplevelOwnerToolStrip() {
return this;
}
/// In the case of a
/// toolstrip -> toolstrip
/// contextmenustrip -> the control that is showing it
/// toolstripdropdown -> top most toolstrip
internal virtual Control GetOwnerControl() {
return this;
}
private void HandleMouseLeave() {
// If we had a particular item that was "entered"
// notify it that we have left.
if (lastMouseActiveItem != null) {
if (!DesignMode) {
MouseHoverTimer.Cancel(lastMouseActiveItem);
}
try {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "firing mouse leave on " + lastMouseActiveItem.ToString());
lastMouseActiveItem.FireEvent(EventArgs.Empty,ToolStripItemEventType.MouseLeave);
}
finally {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "setting last active item to null");
lastMouseActiveItem = null;
}
}
ToolStripMenuItem.MenuTimer.HandleToolStripMouseLeave(this);
}
/// <devdoc>
/// Summary of HandleItemClick.
/// </devdoc>
internal void HandleItemClick(ToolStripItem dismissingItem) {
ToolStripItemClickedEventArgs e= new ToolStripItemClickedEventArgs(dismissingItem);
OnItemClicked(e);
// VSWhidbey 395136 - ensure both the overflow and the main toolstrip fire ItemClick event
// otherwise the overflow wont dismiss.
if (!IsDropDown && dismissingItem.IsOnOverflow) {
OverflowButton.DropDown.HandleItemClick(dismissingItem);
}
}
internal virtual void HandleItemClicked(ToolStripItem dismissingItem) {
// post processing after the click has happened.
/*if (ContainsFocus && !Focused) {
RestoreFocusInternal();
}*/
ToolStripDropDownItem item = dismissingItem as ToolStripDropDownItem;
if (item != null && !item.HasDropDownItems)
{
KeyboardActive = false;
}
}
private void HookStaticEvents(bool hook) {
if (hook) {
if (!alreadyHooked) {
try {
ToolStripManager.RendererChanged += new EventHandler(OnDefaultRendererChanged);
SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged);
}
finally{
alreadyHooked = true;
}
}
}
else if (alreadyHooked) {
try {
ToolStripManager.RendererChanged -= new EventHandler(OnDefaultRendererChanged);
SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(OnUserPreferenceChanged);
}
finally {
alreadyHooked = false;
}
}
}
//initialize winbar
private void InitializeRenderer(ToolStripRenderer renderer) {
// wrap this in a LayoutTransaction so that if they change sizes
// in this method we've suspended layout.
using(LayoutTransaction.CreateTransactionIf(AutoSize, this, this, PropertyNames.Renderer)) {
renderer.Initialize(this);
for (int i = 0; i < this.Items.Count; i++) {
renderer.InitializeItem(this.Items[i]);
}
}
Invalidate( this.Controls.Count > 0);
}
// sometimes you only want to force a layout if the winbar is visible.
private void InvalidateLayout() {
if (IsHandleCreated) {
LayoutTransaction.DoLayout(this, this, null);
}
}
internal void InvalidateTextItems() {
using (new LayoutTransaction(this, this, "ShowKeyboardFocusCues", /*PerformLayout=*/Visible)) {
for (int j = 0; j < DisplayedItems.Count; j++) {
if (((DisplayedItems[j].DisplayStyle & ToolStripItemDisplayStyle.Text) == ToolStripItemDisplayStyle.Text)){
DisplayedItems[j].InvalidateItemLayout("ShowKeyboardFocusCues");
}
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.IsInputKey"]/*' />
/// <devdoc>
/// Summary of IsInputKey.
/// </devdoc>
/// <param name=keyData></param>
protected override bool IsInputKey(Keys keyData) {
ToolStripItem item = this.GetSelectedItem();
if ((item != null) && item.IsInputKey(keyData)) {
return true;
}
return base.IsInputKey(keyData);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.IsInputChar"]/*' />
/// <devdoc>
/// Summary of IsInputChar.
/// </devdoc>
/// <param name=charCode></param>
protected override bool IsInputChar(char charCode) {
ToolStripItem item = this.GetSelectedItem();
if ((item != null) && item.IsInputChar(charCode)) {
return true;
}
return base.IsInputChar(charCode);
}
private static bool IsPseudoMnemonic(char charCode, string text) {
if (!String.IsNullOrEmpty(text)) {
if (!WindowsFormsUtils.ContainsMnemonic(text)) {
char charToCompare = Char.ToUpper(charCode, CultureInfo.CurrentCulture);
char firstLetter = Char.ToUpper(text[0], CultureInfo.CurrentCulture);
if (firstLetter == charToCompare ||(Char.ToLower(charCode, CultureInfo.CurrentCulture) == Char.ToLower(text[0], CultureInfo.CurrentCulture)) ) {
return true;
}
}
}
return false;
}
/// <devdoc> Force an item to be painted immediately, rather than waiting for WM_PAINT to occur. </devdoc>
internal void InvokePaintItem(ToolStripItem item) {
// Force a WM_PAINT to happen NOW.
Invalidate(item.Bounds);
Update();
}
/// <devdoc>
/// Gets or sets the <see cref='System.Windows.Forms.ImageList'/> that contains the <see cref='System.Drawing.Image'/> displayed on a label control
/// </devdoc>
private void ImageListRecreateHandle(object sender, EventArgs e) {
Invalidate();
}
/// <devdoc>
/// This override fires the LocationChanging event if
/// 1) We are not currently Rafting .. since this cause this infinite times...
/// 2) If we havent been called once .. Since the "LocationChanging" is listened to by the RaftingCell and calls "JOIN" which may call us back.
/// </devdoc>
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
Point location = this.Location;
if (!IsCurrentlyDragging && !IsLocationChanging && IsInToolStripPanel)
{
ToolStripLocationCancelEventArgs cae = new ToolStripLocationCancelEventArgs(new Point(x, y), false);
try {
if (location.X != x || location.Y != y) {
SetToolStripState(STATE_LOCATIONCHANGING, true);
OnLocationChanging(cae);
}
if (!cae.Cancel) {
base.SetBoundsCore(x, y, width, height, specified);
}
}
finally {
SetToolStripState(STATE_LOCATIONCHANGING, false);
}
}
else {
if (IsCurrentlyDragging) {
Region transparentRegion = Renderer.GetTransparentRegion(this);
if (transparentRegion != null && (location.X != x || location.Y != y)) {
try {
Invalidate(transparentRegion);
Update();
}
finally {
transparentRegion.Dispose();
}
}
}
SetToolStripState(STATE_LOCATIONCHANGING, false);
base.SetBoundsCore(x, y, width, height, specified);
}
}
internal void PaintParentRegion(Graphics g, Region region) {
}
internal bool ProcessCmdKeyInternal(ref Message m, Keys keyData) {
return ProcessCmdKey(ref m, keyData);
}
// This function will print to the PrinterDC. ToolStrip have there own buffered painting and doesnt play very well
// with the DC translations done by base Control class. Hence we do our own Painting and the BitBLT the DC into the printerDc.
// Refer to VsWhidbey : 400683.
internal override void PrintToMetaFileRecursive(HandleRef hDC, IntPtr lParam, Rectangle bounds) {
using (Bitmap image = new Bitmap(bounds.Width, bounds.Height))
using (Graphics g = Graphics.FromImage(image)) {
IntPtr imageHdc = g.GetHdc();
//send the actual wm_print message
UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.WM_PRINT, (IntPtr)imageHdc,
(IntPtr)(NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT));
//now BLT the result to the destination bitmap.
IntPtr desthDC = hDC.Handle;
SafeNativeMethods.BitBlt(new HandleRef(this, desthDC), bounds.X, bounds.Y, bounds.Width, bounds.Height,
new HandleRef(g, imageHdc), 0, 0, NativeMethods.SRCCOPY);
g.ReleaseHdcInternal(imageHdc);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ProcessCmdKey"]/*' />
/// <devdoc>
/// Summary of ProcessCmdKey.
/// </devdoc>
/// <param name=m></param>
/// <param name=keyData></param>
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override bool ProcessCmdKey(ref Message m, Keys keyData) {
if (ToolStripManager.IsMenuKey(keyData)) {
if (!IsDropDown && ToolStripManager.ModalMenuFilter.InMenuMode) {
ClearAllSelections();
ToolStripManager.ModalMenuFilter.MenuKeyToggle = true;
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.ProcessCmdKey] Detected a second ALT keypress while in Menu Mode.");
ToolStripManager.ModalMenuFilter.ExitMenuMode();
}
}
// Give the ToolStripItem very first chance at
// processing keys (except for ALT handling)
ToolStripItem selectedItem = this.GetSelectedItem();
if (selectedItem != null){
if (selectedItem.ProcessCmdKey(ref m, keyData)) {
return true;
}
}
foreach (ToolStripItem item in this.Items) {
if (item == selectedItem) {
continue;
}
if (item.ProcessCmdKey(ref m, keyData)) {
return true;
}
}
if (!IsDropDown) {
bool isControlTab =
(keyData & Keys.Control) == Keys.Control && (keyData & Keys.KeyCode) == Keys.Tab;
if (isControlTab && !TabStop && HasKeyboardInput) {
bool handled = false;
if ((keyData & Keys.Shift) == Keys.None) {
handled = ToolStripManager.SelectNextToolStrip(this, /*forward*/true);
}
else {
handled = ToolStripManager.SelectNextToolStrip(this, /*forward*/false);
}
if (handled) {
return true;
}
}
}
return base.ProcessCmdKey(ref m, keyData);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ProcessDialogKey"]/*' />
/// <devdoc>
/// Processes a dialog key. Overrides Control.processDialogKey(). This
/// method implements handling of the TAB, LEFT, RIGHT, UP, and DOWN
/// keys in dialogs.
/// The method performs no processing on keys that include the ALT or
/// CONTROL modifiers. For the TAB key, the method selects the next control
/// on the form. For the arrow keys,
/// !!!
/// </devdoc>
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected override bool ProcessDialogKey(Keys keyData) {
bool retVal = false;
// Give the ToolStripItem first dibs
ToolStripItem item = this.GetSelectedItem();
if (item != null){
if(item.ProcessDialogKey(keyData)) {
return true;
}
}
// if the ToolStrip receives an escape, then we
// should send the focus back to the last item that
// had focus.
bool hasModifiers = ((keyData & (Keys.Alt | Keys.Control)) != Keys.None);
Keys keyCode = (Keys)keyData & Keys.KeyCode;
switch (keyCode) {
case Keys.Back:
// if it's focused itself, process. if it's not focused, make sure a child control
// doesnt have focus before processing
if (!ContainsFocus) {
// shift backspace/backspace work as backspace, which is the same as shift+tab
retVal = ProcessTabKey(false);
}
break;
case Keys.Tab:
// ctrl+tab does nothing
if (!hasModifiers){
retVal = ProcessTabKey((keyData & Keys.Shift) == Keys.None);
}
break;
case Keys.Left:
case Keys.Right:
case Keys.Up:
case Keys.Down:
retVal = ProcessArrowKey(keyCode);
break;
case Keys.Home:
SelectNextToolStripItem(null, /*forward =*/ true );
retVal = true;
break;
case Keys.End:
SelectNextToolStripItem(null, /*forward =*/ false );
retVal = true;
break;
case Keys.Escape: // escape and menu key should restore focus
// ctrl+esc does nothing
if (!hasModifiers && !TabStop){
RestoreFocusInternal();
retVal = true;
}
break;
}
if (retVal) {
return retVal;
}
Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] calling base");
return base.ProcessDialogKey(keyData);
}
internal virtual void ProcessDuplicateMnemonic(ToolStripItem item, char charCode) {
if (!CanProcessMnemonic()) { // Checking again for security...
return;
}
// SECREVIEW see toolstrip dropdown ProcessDuplicateMnemonic for special security implications
if (item != null) {
// SECREVIEW: SetFocusUnsafe as ProcessMnemonic has link demand for AWP.
SetFocusUnsafe();
item.Select();
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ProcessMnemonic"]/*' />
/// <devdoc>
///
/// Rules for parsing mnemonics
/// PASS 1: Real mnemonics
/// Check items for the character after the &. If it matches, perform the click event or open the dropdown (in the case that it has dropdown items)
/// PASS 2: Fake mnemonics
/// Begin with the current selection and parse through the first character in the items in the menu.
/// If there is only one item that matches
/// perform the click event or open the dropdown (in the case that it has dropdown items)
/// Else
/// change the selection from the current selected item to the first item that matched.
/// </devdoc>
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected internal override bool ProcessMnemonic(char charCode) {
// menus and toolbars only take focus on ALT
if (!CanProcessMnemonic()) {
return false;
}
if (Focused || ContainsFocus) {
return ProcessMnemonicInternal(charCode);
}
bool inMenuMode = ToolStripManager.ModalMenuFilter.InMenuMode;
if (!inMenuMode && Control.ModifierKeys == Keys.Alt) {
// This is the case where someone hasnt released the ALT key yet, but has pushed another letter.
// In some cases we can activate the menu that is not the MainMenuStrip...
// See VSWhidbey 501382 for more details.
return ProcessMnemonicInternal(charCode);
}
else if (inMenuMode && ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this) {
return ProcessMnemonicInternal(charCode);
}
// do not call base, as we dont want to walk through the controls collection and reprocess everything
// we should have processed in the displayed items collection.
return false;
}
private bool ProcessMnemonicInternal(char charCode) {
if (!CanProcessMnemonic()) { // Checking again for security...
return false;
}
// at this point we assume we can process mnemonics as process mnemonic has filtered for use.
ToolStripItem startingItem = GetSelectedItem();
int startIndex = 0;
if (startingItem != null) {
startIndex = DisplayedItems.IndexOf(startingItem);
}
startIndex = Math.Max(0, startIndex);
ToolStripItem firstMatch = null;
bool foundMenuItem = false;
int index = startIndex;
// PASS1, iterate through the real mnemonics
for (int i = 0; i < DisplayedItems.Count; i++) {
ToolStripItem currentItem = DisplayedItems[index];
index = (index +1)%DisplayedItems.Count;
if (string.IsNullOrEmpty(currentItem.Text) || !currentItem.Enabled) {
continue;
}
// VSWhidbey 429513 - only items which display text should be processed
if ((currentItem.DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.Text) {
continue;
}
// keep track whether we've found a menu item - we'll have to do a
// second pass for fake mnemonics in that case.
foundMenuItem = (foundMenuItem || (currentItem is ToolStripMenuItem));
if (Control.IsMnemonic(charCode,currentItem.Text)) {
if (firstMatch == null) {
firstMatch = currentItem;
}
else {
// we've found a second match - we should only change selection.
if (firstMatch == startingItem) {
// change the selection to be the second match as the first is already selected
ProcessDuplicateMnemonic(currentItem, charCode);
}
else {
ProcessDuplicateMnemonic(firstMatch, charCode);
}
// we've found two mnemonics, just return.
return true;
}
}
}
// We've found a singular match.
if (firstMatch != null) {
return firstMatch.ProcessMnemonic(charCode);
}
if (!foundMenuItem) {
return false;
}
index = startIndex;
// 242501 MenuStrip parity: key presses should change selection if mnemonic not present
// if we havent found a mnemonic, cycle through the menu items and
// checbbbMk if we match.
// PASS2, iterate through the pseudo mnemonics
for (int i = 0; i < DisplayedItems.Count; i++) {
ToolStripItem currentItem = DisplayedItems[index];
index = (index +1)%DisplayedItems.Count;
// Menu items only
if (!(currentItem is ToolStripMenuItem) || string.IsNullOrEmpty(currentItem.Text) || !currentItem.Enabled) {
continue;
}
// VSWhidbey 429513 - only items which display text should be processed
if ((currentItem.DisplayStyle & ToolStripItemDisplayStyle.Text) != ToolStripItemDisplayStyle.Text) {
continue;
}
if (ToolStrip.IsPseudoMnemonic(charCode,currentItem.Text)) {
if (firstMatch == null) {
firstMatch = currentItem;
}
else {
// we've found a second match - we should only change selection.
if (firstMatch == startingItem) {
// change the selection to be the second match as the first is already selected
ProcessDuplicateMnemonic(currentItem, charCode);
}
else {
ProcessDuplicateMnemonic(firstMatch, charCode);
}
// we've found two mnemonics, just return.
return true;
}
}
}
if (firstMatch != null) {
return firstMatch.ProcessMnemonic(charCode);
}
// do not call base, as we dont want to walk through the controls collection and reprocess everything
// we should have processed in the displayed items collection.
return false;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ProcessTabKey"]/*' />
/// <devdoc>
/// Summary of ProcessTabKey.
/// </devdoc>
/// <param name=forward></param>
private bool ProcessTabKey(bool forward) {
if (TabStop) {
// ToolBar in tab-order parity
// this means we want the toolstrip in the normal tab order - which means it shouldnt wrap.
// First tab gets you into the toolstrip, second tab moves you on your way outside the container.
// arrow keys would continue to wrap.
return false;
}
else {
// TabStop = false
// this means we dont want the toolstrip in the normal tab order (default).
// We got focus to the toolstrip by putting focus into a control contained on the toolstrip or
// via a mnemonic e.g. Bold. In this case we want to wrap.
// arrow keys would continue to wrap
if (RightToLeft == RightToLeft.Yes) {
forward = !forward;
}
SelectNextToolStripItem(GetSelectedItem(), forward);
return true;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.ProcessArrowKey"]/*' />
/// <devdoc>
/// Summary of ProcessArrowKey: this is more useful than overriding ProcessDialogKey because usually
/// the difference between ToolStrip/ToolStripDropDown is arrow key handling. ProcessDialogKey first gives
/// the selected ToolStripItem the chance to process the message... so really a proper inheritor would
/// call down to the base first. Unfortunately doing this would cause the the arrow keys would be eaten
/// in the base class. Instead we're providing a separate place to override all arrow key handling.
/// </devdoc>
internal virtual bool ProcessArrowKey(Keys keyCode) {
bool retVal = false;
Debug.WriteLineIf(MenuAutoExpandDebug.TraceVerbose, "[ToolStrip.ProcessArrowKey] MenuTimer.Cancel called");
ToolStripMenuItem.MenuTimer.Cancel();
switch (keyCode) {
case Keys.Left:
case Keys.Right:
retVal = ProcessLeftRightArrowKey(keyCode == Keys.Right);
break;
case Keys.Up:
case Keys.Down:
if (IsDropDown || Orientation != Orientation.Horizontal) {
ToolStripItem currentSel = GetSelectedItem();
if (keyCode == Keys.Down) {
ToolStripItem nextItem = GetNextItem(currentSel, ArrowDirection.Down);
if (nextItem != null) {
ChangeSelection(nextItem);
retVal = true;
}
}
else {
ToolStripItem nextItem = GetNextItem(currentSel, ArrowDirection.Up);
if (nextItem != null){
ChangeSelection(nextItem);
retVal = true;
}
}
}
break;
}
return retVal;
}
/// <devdoc>
/// Process an arrowKey press by selecting the next control in the group
/// that the activeControl belongs to.
/// </devdoc>
/// <internalonly/>
private bool ProcessLeftRightArrowKey(bool right) {
ToolStripItem selectedItem = GetSelectedItem();
ToolStripItem nextItem = SelectNextToolStripItem(GetSelectedItem(), right);
return true;
}
/// <devdoc>
/// Summary of NotifySelectionChange.
/// </devdoc>
/// <param name=item></param>
internal void NotifySelectionChange(ToolStripItem item) {
if (item == null) {
Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG NotifySelectionChange] none should be selected");
ClearAllSelections();
}
else if (item.Selected) {
Debug.WriteLineIf(SelectionDebug.TraceVerbose, "[SelectDBG NotifySelectionChange] Notify selection change: " + item.ToString() + ": " + item.Selected.ToString());
ClearAllSelectionsExcept(item);
}
}
private void OnDefaultRendererChanged(object sender, EventArgs e) {
// callback from static event
if (GetToolStripState(STATE_USEDEFAULTRENDERER)) {
OnRendererChanged(e);
}
}
protected virtual void OnBeginDrag(EventArgs e) {
SetToolStripState(STATE_DRAGGING, true);
Debug.Assert(ToolStripPanelRow != null, "Why is toolstrippanel row null?");
Debug.Assert(this.ParentInternal as ToolStripPanel != null, "Why is our parent not a toolstrip panel?");
ClearAllSelections();
UpdateToolTip(null); // supress the tooltip.
EventHandler handler = (EventHandler)Events[EventBeginDrag];
if (handler != null) handler(this,e);
}
protected virtual void OnEndDrag(EventArgs e) {
SetToolStripState(STATE_DRAGGING, false);
Debug.Assert(ToolStripPanelRow != null, "Why is toolstrippanel row null?");
Debug.Assert(this.ParentInternal as ToolStripPanel != null, "Why is our parent not a toolstrip panel?");
Debug.Assert(ToolStripPanelRow == null || ToolStripPanelRow.ToolStripPanel.RowsInternal.Contains(ToolStripPanelRow), "Why are we in an orphaned row?");
EventHandler handler = (EventHandler)Events[EventEndDrag];
if (handler != null) handler(this,e);
}
protected override void OnDockChanged(EventArgs e){
base.OnDockChanged(e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnDefaultRendererChanged"]/*' />
protected virtual void OnRendererChanged(EventArgs e) {
InitializeRenderer(Renderer);
EventHandler handler = (EventHandler)Events[EventRendererChanged];
if (handler != null) handler(this,e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnEnabledChanged"]/*' />
/// <devdoc>
/// Summary of OnEnabledChanged.
/// </devdoc>
protected override void OnEnabledChanged(EventArgs e) {
base.OnEnabledChanged(e);
// notify items that the parent has changed
for (int i = 0; i < this.Items.Count; i++) {
if (Items[i] != null && Items[i].ParentInternal == this) {
Items[i].OnParentEnabledChanged(e);
}
}
}
internal void OnDefaultFontChanged() {
defaultFont = null;
if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) {
ToolStripManager.CurrentDpi = DeviceDpi;
defaultFont = ToolStripManager.DefaultFont;
}
if (!IsFontSet()) {
OnFontChanged(EventArgs.Empty);
}
}
protected override void OnFontChanged(EventArgs e) {
base.OnFontChanged(e);
for (int i = 0; i < this.Items.Count; i++) {
Items[i].OnOwnerFontChanged(e);
}
}
protected override void OnInvalidated(InvalidateEventArgs e) {
base.OnInvalidated(e);
#if false
// DEBUG code which is helpful for FlickerFest debugging.
if (FlickerDebug.TraceVerbose) {
string name = this.Name;
if (string.IsNullOrEmpty(name)) {
if (IsDropDown) {
ToolStripItem item = ((ToolStripDropDown)this).OwnerItem;
if (item != null && item.Name != null) {
name = item.Name = ".DropDown";
}
}
if (string.IsNullOrEmpty(name)) {
name = this.GetType().Name;
}
}
// for debugging VS we want to filter out the propgrid toolstrip
Debug.WriteLineIf(!(this.ParentInternal is PropertyGrid), "Invalidate called on: " + name + new StackTrace().ToString());
}
#endif
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnHandleCreated"]/*' />
/// <devdoc>
/// Summary of OnHandleCreated.
/// </devdoc>
protected override void OnHandleCreated(EventArgs e) {
if ((this.AllowDrop || this.AllowItemReorder) && (DropTargetManager != null)) {
this.DropTargetManager.EnsureRegistered(this);
}
// calling control's (in base) version AFTER we register our DropTarget, so it will
// listen to us instead of control's implementation
base.OnHandleCreated(e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnHandleDestroyed"]/*' />
/// <devdoc>
/// Summary of OnHandleDestroyed.
/// </devdoc>
protected override void OnHandleDestroyed(EventArgs e) {
if (DropTargetManager != null) {
// Make sure we unregister ourselves as a drop target
this.DropTargetManager.EnsureUnRegistered(this);
}
base.OnHandleDestroyed(e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnItemAdded"]/*' />
protected internal virtual void OnItemAdded(ToolStripItemEventArgs e) {
DoLayoutIfHandleCreated(e);
if (!HasVisibleItems && e.Item != null && ((IArrangedElement)e.Item).ParticipatesInLayout) {
// VSWhidbey 441403
// in certain cases, we may not have laid out yet (e.g. a dropdown may not layout until
// it becomes visible.) We will recalculate this in SetDisplayedItems, but for the moment
// if we find an item that ParticipatesInLayout, mark us as having visible items.
HasVisibleItems = true;
}
ToolStripItemEventHandler handler = (ToolStripItemEventHandler)Events[EventItemAdded];
if (handler != null) handler(this, e);
}
/// <include file='doc\ToolStripDropDown.uex' path='docs/doc[@for="ToolStripDropDown.OnItemClicked"]/*' />
/// <devdoc>
/// Called when an item has been clicked on the winbar.
/// </devdoc>
protected virtual void OnItemClicked(ToolStripItemClickedEventArgs e) {
ToolStripItemClickedEventHandler handler = (ToolStripItemClickedEventHandler)Events[EventItemClicked];
if (handler != null) handler(this, e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnItemRemoved"]/*' />
protected internal virtual void OnItemRemoved(ToolStripItemEventArgs e) {
// clear cached item states.
OnItemVisibleChanged(e, /*performlayout*/true);
ToolStripItemEventHandler handler = (ToolStripItemEventHandler)Events[EventItemRemoved];
if (handler != null) handler(this, e);
}
internal void OnItemVisibleChanged(ToolStripItemEventArgs e, bool performLayout) {
// clear cached item states.
if (e.Item == lastMouseActiveItem) {
lastMouseActiveItem = null;
}
if (e.Item == LastMouseDownedItem) {
lastMouseDownedItem = null;
}
if (e.Item == currentlyActiveTooltipItem) {
UpdateToolTip(null);
}
if (performLayout) {
DoLayoutIfHandleCreated(e);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnLayout"]/*' />
protected override void OnLayout(LayoutEventArgs e) {
this.LayoutRequired = false;
// we need to do this to prevent autosizing to happen while we're reparenting.
ToolStripOverflow overflow = GetOverflow();
if (overflow != null) {
overflow.SuspendLayout();
toolStripOverflowButton.Size = toolStripOverflowButton.GetPreferredSize(this.DisplayRectangle.Size - this.Padding.Size);
}
for (int j = 0; j < Items.Count; j++) {
Items[j].OnLayout(e);
}
base.OnLayout(e);
SetDisplayedItems();
OnLayoutCompleted(EventArgs.Empty);
Invalidate();
if (overflow != null) {
overflow.ResumeLayout();
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnLayoutCompleted"]/*' />
protected virtual void OnLayoutCompleted(EventArgs e) {
EventHandler handler = (EventHandler)Events[EventLayoutCompleted];
if (handler != null) handler(this, e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnLayoutStyleChanged"]/*' />
protected virtual void OnLayoutStyleChanged(EventArgs e) {
EventHandler handler = (EventHandler)Events[EventLayoutStyleChanged];
if (handler != null) handler(this, e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnLostFocus"]/*' />
protected override void OnLostFocus(EventArgs e) {
base.OnLostFocus(e);
ClearAllSelections();
}
protected override void OnLeave(EventArgs e) {
base.OnLeave(e);
if (!IsDropDown) {
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "uninstalling RestoreFocusFilter");
// PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could
// get called a lot and we want to have to assert AWP.
Application.ThreadContext.FromCurrent().RemoveMessageFilter(RestoreFocusFilter);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnLayoutCompleted"]/*' />
internal virtual void OnLocationChanging(ToolStripLocationCancelEventArgs e) {
ToolStripLocationCancelEventHandler handler = (ToolStripLocationCancelEventHandler)Events[EventLocationChanging];
if (handler != null) handler(this, e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnMouseDown"]/*' />
/// <devdoc>
/// Delegate mouse down to the winbar and its affected items
/// </devdoc>
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs mea) {
// NEVER use this directly from another class. Always use GetMouseID so that
// 0 is not returned to another class.
mouseDownID++;
ToolStripItem item = GetItemAt(mea.X, mea.Y);
if (item != null) {
if (!IsDropDown && (!(item is ToolStripDropDownItem))){
// set capture only when we know we're not on a dropdown (already effectively have capture due to modal menufilter)
// and the item in question requires the mouse to be in the same item to be clicked.
SetToolStripState(STATE_LASTMOUSEDOWNEDITEMCAPTURE, true);
this.CaptureInternal = true;
}
MenuAutoExpand = true;
if (mea != null) {
// Transpose this to "client coordinates" of the ToolStripItem.
Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords);
mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta);
}
lastMouseDownedItem = item;
item.FireEvent(mea, ToolStripItemEventType.MouseDown);
}
else {
base.OnMouseDown(mea);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnMouseMove"]/*' />
/// <devdoc>
/// Delegate mouse moves to the winbar and its affected items
/// </devdoc>
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs mea) {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose,"OnMouseMove called");
ToolStripItem item = GetItemAt(mea.X, mea.Y);
if (!Grip.MovingToolStrip) {
// If we had a particular item that was "entered"
// notify it that we have entered. It's fair to put
// this in the MouseMove event, as MouseEnter is fired during
// control's WM_MOUSEMOVE. Waiting until this event gives us
// the actual coordinates.
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Item to get mouse move: {0}", (item == null) ? "null" : item.ToString()));
if (item != lastMouseActiveItem) {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "This is a new item - last item to get was {0}", (lastMouseActiveItem == null) ? "null" : lastMouseActiveItem.ToString()));
// notify the item that we've moved on
HandleMouseLeave();
// track only items that dont get mouse events themselves.
lastMouseActiveItem = (item is ToolStripControlHost) ? null : item;
if (lastMouseActiveItem != null) {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseEnter on: {0}", (lastMouseActiveItem == null) ? "null" : lastMouseActiveItem.ToString()));
item.FireEvent(new System.EventArgs(), ToolStripItemEventType.MouseEnter);
}
//
if (!DesignMode) {
MouseHoverTimer.Start(lastMouseActiveItem);
}
}
}
else {
item = this.Grip;
}
if (item != null) {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseMove on: {0}", (item == null) ? "null" : item.ToString()));
// Fire mouse move on the item
// Transpose this to "client coordinates" of the ToolStripItem.
Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords);
mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta);
item.FireEvent(mea, ToolStripItemEventType.MouseMove);
}
else {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "Firing MouseMove on: {0}", (this == null) ? "null" : this.ToString()));
base.OnMouseMove(mea);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnMouseLeave"]/*' />
/// <devdoc>
/// Delegate mouse leave to the winbar and its affected items
/// </devdoc>
protected override void OnMouseLeave(System.EventArgs e) {
HandleMouseLeave();
base.OnMouseLeave(e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnMouseCaptureChanged"]/*' />
protected override void OnMouseCaptureChanged(System.EventArgs e) {
if (!GetToolStripState(STATE_SUSPENDCAPTURE)) {
// while we're showing a feedback rect, dont cancel moving the toolstrip.
Grip.MovingToolStrip = false;
}
ClearLastMouseDownedItem();
base.OnMouseCaptureChanged(e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnMouseUp"]/*' />
/// <devdoc>
/// Delegate mouse up to the winbar and its affected items
/// </devdoc>
protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mea) {
ToolStripItem item = (Grip.MovingToolStrip) ? Grip : GetItemAt(mea.X, mea.Y);
if (item != null) {
if (mea != null) {
// Transpose this to "client coordinates" of the ToolStripItem.
Point itemRelativePoint = item.TranslatePoint(new Point(mea.X, mea.Y), ToolStripPointType.ToolStripCoords, ToolStripPointType.ToolStripItemCoords);
mea = new MouseEventArgs(mea.Button, mea.Clicks,itemRelativePoint.X, itemRelativePoint.Y, mea.Delta);
}
item.FireEvent(mea, ToolStripItemEventType.MouseUp);
}
else {
base.OnMouseUp(mea);
}
ClearLastMouseDownedItem();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics toolstripGraphics = e.Graphics;
Size bitmapSize = this.largestDisplayedItemSize;
bool excludedTransparentRegion = false;
Rectangle viewableArea = this.DisplayRectangle;
Region transparentRegion = Renderer.GetTransparentRegion(this);
try {
// Paint the items
// The idea here is to let items pretend they are controls.
// they should get paint events at 0,0 and have proper clipping regions
// set up for them. We cannot use g.TranslateTransform as that does
// not translate the GDI world, and things like XP Visual Styles and the
// TextRenderer only know how to speak GDI.
//
// The previous appropach was to set up the GDI clipping region and allocate a graphics
// from that, but that meant we were allocating graphics objects left and right, which
// turned out to be slow.
//
// So now we allocate an offscreen bitmap of size == MaxItemSize, copy the background
// of the toolstrip into that bitmap, then paint the item on top of the bitmap, then copy
// the contents of the bitmap back onto the toolstrip. This gives us our paint event starting
// at 0,0. Combine this with double buffering of the toolstrip and the entire toolstrip is updated
// after returning from this function.
if (!LayoutUtils.IsZeroWidthOrHeight(bitmapSize)) {
// cant create a 0x0 bmp.
// Supporting RoundedEdges...
// we've got a concept of a region that we shouldnt paint (the TransparentRegion as specified in the Renderer).
// in order to support this we're going to intersect that region with the clipping region.
// this new region will be excluded during the guts of OnPaint, and restored at the end of OnPaint.
if (transparentRegion != null) {
// only use the intersection so we can easily add back in the bits we took out at the end.
transparentRegion.Intersect(toolstripGraphics.Clip);
toolstripGraphics.ExcludeClip(transparentRegion);
excludedTransparentRegion = true;
}
// Preparing for painting the individual items...
// using WindowsGraphics here because we want to preserve the clipping information.
// calling GetHdc by itself does not set up the clipping info.
using(WindowsGraphics toolStripWindowsGraphics = WindowsGraphics.FromGraphics(toolstripGraphics, ApplyGraphicsProperties.Clipping)){
// get the cached item HDC.
HandleRef toolStripHDC = new HandleRef(this, toolStripWindowsGraphics.GetHdc());
HandleRef itemHDC = ItemHdcInfo.GetCachedItemDC(toolStripHDC, bitmapSize);
Graphics itemGraphics = Graphics.FromHdcInternal(itemHDC.Handle);
try {
// Painting the individual items...
// iterate through all the items, painting them
// one by one into the compatible offscreen DC, and then copying
// them back onto the main toolstrip.
for (int i = 0; i < DisplayedItems.Count; i++) {
ToolStripItem item = DisplayedItems[i];
if (item != null) { // CONSIDER ! item is ToolStripControlHost
Rectangle clippingRect = e.ClipRectangle;
Rectangle bounds = item.Bounds;
if (!IsDropDown && item.Owner == this) {
// owned items should not paint outside the client
// area. (this is mainly to prevent obscuring the grip
// and overflowbutton - ToolStripDropDownMenu places items
// outside of the display rectangle - so we need to allow for this
// in dropdoowns).
clippingRect.Intersect(viewableArea);
}
// get the intersection of these two.
clippingRect.Intersect(bounds);
if (LayoutUtils.IsZeroWidthOrHeight(clippingRect)) {
continue; // no point newing up a graphics object if there's nothing to paint.
}
Size itemSize = item.Size;
// check if our item buffer is large enough to handle.
if (!LayoutUtils.AreWidthAndHeightLarger(bitmapSize, itemSize)) {
// the cached HDC isnt big enough for this item. make it bigger.
this.largestDisplayedItemSize = itemSize;
bitmapSize = itemSize;
// dispose the old graphics - create a new, bigger one.
itemGraphics.Dispose();
// calling this should take the existing DC and select in a bigger bitmap.
itemHDC = ItemHdcInfo.GetCachedItemDC(toolStripHDC, bitmapSize);
// allocate a new graphics.
itemGraphics = Graphics.FromHdcInternal(itemHDC.Handle);
}
// since the item graphics object will have 0,0 at the
// corner we need to actually shift the origin of the rect over
// so it will be 0,0 too.
clippingRect.Offset(-bounds.X, -bounds.Y);
// PERF - consider - we only actually need to copy the clipping rect.
// copy the background from the toolstrip onto the offscreen bitmap
SafeNativeMethods.BitBlt(itemHDC, 0, 0, item.Size.Width, item.Size.Height, toolStripHDC, item.Bounds.X, item.Bounds.Y, NativeMethods.SRCCOPY);
// paint the item into the offscreen bitmap
using (PaintEventArgs itemPaintEventArgs = new PaintEventArgs(itemGraphics, clippingRect)) {
item.FireEvent(itemPaintEventArgs, ToolStripItemEventType.Paint);
}
// copy the item back onto the toolstrip
SafeNativeMethods.BitBlt(toolStripHDC, item.Bounds.X, item.Bounds.Y, item.Size.Width, item.Size.Height, itemHDC, 0, 0, NativeMethods.SRCCOPY);
}
}
}
finally {
if (itemGraphics != null) {
itemGraphics.Dispose();
}
}
}
}
// Painting the edge effects...
// These would include things like (shadow line on the bottom, some overflow effects)
Renderer.DrawToolStripBorder(new ToolStripRenderEventArgs(toolstripGraphics, this));
// Restoring the clip region to its original state...
// the transparent region should be added back in as the insertion mark should paint over it.
if (excludedTransparentRegion) {
toolstripGraphics.SetClip(transparentRegion,CombineMode.Union);
}
// Paint the item re-order insertion mark...
// This should ignore the transparent region and paint
// over the entire area.
PaintInsertionMark(toolstripGraphics);
}
finally {
if (transparentRegion != null) {
transparentRegion.Dispose();
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnRightToLeftChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnRightToLeftChanged(EventArgs e) {
base.OnRightToLeftChanged(e);
// normally controls just need to do handle recreation, but ToolStrip does it based on layout of items.
using(new LayoutTransaction(this, this, PropertyNames.RightToLeft)) {
for (int i = 0; i < Items.Count; i++) {
Items[i].OnParentRightToLeftChanged(e);
}
if (toolStripOverflowButton != null) {
toolStripOverflowButton.OnParentRightToLeftChanged(e);
}
if (toolStripGrip != null) {
toolStripGrip.OnParentRightToLeftChanged(e);
}
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnPaintBackground"]/*' />
/// <devdoc>
/// Inheriting classes should override this method to handle the erase
/// background request from windows. It is not necessary to call
/// base.onPaintBackground, however if you do not want the default
/// Windows behavior you must set event.handled to true.
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnPaintBackground(PaintEventArgs e) {
base.OnPaintBackground(e);
Graphics g = e.Graphics;
GraphicsState graphicsState = g.Save();
try {
using (Region transparentRegion = Renderer.GetTransparentRegion(this)) {
if (transparentRegion != null) {
EraseCorners(e, transparentRegion);
g.ExcludeClip(transparentRegion);
}
}
Renderer.DrawToolStripBackground(new ToolStripRenderEventArgs(g, this));
}
finally {
if (graphicsState != null) {
g.Restore(graphicsState);
}
}
}
protected override void OnVisibleChanged(EventArgs e) {
base.OnVisibleChanged(e);
if (!Disposing && !IsDisposed) {
HookStaticEvents(Visible);
}
}
private void EraseCorners(PaintEventArgs e, Region transparentRegion) {
if (transparentRegion != null) {
PaintTransparentBackground(e, ClientRectangle, transparentRegion);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnPaintGrip"]/*' />
/// <devdoc>
/// Summary of OnPaint.
/// </devdoc>
internal protected virtual void OnPaintGrip(System.Windows.Forms.PaintEventArgs e) {
Renderer.DrawGrip(new ToolStripGripRenderEventArgs(e.Graphics, this));
PaintEventHandler handler = (PaintEventHandler)Events[EventPaintGrip];
if (handler != null) handler(this,e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnScroll"]/*' />
protected override void OnScroll(ScrollEventArgs se) {
if (se.Type != ScrollEventType.ThumbTrack && se.NewValue != se.OldValue) {
ScrollInternal(se.OldValue - se.NewValue);
}
base.OnScroll(se);
}
private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) {
switch (e.Category) {
case UserPreferenceCategory.Window:
OnDefaultFontChanged();
break;
case UserPreferenceCategory.General:
InvalidateTextItems();
break;
}
}
protected override void OnTabStopChanged(EventArgs e) {
// VSWhidbey 442518 - SelectNextControl can select non-tabstop things.
// we need to prevent this by changing the value of "CanSelect"
SetStyle(ControlStyles.Selectable, TabStop);
base.OnTabStopChanged(e);
}
/// <summary>
/// When overridden in a derived class, handles rescaling of any magic numbers used in control painting.
/// Must call the base class method to get the current DPI values. This method is invoked only when
/// Application opts-in into the Per-monitor V2 support, targets .NETFX 4.7 and has
/// EnableDpiChangedMessageHandling and EnableDpiChangedHighDpiImprovements config switches turned on.
/// </summary>
/// <param name="deviceDpiOld">Old DPI value</param>
/// <param name="deviceDpiNew">New DPI value</param>
protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) {
base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);
if (DpiHelper.EnableToolStripPerMonitorV2HighDpiImprovements) {
if (deviceDpiOld != deviceDpiNew) {
ToolStripManager.CurrentDpi = deviceDpiNew;
defaultFont = ToolStripManager.DefaultFont;
// We need to take care of this control.
ResetScaling(deviceDpiNew);
// We need to scale the one Grip per ToolStrip as well (if present).
if (toolStripGrip != null) {
toolStripGrip.ToolStrip_RescaleConstants(deviceDpiOld, deviceDpiNew);
}
// We need to delegate this "event" to the Controls/Components, which are
// not directly affected by this, but need to consume.
rescaleConstsCallbackDelegate?.Invoke(deviceDpiOld, deviceDpiNew);
}
}
}
/// <summary>
/// Resets the scaling (only in PerMonitorV2 scenarios).
/// Do only call from code which is quirked with PerMonitorV2 quirks for the ToolStrip.
/// </summary>
/// <param name="newDpi">The new DPI passed by WmDpiChangedBeforeParent.</param>
internal virtual void ResetScaling(int newDpi) {
iconWidth = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, newDpi);
iconHeight = DpiHelper.LogicalToDeviceUnits(ICON_DIMENSION, newDpi);
insertionBeamWidth = DpiHelper.LogicalToDeviceUnits(INSERTION_BEAM_WIDTH, newDpi);
scaledDefaultPadding = DpiHelper.LogicalToDeviceUnits(defaultPadding, newDpi);
scaledDefaultGripMargin = DpiHelper.LogicalToDeviceUnits(defaultGripMargin, newDpi);
imageScalingSize = new Size(iconWidth, iconHeight);
}
/// <devdoc>
/// Paints the I beam when items are being reordered
/// </devdoc>
internal void PaintInsertionMark(Graphics g) {
if (lastInsertionMarkRect != Rectangle.Empty) {
int widthOfBeam = insertionBeamWidth;
if (Orientation == Orientation.Horizontal) {
int start = lastInsertionMarkRect.X;
int verticalBeamStart = start + 2;
// draw two vertical lines
g.DrawLines(SystemPens.ControlText,
new Point[] { new Point(verticalBeamStart, lastInsertionMarkRect.Y), new Point(verticalBeamStart, lastInsertionMarkRect.Bottom-1), // first vertical line
new Point(verticalBeamStart+1, lastInsertionMarkRect.Y), new Point(verticalBeamStart+1, lastInsertionMarkRect.Bottom-1), //second vertical line
});
// then two top horizontal
g.DrawLines(SystemPens.ControlText,
new Point[] { new Point(start, lastInsertionMarkRect.Bottom-1), new Point(start + widthOfBeam-1, lastInsertionMarkRect.Bottom-1), //bottom line
new Point(start+1, lastInsertionMarkRect.Bottom -2), new Point(start + widthOfBeam-2, lastInsertionMarkRect.Bottom-2),//bottom second line
});
// then two bottom horizontal
g.DrawLines(SystemPens.ControlText,
new Point[] { new Point(start, lastInsertionMarkRect.Y), new Point(start + widthOfBeam-1, lastInsertionMarkRect.Y), //top line
new Point(start+1, lastInsertionMarkRect.Y+1), new Point(start + widthOfBeam-2, lastInsertionMarkRect.Y+1)//top second line
});
}
else {
widthOfBeam = insertionBeamWidth;
int start = lastInsertionMarkRect.Y;
int horizontalBeamStart = start + 2;
// draw two horizontal lines
g.DrawLines(SystemPens.ControlText,
new Point[] { new Point(lastInsertionMarkRect.X, horizontalBeamStart), new Point(lastInsertionMarkRect.Right-1, horizontalBeamStart), // first vertical line
new Point(lastInsertionMarkRect.X, horizontalBeamStart+1), new Point(lastInsertionMarkRect.Right-1, horizontalBeamStart+1), //second vertical line
});
// then two left vertical
g.DrawLines(SystemPens.ControlText,
new Point[] { new Point(lastInsertionMarkRect.X, start), new Point(lastInsertionMarkRect.X, start + widthOfBeam-1), //left line
new Point(lastInsertionMarkRect.X+1, start+1), new Point(lastInsertionMarkRect.X+1, start + widthOfBeam-2), //second left line
});
// then two right vertical
g.DrawLines(SystemPens.ControlText,
new Point[] { new Point(lastInsertionMarkRect.Right-1, start), new Point(lastInsertionMarkRect.Right-1, start + widthOfBeam-1), //right line
new Point(lastInsertionMarkRect.Right-2, start+1), new Point(lastInsertionMarkRect.Right-2, start + widthOfBeam-2), //second right line
});
}
}
}
/// <devdoc>
/// Paints the I beam when items are being reordered
/// </devdoc>
internal void PaintInsertionMark(Rectangle insertionRect) {
if (lastInsertionMarkRect != insertionRect) {
ClearInsertionMark();
lastInsertionMarkRect = insertionRect;
this.Invalidate(insertionRect);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public new Control GetChildAtPoint(Point point) {
return base.GetChildAtPoint(point);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public new Control GetChildAtPoint(Point pt, GetChildAtPointSkip skipValue) {
return base.GetChildAtPoint(pt, skipValue);
}
// GetNextControl for ToolStrip should always return null
// we do our own tabbing/etc - this allows us to pretend
// we dont have child controls.
internal override Control GetFirstChildControlInTabOrder(bool forward) {
return null;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.GetItemAt"]/*' />
/// <devdoc>
/// Finds the ToolStripItem contained within a specified client coordinate point
/// If item not found - returns null
/// </devdoc>
public ToolStripItem GetItemAt(int x, int y) {
return GetItemAt(new Point(x,y));
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.GetItemAt1"]/*' />
/// <devdoc>
/// Finds the ToolStripItem contained within a specified client coordinate point
/// If item not found - returns null
/// </devdoc>
public ToolStripItem GetItemAt(Point point) {
Rectangle comparisonRect = new Rectangle(point, onePixel);
Rectangle bounds;
// Check the last item we had the mouse over
if (lastMouseActiveItem != null) {
bounds = lastMouseActiveItem.Bounds;
if (bounds.IntersectsWith(comparisonRect) && lastMouseActiveItem.ParentInternal == this) {
return this.lastMouseActiveItem;
}
}
// Walk the ToolStripItem collection
for (int i = 0; i < this.DisplayedItems.Count; i++) {
if (DisplayedItems[i] == null || DisplayedItems[i].ParentInternal != this) {
continue;
}
bounds = DisplayedItems[i].Bounds;
// inflate the grip so it is easier to access
if (toolStripGrip != null && DisplayedItems[i] == toolStripGrip) {
bounds = LayoutUtils.InflateRect(bounds, GripMargin);
}
if (bounds.IntersectsWith(comparisonRect)) {
return this.DisplayedItems[i];
}
}
return null;
}
private void RestoreFocusInternal(bool wasInMenuMode) {
// VSWhidbey 503500
// This is called from the RestoreFocusFilter. If the state of MenuMode has changed
// since we posted this message, we do not know enough information about whether
// we should exit menu mode.
if (wasInMenuMode == ToolStripManager.ModalMenuFilter.InMenuMode) {
RestoreFocusInternal();
}
}
/// <devdoc> RestoreFocus - returns focus to the control who activated us
/// See comment on SnapFocus
/// </devdoc>
internal void RestoreFocusInternal() {
ToolStripManager.ModalMenuFilter.MenuKeyToggle = false;
ClearAllSelections();
lastMouseDownedItem = null;
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] Someone has called RestoreFocus, exiting MenuMode.");
ToolStripManager.ModalMenuFilter.ExitMenuMode();
if (!IsDropDown) {
// reset menu auto expansion.
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] Setting menu auto expand to false");
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocus] uninstalling RestoreFocusFilter");
// PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could
// get called a lot and we want to have to assert AWP.
Application.ThreadContext.FromCurrent().RemoveMessageFilter(RestoreFocusFilter);
MenuAutoExpand = false;
if (!DesignMode && !TabStop && (Focused || ContainsFocus)) {
RestoreFocus();
}
}
// this matches the case where you click on a toolstrip control host
// then tab off of it, then hit ESC. ESC would "restore focus" and
// we should cancel keyboard activation if this method has cancelled focus.
if (KeyboardActive && !Focused && !ContainsFocus) {
KeyboardActive = false;
}
}
// override if you want to control (when TabStop = false) where the focus returns to
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void RestoreFocus() {
bool focusSuccess = false;
if ((hwndThatLostFocus != IntPtr.Zero) && (hwndThatLostFocus != this.Handle)) {
Control c = Control.FromHandleInternal(hwndThatLostFocus);
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip RestoreFocus]: Will Restore Focus to: " + WindowsFormsUtils.GetControlInformation(hwndThatLostFocus));
hwndThatLostFocus = IntPtr.Zero;
if ((c != null) && c.Visible) {
focusSuccess = c.FocusInternal();
}
}
hwndThatLostFocus = IntPtr.Zero;
if (!focusSuccess) {
// clear out the focus, we have focus, we're not supposed to anymore.
UnsafeNativeMethods.SetFocus(NativeMethods.NullHandleRef);
}
}
internal virtual void ResetRenderMode() {
RenderMode = ToolStripRenderMode.ManagerRenderMode;
}
/// <include file='doc\WinBar.uex' path='docs/doc[@for="ToolStrip.ResetMinimumSize"]/*' />
[EditorBrowsable(EditorBrowsableState.Never)]
public void ResetMinimumSize() {
CommonProperties.SetMinimumSize(this, new Size(-1,-1));
}
private void ResetGripMargin() {
GripMargin = Grip.DefaultMargin;
}
internal void ResumeCaputureMode() {
SetToolStripState(STATE_SUSPENDCAPTURE, false);
}
internal void SuspendCaputureMode() {
SetToolStripState(STATE_SUSPENDCAPTURE, true);
}
internal virtual void ScrollInternal(int delta) {
SuspendLayout();
foreach (ToolStripItem item in this.Items) {
Point newLocation = item.Bounds.Location;
newLocation.Y -= delta;
SetItemLocation(item, newLocation);
}
ResumeLayout(false);
Invalidate();
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.SetItemLocation"]/*' />
/// <devdoc>
/// Summary of SetItemLocation
/// </devdoc>
/// <param name=m></param>
protected internal void SetItemLocation(ToolStripItem item, Point location) {
if (item == null) {
throw new ArgumentNullException("item");
}
if (item.Owner != this) {
throw new NotSupportedException(SR.GetString(SR.ToolStripCanOnlyPositionItsOwnItems));
}
item.SetBounds(new Rectangle(location, item.Size));
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.SetItemParent"]/*' />
/// <devdoc>
/// This is needed so that people doing custom layout engines can change the "Parent" property of the item.
/// </devdoc>
protected static void SetItemParent(ToolStripItem item, ToolStrip parent) {
item.Parent = parent;
}
protected override void SetVisibleCore(bool visible) {
if (visible) {
SnapMouseLocation();
}
else {
// make sure we reset selection - this is critical for close/reopen dropdowns.
if (!Disposing && !IsDisposed) {
ClearAllSelections();
}
// when we're not visible, clear off old item HDC.
CachedItemHdcInfo lastInfo = cachedItemHdcInfo;
cachedItemHdcInfo = null;
lastMouseDownedItem = null;
if (lastInfo != null) {
lastInfo.Dispose();
}
}
base.SetVisibleCore(visible);
}
internal bool ShouldSelectItem() {
// we only want to select the item if the cursor position has
// actually moved from when the window became visible.
// We ALWAYS get a WM_MOUSEMOVE when the window is shown,
// which could accidentally change selection.
if (mouseEnterWhenShown == InvalidMouseEnter) {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] MouseEnter already reset.");
return true;
}
Point mousePosition = WindowsFormsUtils.LastCursorPoint;
if (mouseEnterWhenShown != mousePosition) {
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] Mouse position has changed - call Select().");
mouseEnterWhenShown = InvalidMouseEnter;
return true;
}
Debug.WriteLineIf(ToolStripItem.MouseDebugging.TraceVerbose, "[TS: ShouldSelectItem] Mouse hasnt actually moved yet.");
return false;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.Select"]/*' />
/// <devdoc>
/// Summary of Select.
/// </devdoc>
/// <param name=directed></param>
/// <param name=forward></param>
protected override void Select(bool directed, bool forward) {
bool correctParentActiveControl = true;
if (ParentInternal != null) {
IContainerControl c = ParentInternal.GetContainerControlInternal();
if (c != null) {
c.ActiveControl = this;
correctParentActiveControl = (c.ActiveControl == this);
}
}
if (directed && correctParentActiveControl) {
SelectNextToolStripItem(null, forward);
}
}
/// <devdoc>
/// Summary of SelectNextToolStripItem.
///
internal ToolStripItem SelectNextToolStripItem(ToolStripItem start, bool forward) {
ToolStripItem nextItem = GetNextItem(start, (forward) ? ArrowDirection.Right : ArrowDirection.Left, /*RTLAware=*/true);
ChangeSelection(nextItem);
return nextItem;
}
//
// SECREVIEW: only call from places protected by a link demand for AllWindowsPermission.
//
internal void SetFocusUnsafe() {
if (TabStop) {
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose,"[ToolStrip.SetFocus] Focusing toolstrip.");
FocusInternal();
}
else {
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose,"[ToolStrip.SetFocus] Entering menu mode.");
ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this, /*menuKeyPressed=*/false);
}
}
private void SetupGrip() {
Rectangle gripRectangle = Rectangle.Empty;
Rectangle displayRect = DisplayRectangle;
if (Orientation == Orientation.Horizontal) {
// the display rectangle already knows about the padding and the grip rectangle width
// so place it relative to that.
gripRectangle.X = Math.Max(0, displayRect.X - Grip.GripThickness);
gripRectangle.Y = Math.Max(0,displayRect.Top - Grip.Margin.Top);
gripRectangle.Width = Grip.GripThickness;
gripRectangle.Height = displayRect.Height;
if (RightToLeft == RightToLeft.Yes) {
gripRectangle.X = ClientRectangle.Right - gripRectangle.Width - Grip.Margin.Horizontal;
gripRectangle.X += Grip.Margin.Left;
}
else {
gripRectangle.X -= Grip.Margin.Right;
}
}
else {
// vertical split stack mode
gripRectangle.X = displayRect.Left;
gripRectangle.Y = displayRect.Top - (Grip.GripThickness + Grip.Margin.Bottom);
gripRectangle.Width = displayRect.Width;
gripRectangle.Height = Grip.GripThickness;
}
if (Grip.Bounds !=gripRectangle) {
Grip.SetBounds(gripRectangle);
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.SetAutoScrollMargin"]/*' />
/// <devdoc>
/// <para>
/// Sets the size of the auto-scroll margins.
/// </para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
new public void SetAutoScrollMargin(int x, int y) {
base.SetAutoScrollMargin(x, y);
}
internal void SetLargestItemSize(Size size) {
if (toolStripOverflowButton != null && toolStripOverflowButton.Visible) {
size = LayoutUtils.UnionSizes(size, toolStripOverflowButton.Bounds.Size);
}
if (toolStripGrip != null && toolStripGrip.Visible) {
size = LayoutUtils.UnionSizes(size, toolStripGrip.Bounds.Size);
}
largestDisplayedItemSize = size;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.SetDisplayedItems"]/*' />
/// <devdoc>
/// Afer we've performed a layout we need to reset the DisplayedItems and the OverflowItems collection.
/// OverflowItems are not supported in layouts other than ToolStripSplitStack
/// </devdoc>
protected virtual void SetDisplayedItems() {
this.DisplayedItems.Clear();
this.OverflowItems.Clear();
HasVisibleItems = false;
Size biggestItemSize = Size.Empty; // used in determining OnPaint caching.
if (this.LayoutEngine is ToolStripSplitStackLayout) {
if (ToolStripGripStyle.Visible == GripStyle) {
this.DisplayedItems.Add(Grip);
SetupGrip();
}
// VSWhidbey 468104
// for splitstack layout we re-arrange the items in the displayed items
// collection so that we can easily tab through them in natural order
Rectangle displayRect = this.DisplayRectangle;
int lastRightAlignedItem = -1;
for (int pass=0; pass < 2; pass++) {
int j = 0;
if (pass == 1 /*add right aligned items*/) {
j = lastRightAlignedItem;
}
// add items to the DisplayedItem collection.
// in pass 0, we go forward adding the head (left) aligned items
// in pass 1, we go backward starting from the last (right) aligned item we found
for (; j >= 0 && j < Items.Count; j = (pass == 0) ? j+1 : j-1){
ToolStripItem item = Items[j];
ToolStripItemPlacement placement = item.Placement;
if (((IArrangedElement)item).ParticipatesInLayout) {
if (placement == ToolStripItemPlacement.Main) {
bool addItem = false;
if (pass == 0) { // Align.Left items
addItem = (item.Alignment == ToolStripItemAlignment.Left);
if (!addItem) {
// stash away this index so we dont have to iterate through the whole list again.
lastRightAlignedItem = j;
}
}
else if (pass == 1) { // Align.Right items
addItem = (item.Alignment == ToolStripItemAlignment.Right);
}
if (addItem) {
HasVisibleItems = true;
biggestItemSize = LayoutUtils.UnionSizes(biggestItemSize, item.Bounds.Size);
this.DisplayedItems.Add(item);
}
}
else if (placement == ToolStripItemPlacement.Overflow && !(item is ToolStripSeparator)) {
if (item is ToolStripControlHost && this.OverflowButton.DropDown.IsRestrictedWindow) {
// VSWhidbey 436973: control hosts cannot be added to the overflow in the Internet
// just set the placement to None.
item.SetPlacement(ToolStripItemPlacement.None);
}
else {
this.OverflowItems.Add(item);
}
}
}
else {
item.SetPlacement(ToolStripItemPlacement.None);
}
}
}
ToolStripOverflow overflow = GetOverflow();
if (overflow != null) {
overflow.LayoutRequired = true;
}
if (OverflowItems.Count ==0) {
this.OverflowButton.Visible = false;
}
else if (CanOverflow){
this.DisplayedItems.Add(OverflowButton);
}
}
else {
// NOT a SplitStack layout. We dont change the order of the displayed items collection
// for custom keyboard handling override GetNextItem.
Debug.WriteLineIf(LayoutDebugSwitch.TraceVerbose, "Setting Displayed Items: Current bounds: " + this.Bounds.ToString());
Rectangle clientBounds = this.ClientRectangle;
// for all other layout managers, we ignore overflow placement
bool allContained = true;
for (int j = 0; j < Items.Count; j++) {
ToolStripItem item = Items[j];
if (((IArrangedElement)item).ParticipatesInLayout)
{
item.ParentInternal = this;
bool boundsCheck = !IsDropDown;
bool intersects = item.Bounds.IntersectsWith(clientBounds);
bool verticallyContained = clientBounds.Contains(clientBounds.X, item.Bounds.Top) &&
clientBounds.Contains(clientBounds.X, item.Bounds.Bottom);
if (!verticallyContained) {
allContained = false;
}
if (!boundsCheck || intersects) {
HasVisibleItems = true;
biggestItemSize = LayoutUtils.UnionSizes(biggestItemSize, item.Bounds.Size);
this.DisplayedItems.Add(item);
item.SetPlacement(ToolStripItemPlacement.Main);
}
}
else {
item.SetPlacement(ToolStripItemPlacement.None);
}
Debug.WriteLineIf(LayoutDebugSwitch.TraceVerbose, item.ToString() + Items[j].Bounds);
}
// For performance we calculate this here, since we're already iterating over the items.
// the only one who cares about it is ToolStripDropDownMenu (to see if it needs scroll buttons).
this.AllItemsVisible = allContained;
}
SetLargestItemSize(biggestItemSize);
}
/// <devdoc>
/// Sets the current value of the specified bit in the control's state.
/// </devdoc>
internal void SetToolStripState(int flag, bool value) {
toolStripState = value? toolStripState | flag: toolStripState & ~flag;
}
// remembers the current mouse location so we can determine
// later if we need to shift selection.
internal void SnapMouseLocation() {
mouseEnterWhenShown = WindowsFormsUtils.LastCursorPoint;
}
/// <devdoc> SnapFocus
/// When get focus to the toolstrip (and we're not participating in the tab order)
/// it's probably cause someone hit the ALT key. We need to remember who that was
/// so when we're done here we can RestoreFocus back to it.
///
/// We're called from WM_SETFOCUS, and otherHwnd is the HWND losing focus.
///
/// Required checks
/// - make sure it's not a dropdown
/// - make sure it's not a child control of this control.
/// - make sure the control is on this window
/// </devdoc>
private void SnapFocus(IntPtr otherHwnd) {
#if DEBUG
if (SnapFocusDebug.TraceVerbose) {
string stackTrace = new StackTrace().ToString();
Regex regex = new Regex("FocusInternal");
Debug.WriteLine(!regex.IsMatch(stackTrace), "who is setting focus to us?");
}
#endif
// we need to know who sent us focus so we know who to send it back to later.
if (!TabStop && !IsDropDown) {
bool snapFocus = false;
if (Focused && (otherHwnd != this.Handle)) {
// the case here is a label before a combo box calling FocusInternal in ProcessMnemonic.
// we'll filter out children later.
snapFocus = true;
}
else if (!ContainsFocus && !Focused) {
snapFocus =true;
}
if (snapFocus) {
// remember the current mouse position so that we can check later if it actually moved
// otherwise we'd unexpectedly change selection to whatever the cursor was over at this moment.
SnapMouseLocation();
// start auto expanding for keyboard and mouse.
// MenuAutoExpand = true;
HandleRef thisHandle = new HandleRef(this, this.Handle);
HandleRef otherHandle = new HandleRef(null, otherHwnd);
// make sure the otherHandle is not a child of thisHandle
if ((thisHandle.Handle != otherHandle.Handle) &&
!UnsafeNativeMethods.IsChild(thisHandle, otherHandle)) {
// make sure the root window of the otherHwnd is the same as
// the root window of thisHwnd.
HandleRef thisHwndRoot = WindowsFormsUtils.GetRootHWnd(this);
HandleRef otherHwndRoot = WindowsFormsUtils.GetRootHWnd(otherHandle);
if (thisHwndRoot.Handle == otherHwndRoot.Handle && (thisHwndRoot.Handle != IntPtr.Zero)) {
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip SnapFocus]: Caching for return focus:" + WindowsFormsUtils.GetControlInformation(otherHandle.Handle));
// we know we're in the same window heirarchy.
hwndThatLostFocus = otherHandle.Handle;
}
}
}
}
}
// when we're control tabbing around we need to remember the original
// thing that lost focus.
internal void SnapFocusChange(ToolStrip otherToolStrip) {
otherToolStrip.hwndThatLostFocus = this.hwndThatLostFocus;
}
private bool ShouldSerializeDefaultDropDownDirection() {
return (toolStripDropDownDirection != ToolStripDropDownDirection.Default);
}
internal virtual bool ShouldSerializeLayoutStyle() {
return layoutStyle != ToolStripLayoutStyle.StackWithOverflow;
}
internal override bool ShouldSerializeMinimumSize() {
Size invalidDefaultSize = new Size(-1,-1);
return (CommonProperties.GetMinimumSize(this, invalidDefaultSize) != invalidDefaultSize);
}
private bool ShouldSerializeGripMargin() {
return GripMargin != DefaultGripMargin;
}
internal virtual bool ShouldSerializeRenderMode() {
// We should NEVER serialize custom.
return (RenderMode != ToolStripRenderMode.ManagerRenderMode && RenderMode != ToolStripRenderMode.Custom);
}
public override string ToString() {
StringBuilder sb = new StringBuilder(base.ToString());
sb.Append(", Name: ");
sb.Append(this.Name);
sb.Append(", Items: ").Append(this.Items.Count);
return sb.ToString();
}
internal void UpdateToolTip(ToolStripItem item) {
if (ShowItemToolTips) {
if (item != currentlyActiveTooltipItem && ToolTip != null) {
// SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone
IntSecurity.AllWindows.Assert();
try {
ToolTip.Hide(this);
}
finally {
System.Security.CodeAccessPermission.RevertAssert();
}
if (AccessibilityImprovements.UseLegacyToolTipDisplay) {
ToolTip.Active = false;
}
currentlyActiveTooltipItem = item;
if (currentlyActiveTooltipItem != null && !GetToolStripState(STATE_DRAGGING)) {
Cursor currentCursor = Cursor.CurrentInternal;
if (currentCursor != null) {
if (AccessibilityImprovements.UseLegacyToolTipDisplay) {
ToolTip.Active = true;
}
Point cursorLocation = Cursor.Position;
cursorLocation.Y += Cursor.Size.Height - currentCursor.HotSpot.Y;
cursorLocation = WindowsFormsUtils.ConstrainToScreenBounds(new Rectangle(cursorLocation, onePixel)).Location;
// SECREVIEW: VSWhidbey 531915 - ToolTip should show in internet zone
IntSecurity.AllWindows.Assert();
try {
ToolTip.Show(currentlyActiveTooltipItem.ToolTipText,
this,
PointToClient(cursorLocation),
ToolTip.AutoPopDelay);
}
finally {
System.Security.CodeAccessPermission.RevertAssert();
}
}
}
}
}
}
private void UpdateLayoutStyle(DockStyle newDock) {
if (!IsInToolStripPanel && layoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow && layoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) {
using (new LayoutTransaction(this, this, PropertyNames.Orientation)) {
//
// We want the ToolStrip to size appropriately when the dock has switched.
//
if (newDock == DockStyle.Left || newDock == DockStyle.Right) {
UpdateOrientation(Orientation.Vertical);
}
else {
UpdateOrientation(Orientation.Horizontal);
}
}
OnLayoutStyleChanged(EventArgs.Empty);
if (this.ParentInternal != null) {
LayoutTransaction.DoLayout(this.ParentInternal, this, PropertyNames.Orientation);
}
}
}
private void UpdateLayoutStyle(Orientation newRaftingRowOrientation) {
if (layoutStyle != ToolStripLayoutStyle.HorizontalStackWithOverflow && layoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow) {
using (new LayoutTransaction(this, this, PropertyNames.Orientation)) {
//
// We want the ToolStrip to size appropriately when the rafting container orientation has switched.
//
/* if (newRaftingRowOrientation != orientation) {
int oldHeight = this.Height;
this.Height = this.Width;
this.Width = oldHeight;
}*/
UpdateOrientation(newRaftingRowOrientation);
if (LayoutEngine is ToolStripSplitStackLayout && layoutStyle == ToolStripLayoutStyle.StackWithOverflow) {
OnLayoutStyleChanged(EventArgs.Empty);
}
}
}
else {
// update the orientation but dont force a layout.
UpdateOrientation(newRaftingRowOrientation);
}
}
private void UpdateOrientation(Orientation newOrientation) {
if (newOrientation != orientation) {
// snap our last dimensions before switching over.
// use specifed bounds so that if something is docked or anchored we dont take the extra stretching
// effects into account.
Size size = CommonProperties.GetSpecifiedBounds(this).Size;
orientation = newOrientation;
// since the Grip affects the DisplayRectangle, we need to re-adjust the size
SetupGrip();
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.WndProc"]/*' />
/// <devdoc>
/// Summary of WndProc.
/// </devdoc>
/// <param name=m></param>
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m) {
if (m.Msg == NativeMethods.WM_SETFOCUS) {
SnapFocus(m.WParam);
}
if (m.Msg == NativeMethods.WM_MOUSEACTIVATE) {
// we want to prevent taking focus if someone clicks on the toolstrip dropdown
// itself. the mouse message will still go through, but focus wont be taken.
// if someone clicks on a child control (combobox, textbox, etc) focus will
// be taken - but we'll handle that in WM_NCACTIVATE handler.
Point pt = PointToClient(WindowsFormsUtils.LastCursorPoint);
IntPtr hwndClicked = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, Handle), pt.X, pt.Y,(int)(GetChildAtPointSkip.Invisible | GetChildAtPointSkip.Disabled | GetChildAtPointSkip.Transparent));
// if we click on the toolstrip itself, eat the activation.
// if we click on a child control, allow the toolstrip to activate.
if (hwndClicked == this.Handle) {
lastMouseDownedItem = null;
m.Result = (IntPtr)NativeMethods.MA_NOACTIVATE;
if (!IsDropDown && !IsInDesignMode) {
// VSWhidbey 473357: if our root HWND is not the active hwnd,
// eat the mouse message and bring the form to the front.
HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(this);
if (rootHwnd.Handle != IntPtr.Zero) {
// snap the active window and compare to our root window.
IntPtr hwndActive = UnsafeNativeMethods.GetActiveWindow();
if (hwndActive != rootHwnd.Handle) {
// Activate the window, and discard the mouse message.
// this appears to be the same behavior as office.
m.Result = (IntPtr)NativeMethods.MA_ACTIVATEANDEAT;
}
}
}
return;
}
else {
// we're setting focus to a child control - remember who gave it to us
// so we can restore it on ESC.
SnapFocus(UnsafeNativeMethods.GetFocus());
if (!IsDropDown && !TabStop) {
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "Installing restoreFocusFilter");
// PERF, SECREVIEW: dont call Application.AddMessageFilter as this could
// get called a lot and we want to have to assert AWP.
Application.ThreadContext.FromCurrent().AddMessageFilter(RestoreFocusFilter);
}
}
}
base.WndProc(ref m);
if (m.Msg == NativeMethods.WM_NCDESTROY) {
// Destroy the owner window, if we created one. We
// cannot do this in OnHandleDestroyed, because at
// that point our handle is not actually destroyed so
// destroying our parent actually causes a recursive
// WM_DESTROY.
if (dropDownOwnerWindow != null) {
dropDownOwnerWindow.DestroyHandle();
}
}
}
// Overriden to return Items instead of Controls.
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.IArrangedElement.Children"]/*' />
/// <internalonly/>
ArrangedElementCollection IArrangedElement.Children {
get { return Items; }
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.IArrangedElement.SetBounds"]/*' />
/// <internalonly/>
void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified) {
SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="Control.IArrangedElement.ParticipatesInLayout"]/*' />
/// <internalonly/>
bool IArrangedElement.ParticipatesInLayout {
get { return GetState(STATE_VISIBLE);}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.CreateAccessibilityInstance"]/*' />
protected override AccessibleObject CreateAccessibilityInstance() {
return new ToolStripAccessibleObject(this);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.CreateControlsInstance"]/*' />
protected override Control.ControlCollection CreateControlsInstance() {
return new WindowsFormsUtils.ReadOnlyControlCollection(this, /* isReadOnly = */ !DesignMode);
}
internal void OnItemAddedInternal(ToolStripItem item) {
if (!AccessibilityImprovements.UseLegacyToolTipDisplay) {
if (this.ShowItemToolTips) {
KeyboardToolTipStateMachine.Instance.Hook(item, this.ToolTip);
}
}
}
internal void OnItemRemovedInternal(ToolStripItem item) {
if (!AccessibilityImprovements.UseLegacyToolTipDisplay) {
KeyboardToolTipStateMachine.Instance.Unhook(item, this.ToolTip);
}
}
internal override bool AllowsChildrenToShowToolTips() {
return base.AllowsChildrenToShowToolTips() && this.ShowItemToolTips;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStripAccessibleObject"]/*' />
[System.Runtime.InteropServices.ComVisible(true)]
public class ToolStripAccessibleObject : ControlAccessibleObject {
private ToolStrip owner;
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStripAccessibleObject.ToolStripAccessibleObject"]/*' />
public ToolStripAccessibleObject(ToolStrip owner) : base(owner) {
this.owner = owner;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStripAccessibleObject.HitTest"]/*' />
/// <devdoc>
/// <para>Return the child object at the given screen coordinates.</para>
/// </devdoc>
public override AccessibleObject HitTest(int x, int y) {
Point clientHit = owner.PointToClient(new Point(x,y));
ToolStripItem item = owner.GetItemAt(clientHit);
return ((item != null) && (item.AccessibilityObject != null)) ?
item.AccessibilityObject :
base.HitTest(x,y);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStripAccessibleObject.GetChild"]/*' />
/// <devdoc>
/// <para>When overridden in a derived class, gets the accessible child corresponding to the specified
/// index.</para>
/// </devdoc>
//
public override AccessibleObject GetChild(int index) {
if ((owner == null) || (owner.Items == null))
return null;
if (index == 0 && owner.Grip.Visible) {
return owner.Grip.AccessibilityObject;
}
else if (owner.Grip.Visible && index > 0) {
index--;
}
if (index < owner.Items.Count) {
ToolStripItem item = null;
int myIndex = 0;
// First we walk through the head aligned items.
for (int i = 0; i < owner.Items.Count; ++i)
{
if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Left) {
if (myIndex == index) {
item = owner.Items[i];
break;
}
myIndex++;
}
}
// If we didn't find it, then we walk through the tail aligned items.
if (item == null) {
for (int i = 0; i < owner.Items.Count; ++i) {
if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Right) {
if (myIndex == index) {
item = owner.Items[i];
break;
}
myIndex++;
}
}
}
if (item == null) {
Debug.Fail("No item matched the index??");
return null;
}
if (item.Placement == ToolStripItemPlacement.Overflow) {
return new ToolStripAccessibleObjectWrapperForItemsOnOverflow(item);
}
return item.AccessibilityObject;
}
if (owner.CanOverflow && owner.OverflowButton.Visible && index == owner.Items.Count) {
return owner.OverflowButton.AccessibilityObject;
}
return null;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStripAccessibleObject.GetChildCount"]/*' />
/// <devdoc>
/// <para> When overridden in a derived class, gets the number of children
/// belonging to an accessible object.</para>
/// </devdoc>
public override int GetChildCount() {
if ((owner == null) || (owner.Items == null))
return -1;
int count = 0;
for (int i = 0; i < owner.Items.Count; i++) {
if (owner.Items[i].Available) {
count++;
}
}
if (owner.Grip.Visible) {
count++;
}
if (owner.CanOverflow && owner.OverflowButton.Visible) {
count++;
}
return count;
}
internal AccessibleObject GetChildFragment(int fragmentIndex, bool getOverflowItem = false) {
var items = getOverflowItem ? owner.OverflowItems : owner.DisplayedItems;
int childFragmentCount = items.Count;
if (!getOverflowItem && owner.CanOverflow && owner.OverflowButton.Visible && fragmentIndex == childFragmentCount - 1) {
return owner.OverflowButton.AccessibilityObject;
}
for (int index = 0; index < childFragmentCount; index++) {
var item = items[index];
if (item.Available && item.Alignment == ToolStripItemAlignment.Left && fragmentIndex == index) {
var controlHostItem = item as ToolStripControlHost;
if (controlHostItem != null) {
return controlHostItem.ControlAccessibilityObject;
}
return item.AccessibilityObject;
}
}
for (int index = 0; index < childFragmentCount; index++) {
var item = owner.Items[index];
if (item.Available && item.Alignment == ToolStripItemAlignment.Right && fragmentIndex == index) {
var controlHostItem = item as ToolStripControlHost;
if (controlHostItem != null) {
return controlHostItem.ControlAccessibilityObject;
}
return item.AccessibilityObject;
}
}
return null;
}
internal int GetChildOverflowFragmentCount() {
if (owner == null || owner.OverflowItems == null) {
return -1;
}
return owner.OverflowItems.Count;
}
internal int GetChildFragmentCount() {
if (owner == null || owner.DisplayedItems == null) {
return -1;
}
return owner.DisplayedItems.Count;
}
internal int GetChildFragmentIndex(ToolStripItem.ToolStripItemAccessibleObject child) {
if (owner == null || owner.Items == null) {
return -1;
}
if (child.Owner == owner.Grip) {
return 0;
}
ToolStripItemCollection items;
var placement = child.Owner.Placement;
if (owner is ToolStripOverflow) {
// Overflow items in ToolStripOverflow host are in DisplayedItems collection.
items = owner.DisplayedItems;
}
else {
if (owner.CanOverflow && owner.OverflowButton.Visible && child.Owner == owner.OverflowButton) {
return GetChildFragmentCount() - 1;
}
// Items can be either in DisplayedItems or in OverflowItems (if overflow)
items = (placement == ToolStripItemPlacement.Main) ? owner.DisplayedItems : owner.OverflowItems;
}
// First we walk through the head aligned items.
for (int index = 0; index < items.Count; index++) {
var item = items[index];
if (item.Available && item.Alignment == ToolStripItemAlignment.Left && child.Owner == items[index]) {
return index;
}
}
// If we didn't find it, then we walk through the tail aligned items.
for (int index = 0; index < items.Count; index++) {
var item = items[index];
if (item.Available && item.Alignment == ToolStripItemAlignment.Right && child.Owner == items[index]) {
return index;
}
}
return -1;
}
internal int GetChildIndex(ToolStripItem.ToolStripItemAccessibleObject child) {
if ((owner == null) || (owner.Items == null)) {
return -1;
}
int index = 0;
if (owner.Grip.Visible) {
if (child.Owner == owner.Grip) {
return 0;
}
index = 1;
}
if (owner.CanOverflow && owner.OverflowButton.Visible && child.Owner == owner.OverflowButton) {
return owner.Items.Count + index;
}
// First we walk through the head aligned items.
for (int i = 0; i < owner.Items.Count; ++i) {
if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Left) {
if (child.Owner == owner.Items[i]) {
return index;
}
index++;
}
}
// If we didn't find it, then we walk through the tail aligned items.
for (int i = 0; i < owner.Items.Count; ++i) {
if (owner.Items[i].Available && owner.Items[i].Alignment == ToolStripItemAlignment.Right) {
if (child.Owner == owner.Items[i]) {
return index;
}
index++;
}
}
return -1;
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStripAccessibleObject.Role"]/*' />
public override AccessibleRole Role {
get {
AccessibleRole role = Owner.AccessibleRole;
if (role != AccessibleRole.Default) {
return role;
}
return AccessibleRole.ToolBar;
}
}
internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot {
get {
return this;
}
}
internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
if (AccessibilityImprovements.Level3) {
switch (direction) {
case UnsafeNativeMethods.NavigateDirection.FirstChild:
int childCount = GetChildFragmentCount();
if (childCount > 0) {
return this.GetChildFragment(0);
}
break;
case UnsafeNativeMethods.NavigateDirection.LastChild:
childCount = GetChildFragmentCount();
if (childCount > 0) {
return this.GetChildFragment(childCount - 1);
}
break;
}
}
return base.FragmentNavigate(direction);
}
internal override object GetPropertyValue(int propertyID) {
if (AccessibilityImprovements.Level3) {
if (propertyID == NativeMethods.UIA_ControlTypePropertyId) {
return NativeMethods.UIA_ToolBarControlTypeId;
}
}
return base.GetPropertyValue(propertyID);
}
internal override bool IsIAccessibleExSupported()
{
return AccessibilityImprovements.Level3 && !owner.IsInDesignMode && !owner.IsTopInDesignMode;
}
}
private class ToolStripAccessibleObjectWrapperForItemsOnOverflow : ToolStripItem.ToolStripItemAccessibleObject {
public ToolStripAccessibleObjectWrapperForItemsOnOverflow(ToolStripItem item)
: base(item) {
}
public override AccessibleStates State {
get {
AccessibleStates state = base.State;
state |= AccessibleStates.Offscreen;
state |= AccessibleStates.Invisible;
return state;
}
}
}
// When we click somewhere outside of the toolstrip it should be as if we hit esc.
internal class RestoreFocusMessageFilter : IMessageFilter {
private ToolStrip ownerToolStrip;
public RestoreFocusMessageFilter(ToolStrip ownerToolStrip) {
this.ownerToolStrip = ownerToolStrip;
}
public bool PreFilterMessage(ref Message m) {
if (ownerToolStrip.Disposing || ownerToolStrip.IsDisposed || ownerToolStrip.IsDropDown) {
return false;
}
// if the app has changed activation, restore focus
switch (m.Msg) {
case NativeMethods.WM_LBUTTONDOWN:
case NativeMethods.WM_RBUTTONDOWN:
case NativeMethods.WM_MBUTTONDOWN:
case NativeMethods.WM_NCLBUTTONDOWN:
case NativeMethods.WM_NCRBUTTONDOWN:
case NativeMethods.WM_NCMBUTTONDOWN:
if (ownerToolStrip.ContainsFocus) {
// if we've clicked on something that's not a child of the toolstrip and we
// currently have focus, restore it.
if (!UnsafeNativeMethods.IsChild(new HandleRef(this, ownerToolStrip.Handle), new HandleRef(this,m.HWnd))) {
HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(ownerToolStrip);
if (rootHwnd.Handle == m.HWnd || UnsafeNativeMethods.IsChild(rootHwnd, new HandleRef(this,m.HWnd))) {
// Only RestoreFocus if the hwnd is a child of the root window and isnt on the toolstrip.
RestoreFocusInternal();
}
}
}
return false;
default:
return false;
}
}
private void RestoreFocusInternal() {
Debug.WriteLineIf(SnapFocusDebug.TraceVerbose, "[ToolStrip.RestoreFocusFilter] Detected a click, restoring focus.");
ownerToolStrip.BeginInvoke(new BooleanMethodInvoker(ownerToolStrip.RestoreFocusInternal), new object[]{ ToolStripManager.ModalMenuFilter.InMenuMode } );
// PERF, SECREVIEW: dont call Application.RemoveMessageFilter as this could
// get called a lot and we want to have to assert AWP.
Application.ThreadContext.FromCurrent().RemoveMessageFilter(this);
}
}
internal override bool ShowsOwnKeyboardToolTip() {
bool hasVisibleSelectableItems = false;
int i = this.Items.Count;
while (i-- != 0 && !hasVisibleSelectableItems) {
ToolStripItem item = this.Items[i];
if (item.CanKeyboardSelect && item.Visible) {
hasVisibleSelectableItems = true;
}
}
return !hasVisibleSelectableItems;
}
}
internal class CachedItemHdcInfo : IDisposable {
internal CachedItemHdcInfo() {
}
~CachedItemHdcInfo() {
Dispose();
}
private HandleRef cachedItemHDC = NativeMethods.NullHandleRef;
private Size cachedHDCSize = Size.Empty;
private HandleRef cachedItemBitmap = NativeMethods.NullHandleRef;
// this DC is cached and should only be deleted on Dispose or when the size changes.
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public HandleRef GetCachedItemDC(HandleRef toolStripHDC, Size bitmapSize) {
if ((cachedHDCSize.Width < bitmapSize.Width)
|| (cachedHDCSize.Height < bitmapSize.Height)) {
if (cachedItemHDC.Handle == IntPtr.Zero) {
// create a new DC - we dont have one yet.
IntPtr compatibleHDC = UnsafeNativeMethods.CreateCompatibleDC(toolStripHDC);
cachedItemHDC = new HandleRef(this, compatibleHDC);
}
// create compatible bitmap with the correct size.
cachedItemBitmap = new HandleRef(this, SafeNativeMethods.CreateCompatibleBitmap(toolStripHDC, bitmapSize.Width, bitmapSize.Height));
IntPtr oldBitmap = SafeNativeMethods.SelectObject(cachedItemHDC,cachedItemBitmap);
// delete the old bitmap
if (oldBitmap != IntPtr.Zero) {
// ExternalDelete to prevent Handle underflow
SafeNativeMethods.ExternalDeleteObject(new HandleRef(null, oldBitmap));
oldBitmap = IntPtr.Zero;
}
// remember what size we created.
cachedHDCSize = bitmapSize;
}
return cachedItemHDC;
}
private void DeleteCachedItemHDC() {
if (cachedItemHDC.Handle != IntPtr.Zero) {
// delete the bitmap
if (cachedItemBitmap.Handle != IntPtr.Zero) {
SafeNativeMethods.DeleteObject(cachedItemBitmap);
cachedItemBitmap = NativeMethods.NullHandleRef;
}
// delete the DC itself.
UnsafeNativeMethods.DeleteCompatibleDC(cachedItemHDC);
}
cachedItemHDC = NativeMethods.NullHandleRef;
cachedItemBitmap = NativeMethods.NullHandleRef;
cachedHDCSize = Size.Empty;
}
public void Dispose() {
DeleteCachedItemHDC();
GC.SuppressFinalize(this);
}
}
internal class MouseHoverTimer : IDisposable {
private System.Windows.Forms.Timer mouseHoverTimer = new System.Windows.Forms.Timer();
private const int SPI_GETMOUSEHOVERTIME_WIN9X = 400; // in Win9x this is not supported so lets use the default from a more modern OS.
// consider - weak reference?
private ToolStripItem currentItem = null;
public MouseHoverTimer() {
int interval = SystemInformation.MouseHoverTime;
if (interval == 0) {
interval = SPI_GETMOUSEHOVERTIME_WIN9X;
}
mouseHoverTimer.Interval = interval;
mouseHoverTimer.Tick += new EventHandler(OnTick);
}
public void Start(ToolStripItem item) {
if (item != currentItem) {
Cancel(currentItem);
}
currentItem = item;
if (currentItem != null) {
mouseHoverTimer.Enabled = true;
}
}
public void Cancel() {
mouseHoverTimer.Enabled = false;
currentItem = null;
}
///<devdoc> cancels if and only if this item was the one that
/// requested the timer
///</devdoc>
public void Cancel(ToolStripItem item) {
if (item == currentItem) {
Cancel();
}
}
public void Dispose() {
if (mouseHoverTimer != null) {
Cancel();
mouseHoverTimer.Dispose();
mouseHoverTimer = null;
}
}
private void OnTick(object sender, EventArgs e) {
mouseHoverTimer.Enabled = false;
if (currentItem != null && !currentItem.IsDisposed) {
currentItem.FireEvent(EventArgs.Empty,ToolStripItemEventType.MouseHover);
}
}
}
/// <devdoc/>
/// This class supports the AllowItemReorder feature.
/// When reordering items ToolStrip and ToolStripItem drag/drop events
/// are routed here.
/// </devdoc>
internal sealed class ToolStripSplitStackDragDropHandler : IDropTarget, ISupportOleDropSource {
private ToolStrip owner;
public ToolStripSplitStackDragDropHandler(ToolStrip owner) {
if (owner == null) {
//
throw new ArgumentNullException("owner");
}
this.owner = owner;
}
public void OnDragEnter(DragEventArgs e){
Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragEnter: " + e.ToString());
if (e.Data.GetDataPresent(typeof(ToolStripItem))) {
e.Effect = DragDropEffects.Move;
this.ShowItemDropPoint(owner.PointToClient(new Point(e.X, e.Y)));
}
}
public void OnDragLeave(System.EventArgs e){
Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragLeave: " + e.ToString());
owner.ClearInsertionMark();
}
public void OnDragDrop(DragEventArgs e){
Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragDrop: " + e.ToString());
if (e.Data.GetDataPresent(typeof(ToolStripItem))) {
ToolStripItem item = (ToolStripItem)e.Data.GetData(typeof(ToolStripItem));
OnDropItem(item, owner.PointToClient(new Point(e.X, e.Y)));
}
}
public void OnDragOver(DragEventArgs e){
Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "OnDragOver: " + e.ToString());
if (e.Data.GetDataPresent(typeof(ToolStripItem))) {
if (this.ShowItemDropPoint(owner.PointToClient(new Point(e.X, e.Y)))) {
e.Effect = DragDropEffects.Move;
}
else {
if (owner != null) {
owner.ClearInsertionMark();
}
e.Effect = DragDropEffects.None;
}
}
}
public void OnGiveFeedback(GiveFeedbackEventArgs e) {
}
public void OnQueryContinueDrag(QueryContinueDragEventArgs e) {
}
private void OnDropItem(ToolStripItem droppedItem, Point ownerClientAreaRelativeDropPoint) {
Point start = Point.Empty;
int toolStripItemIndex = GetItemInsertionIndex(ownerClientAreaRelativeDropPoint);
if (toolStripItemIndex >= 0) {
ToolStripItem item = owner.Items[toolStripItemIndex];
if (item == droppedItem) {
owner.ClearInsertionMark();
return; // optimization
}
RelativeLocation relativeLocation = ComparePositions(item.Bounds, ownerClientAreaRelativeDropPoint);
droppedItem.Alignment = item.Alignment;
// Protect against negative indicies
int insertIndex = Math.Max(0, toolStripItemIndex);
if (relativeLocation == RelativeLocation.Above) {
insertIndex = (item.Alignment == ToolStripItemAlignment.Left) ? insertIndex : insertIndex + 1;
}
else if (relativeLocation == RelativeLocation.Below) {
insertIndex = (item.Alignment == ToolStripItemAlignment.Left) ? insertIndex : insertIndex-1;
}
else if (((item.Alignment == ToolStripItemAlignment.Left) && (relativeLocation == RelativeLocation.Left)) ||
((item.Alignment == ToolStripItemAlignment.Right) && (relativeLocation == RelativeLocation.Right))) {
// the item alignment is Tail & dropped to right of the center of the item
// or the item alignment is Head & dropped to the left of the center of the item
// Normally, insert the new item after the item, however in RTL insert before the item
insertIndex = Math.Max(0, (owner.RightToLeft == RightToLeft.Yes) ? insertIndex + 1 : insertIndex);
}
else {
// the item alignment is Tail & dropped to left of the center of the item
// or the item alignment is Head & dropped to the right of the center of the item
// Normally, insert the new item before the item, however in RTL insert after the item
insertIndex = Math.Max(0, (owner.RightToLeft == RightToLeft.No) ? insertIndex + 1 : insertIndex);
}
// VSWhidbey 517774
// If the control is moving from a lower to higher index, you actually want to set it one less than its position.
// This is because it is being removed from its original position, which lowers the index of every control before
// its new drop point by 1.
if (owner.Items.IndexOf(droppedItem) < insertIndex) {
insertIndex--;
}
owner.Items.MoveItem(Math.Max(0,insertIndex), droppedItem);
owner.ClearInsertionMark();
}
else if (toolStripItemIndex == -1 && owner.Items.Count == 0) {
owner.Items.Add(droppedItem);
owner.ClearInsertionMark();
}
}
private bool ShowItemDropPoint(Point ownerClientAreaRelativeDropPoint) {
int i = GetItemInsertionIndex(ownerClientAreaRelativeDropPoint);
if (i >= 0) {
ToolStripItem item = owner.Items[i];
RelativeLocation relativeLocation = ComparePositions(item.Bounds, ownerClientAreaRelativeDropPoint);
Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "Drop relative loc " + relativeLocation);
Debug.WriteLineIf(ToolStrip.ItemReorderDebug.TraceVerbose, "Index " + i);
Rectangle insertionRect = Rectangle.Empty;
switch (relativeLocation) {
case RelativeLocation.Above:
insertionRect = new Rectangle(owner.Margin.Left, item.Bounds.Top, owner.Width - (owner.Margin.Horizontal) -1, ToolStrip.insertionBeamWidth);
break;
case RelativeLocation.Below:
insertionRect = new Rectangle(owner.Margin.Left, item.Bounds.Bottom, owner.Width - (owner.Margin.Horizontal) -1, ToolStrip.insertionBeamWidth);
break;
case RelativeLocation.Right:
insertionRect = new Rectangle(item.Bounds.Right, owner.Margin.Top, ToolStrip.insertionBeamWidth, owner.Height- (owner.Margin.Vertical)-1);
break;
case RelativeLocation.Left:
insertionRect = new Rectangle(item.Bounds.Left, owner.Margin.Top, ToolStrip.insertionBeamWidth, owner.Height - (owner.Margin.Vertical) -1);
break;
}
owner.PaintInsertionMark(insertionRect);
return true;
}
else if (owner.Items.Count == 0) {
Rectangle insertionRect = owner.DisplayRectangle;
insertionRect.Width = ToolStrip.insertionBeamWidth;
owner.PaintInsertionMark(insertionRect);
return true;
}
return false;
}
private int GetItemInsertionIndex(Point ownerClientAreaRelativeDropPoint) {
for(int i = 0; i< owner.DisplayedItems.Count; i++) {
Rectangle bounds = owner.DisplayedItems[i].Bounds;
bounds.Inflate(owner.DisplayedItems[i].Margin.Size);
if (bounds.Contains(ownerClientAreaRelativeDropPoint)) {
Debug.WriteLineIf(ToolStrip.DropTargetDebug.TraceVerbose, "MATCH " + owner.DisplayedItems[i].Text + " Bounds: " + owner.DisplayedItems[i].Bounds.ToString());
// consider what to do about items not in the display
return owner.Items.IndexOf(owner.DisplayedItems[i]);
}
}
if (owner.DisplayedItems.Count > 0) {
for (int i = 0; i < owner.DisplayedItems.Count; i++) {
if (owner.DisplayedItems[i].Alignment == ToolStripItemAlignment.Right) {
if (i > 0) {
return owner.Items.IndexOf(owner.DisplayedItems[i - 1]);
}
return owner.Items.IndexOf(owner.DisplayedItems[i]);
}
}
return owner.Items.IndexOf(owner.DisplayedItems[owner.DisplayedItems.Count - 1]);
}
return -1;
}
private enum RelativeLocation {
Above,
Below,
Right,
Left
}
private RelativeLocation ComparePositions(Rectangle orig, Point check) {
if (owner.Orientation == Orientation.Horizontal) {
int widthUnit = orig.Width / 2;
RelativeLocation relativeLocation = RelativeLocation.Left;
// we can return here if we are checking abovebelowleftright, because
// the left right calculation is more picky than the above/below calculation
// and the above below calculation will just override this one.
if ((orig.Left + widthUnit) >= check.X) {
relativeLocation = RelativeLocation.Left;
return relativeLocation;
}
else if ((orig.Right - widthUnit) <= check.X) {
relativeLocation = RelativeLocation.Right;
return relativeLocation;
}
}
if (owner.Orientation == Orientation.Vertical) {
int heightUnit = orig.Height/ 2;
RelativeLocation relativeLocation = (check.Y <= (orig.Top + heightUnit)) ?
RelativeLocation.Above
: RelativeLocation.Below;
return relativeLocation;
}
Debug.Fail("Could not calculate the relative position for AllowItemReorder");
return RelativeLocation.Left;
}
}
}
|