4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // Copyright (c) 2006 Jonathan Pobst
26 // Jonathan Pobst (monkey@jpobst.com)
32 using System.ComponentModel;
33 using System.Runtime.InteropServices;
35 namespace System.Windows.Forms
37 [ClassInterface (ClassInterfaceType.AutoDispatch)]
39 public class MenuStrip : ToolStrip
41 private ToolStripMenuItem mdi_window_list_item;
43 public MenuStrip () : base ()
45 base.CanOverflow = false;
46 this.GripStyle = ToolStripGripStyle.Hidden;
48 this.Dock = DockStyle.Top;
51 #region Public Properties
52 [DefaultValue (false)]
54 public new bool CanOverflow {
55 get { return base.CanOverflow; }
56 set { base.CanOverflow = value; }
59 [DefaultValue (ToolStripGripStyle.Hidden)]
60 public new ToolStripGripStyle GripStyle {
61 get { return base.GripStyle; }
62 set { base.GripStyle = value; }
66 [MergableProperty (false)]
67 [TypeConverter (typeof (MdiWindowListItemConverter))]
68 public ToolStripMenuItem MdiWindowListItem {
69 get { return this.mdi_window_list_item; }
71 if (this.mdi_window_list_item != value) {
72 this.mdi_window_list_item = value;
73 this.RefreshMdiItems ();
78 [DefaultValue (false)]
79 public new bool ShowItemToolTips {
80 get { return base.ShowItemToolTips; }
81 set { base.ShowItemToolTips = value; }
85 public new bool Stretch {
86 get { return base.Stretch; }
87 set { base.Stretch = value; }
91 #region Protected Properties
92 protected override Padding DefaultGripMargin { get { return new Padding (2, 2, 0, 2); } }
93 protected override Padding DefaultPadding { get { return new Padding (6, 2, 0, 2); } }
94 protected override bool DefaultShowItemToolTips { get { return false; } }
95 protected override Size DefaultSize { get { return new Size (200, 24); } }
98 #region Protected Methods
99 protected override AccessibleObject CreateAccessibilityInstance ()
101 return new MenuStripAccessibleObject ();
104 protected internal override ToolStripItem CreateDefaultItem (string text, Image image, EventHandler onClick)
106 return new ToolStripMenuItem (text, image, onClick);
109 protected virtual void OnMenuActivate (EventArgs e)
111 EventHandler eh = (EventHandler)(Events [MenuActivateEvent]);
116 protected virtual void OnMenuDeactivate (EventArgs e)
118 EventHandler eh = (EventHandler)(Events [MenuDeactivateEvent]);
123 protected override bool ProcessCmdKey (ref Message m, Keys keyData)
125 return base.ProcessCmdKey (ref m, keyData);
128 protected override void WndProc (ref Message m)
130 base.WndProc (ref m);
134 #region Public Events
135 static object MenuActivateEvent = new object ();
136 static object MenuDeactivateEvent = new object ();
138 public event EventHandler MenuActivate {
139 add { Events.AddHandler (MenuActivateEvent, value); }
140 remove { Events.RemoveHandler (MenuActivateEvent, value); }
143 public event EventHandler MenuDeactivate {
144 add { Events.AddHandler (MenuDeactivateEvent, value); }
145 remove { Events.RemoveHandler (MenuDeactivateEvent, value); }
149 #region Internal Properties
150 internal override bool KeyboardActive {
151 get { return base.KeyboardActive; }
153 if (base.KeyboardActive != value) {
154 base.KeyboardActive = value;
157 this.OnMenuActivate (EventArgs.Empty);
159 this.OnMenuDeactivate (EventArgs.Empty);
164 internal bool MenuDroppedDown {
165 get { return this.menu_selected; }
166 set { this.menu_selected = value; }
170 #region Internal Methods
171 internal override void Dismiss (ToolStripDropDownCloseReason reason)
173 // Make sure we don't auto-dropdown next time we're activated
174 this.MenuDroppedDown = false;
176 base.Dismiss (reason);
179 internal void FireMenuActivate ()
181 // The tracker lets us know when the form is clicked or loses focus
182 ToolStripManager.AppClicked += new EventHandler (ToolStripMenuTracker_AppClicked);
183 ToolStripManager.AppFocusChange += new EventHandler (ToolStripMenuTracker_AppFocusChange);
185 this.OnMenuActivate (EventArgs.Empty);
188 internal void FireMenuDeactivate ()
190 // Detach from the tracker
191 ToolStripManager.AppClicked -= new EventHandler (ToolStripMenuTracker_AppClicked); ;
192 ToolStripManager.AppFocusChange -= new EventHandler (ToolStripMenuTracker_AppFocusChange);
194 this.OnMenuDeactivate (EventArgs.Empty);
197 internal override bool OnMenuKey ()
199 // Set ourselves active and select our first item
200 ToolStripManager.SetActiveToolStrip (this, true);
201 ToolStripItem tsi = this.SelectNextToolStripItem (null, true);
206 if (tsi is MdiControlStrip.SystemMenuItem)
207 this.SelectNextToolStripItem (tsi, true);
212 private void ToolStripMenuTracker_AppFocusChange (object sender, EventArgs e)
214 this.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.AppFocusChange);
217 private void ToolStripMenuTracker_AppClicked (object sender, EventArgs e)
219 this.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.AppClicked);
222 internal void RefreshMdiItems ()
224 if (this.mdi_window_list_item == null)
227 Form parent_form = this.FindForm ();
229 if (parent_form == null || parent_form.MainMenuStrip != this)
232 MdiClient mdi = parent_form.MdiContainer;
234 // If there isn't a MdiContainer, we don't need to worry about MdiItems :)
238 // Make a copy so we can delete from the real one
239 ToolStripItem[] loopitems = new ToolStripItem[this.mdi_window_list_item.DropDownItems.Count];
240 this.mdi_window_list_item.DropDownItems.CopyTo (loopitems, 0);
242 // If the mdi child has been removed, remove our menu item
243 foreach (ToolStripItem tsi in loopitems)
244 if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry)
245 if (!mdi.mdi_child_list.Contains ((tsi as ToolStripMenuItem).MdiClientForm) || !(tsi as ToolStripMenuItem).MdiClientForm.Visible)
246 this.mdi_window_list_item.DropDownItems.Remove (tsi);
248 // Add the new forms and update state
249 for (int i = 0; i < mdi.mdi_child_list.Count; i++) {
250 Form mdichild = (Form)mdi.mdi_child_list[i];
251 ToolStripMenuItem tsi;
253 if (!mdichild.Visible)
256 if ((tsi = FindMdiMenuItemOfForm (mdichild)) == null) {
257 if (CountMdiMenuItems () == 0 && this.mdi_window_list_item.DropDownItems.Count > 0 && !(this.mdi_window_list_item.DropDownItems[this.mdi_window_list_item.DropDownItems.Count - 1] is ToolStripSeparator))
258 this.mdi_window_list_item.DropDownItems.Add (new ToolStripSeparator ());
260 tsi = new ToolStripMenuItem ();
261 tsi.MdiClientForm = mdichild;
262 this.mdi_window_list_item.DropDownItems.Add (tsi);
265 tsi.Text = string.Format ("&{0} {1}", i + 1, mdichild.Text);
266 tsi.Checked = parent_form.ActiveMdiChild == mdichild;
269 // Check that everything is in the correct order
270 if (NeedToReorderMdi ())
274 private ToolStripMenuItem FindMdiMenuItemOfForm (Form f)
276 // Not terribly efficient, but Mdi window lists shouldn't get too big
277 foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems)
278 if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).MdiClientForm == f)
279 return (ToolStripMenuItem)tsi;
284 private int CountMdiMenuItems ()
288 foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems)
289 if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry)
295 private bool NeedToReorderMdi ()
297 // Mdi menus must be: User Items, Separator, Mdi Items
298 bool seenMdi = false;
300 foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems) {
301 if (tsi is ToolStripMenuItem) {
302 if (!(tsi as ToolStripMenuItem).IsMdiWindowListEntry) {
313 internal bool ProcessImplicitMnemonic (char charCode)
315 string code = Char.ToUpper (charCode).ToString ();
317 // If any item's text starts with our letter, it gets the message
318 foreach (ToolStripItem tsi in this.Items)
319 if (tsi.Enabled && tsi.Visible && !string.IsNullOrEmpty (tsi.Text) && tsi.Text.ToUpper ().StartsWith (code))
320 return tsi.ProcessMnemonic (charCode);
325 private void ReorderMdiMenu ()
327 ToolStripItem[] loopitems = new ToolStripItem[this.mdi_window_list_item.DropDownItems.Count];
328 this.mdi_window_list_item.DropDownItems.CopyTo (loopitems, 0);
330 this.mdi_window_list_item.DropDownItems.Clear ();
332 foreach (ToolStripItem tsi in loopitems)
333 if (tsi is ToolStripSeparator || !(tsi as ToolStripMenuItem).IsMdiWindowListEntry)
334 this.mdi_window_list_item.DropDownItems.Add (tsi);
336 int count = this.mdi_window_list_item.DropDownItems.Count;
338 if (count > 0 && !(this.mdi_window_list_item.DropDownItems[count - 1] is ToolStripSeparator))
339 this.mdi_window_list_item.DropDownItems.Add (new ToolStripSeparator ());
341 foreach (ToolStripItem tsi in loopitems)
342 if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry)
343 this.mdi_window_list_item.DropDownItems.Add (tsi);
347 #region MenuStripAccessibleObject
348 private class MenuStripAccessibleObject : AccessibleObject
355 #region MdiWindowListItemConverter
356 internal class MdiWindowListItemConverter : TypeConverter