1 //
2 // MenuStrip.cs
3 //
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:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
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.
22 //
23 // Copyright (c) 2006 Jonathan Pobst
24 //
25 // Authors:
26 //	Jonathan Pobst (monkey@jpobst.com)
27 //
28 
29 using System;
30 using System.Drawing;
31 using System.ComponentModel;
32 using System.Runtime.InteropServices;
33 
34 namespace System.Windows.Forms
35 {
36 	[ClassInterface (ClassInterfaceType.AutoDispatch)]
37 	[ComVisible (true)]
38 	public class MenuStrip : ToolStrip
39 	{
40 		private ToolStripMenuItem mdi_window_list_item;
41 
MenuStrip()42 		public MenuStrip () : base ()
43 		{
44 			base.CanOverflow = false;
45 			this.GripStyle = ToolStripGripStyle.Hidden;
46 			this.Stretch = true;
47 			this.Dock = DockStyle.Top;
48 		}
49 
50 		#region Public Properties
51 		[DefaultValue (false)]
52 		[Browsable (false)]
53 		public new bool CanOverflow {
54 			get { return base.CanOverflow; }
55 			set { base.CanOverflow = value; }
56 		}
57 
58 		[DefaultValue (ToolStripGripStyle.Hidden)]
59 		public new ToolStripGripStyle GripStyle {
60 			get { return base.GripStyle; }
61 			set { base.GripStyle = value; }
62 		}
63 
64 		[DefaultValue (null)]
65 		[MergableProperty (false)]
66 		[TypeConverter (typeof (MdiWindowListItemConverter))]
67 		public ToolStripMenuItem MdiWindowListItem {
68 			get { return this.mdi_window_list_item; }
69 			set {
70 				if (this.mdi_window_list_item != value) {
71 					this.mdi_window_list_item = value;
72 					this.RefreshMdiItems ();
73 				}
74 			}
75 		}
76 
77 		[DefaultValue (false)]
78 		public new bool ShowItemToolTips {
79 			get { return base.ShowItemToolTips; }
80 			set { base.ShowItemToolTips = value; }
81 		}
82 
83 		[DefaultValue (true)]
84 		public new bool Stretch {
85 			get { return base.Stretch; }
86 			set { base.Stretch = value; }
87 		}
88 		#endregion
89 
90 		#region Protected Properties
91 		protected override Padding DefaultGripMargin { get { return new Padding (2, 2, 0, 2); } }
92 		protected override Padding DefaultPadding { get { return new Padding (6, 2, 0, 2); } }
93 		protected override bool DefaultShowItemToolTips { get { return false; } }
94 		protected override Size DefaultSize { get { return new Size (200, 24); } }
95 		#endregion
96 
97 		#region Protected Methods
CreateAccessibilityInstance()98 		protected override AccessibleObject CreateAccessibilityInstance ()
99 		{
100 			return new MenuStripAccessibleObject ();
101 		}
102 
CreateDefaultItem(string text, Image image, EventHandler onClick)103 		protected internal override ToolStripItem CreateDefaultItem (string text, Image image, EventHandler onClick)
104 		{
105 			return new ToolStripMenuItem (text, image, onClick);
106 		}
107 
OnMenuActivate(EventArgs e)108 		protected virtual void OnMenuActivate (EventArgs e)
109 		{
110 			EventHandler eh = (EventHandler)(Events [MenuActivateEvent]);
111 			if (eh != null)
112 				eh (this, e);
113 		}
114 
OnMenuDeactivate(EventArgs e)115 		protected virtual void OnMenuDeactivate (EventArgs e)
116 		{
117 			EventHandler eh = (EventHandler)(Events [MenuDeactivateEvent]);
118 			if (eh != null)
119 				eh (this, e);
120 		}
121 
ProcessCmdKey(ref Message m, Keys keyData)122 		protected override bool ProcessCmdKey (ref Message m, Keys keyData)
123 		{
124 			return base.ProcessCmdKey (ref m, keyData);
125 		}
126 
WndProc(ref Message m)127 		protected override void WndProc (ref Message m)
128 		{
129 			base.WndProc (ref m);
130 		}
131 		#endregion
132 
133 		#region Public Events
134 		static object MenuActivateEvent = new object ();
135 		static object MenuDeactivateEvent = new object ();
136 
137 		public event EventHandler MenuActivate {
138 			add { Events.AddHandler (MenuActivateEvent, value); }
139 			remove { Events.RemoveHandler (MenuActivateEvent, value); }
140 		}
141 
142 		public event EventHandler MenuDeactivate {
143 			add { Events.AddHandler (MenuDeactivateEvent, value); }
144 			remove { Events.RemoveHandler (MenuDeactivateEvent, value); }
145 		}
146 		#endregion
147 
148 		#region Internal Properties
149 		internal override bool KeyboardActive {
150 			get { return base.KeyboardActive; }
151 			set {
152 				if (base.KeyboardActive != value) {
153 					base.KeyboardActive = value;
154 
155 					if (value)
156 						this.OnMenuActivate (EventArgs.Empty);
157 					else
158 						this.OnMenuDeactivate (EventArgs.Empty);
159 				}
160 			}
161 		}
162 
163 		internal bool MenuDroppedDown {
164 			get { return this.menu_selected; }
165 			set { this.menu_selected = value; }
166 		}
167 		#endregion
168 
169 		#region Internal Methods
Dismiss(ToolStripDropDownCloseReason reason)170 		internal override void Dismiss (ToolStripDropDownCloseReason reason)
171 		{
172 			// Make sure we don't auto-dropdown next time we're activated
173 			this.MenuDroppedDown = false;
174 
175 			base.Dismiss (reason);
176 
177 			this.FireMenuDeactivate ();
178 		}
179 
FireMenuActivate()180 		internal void FireMenuActivate ()
181 		{
182 			// The tracker lets us know when the form is clicked or loses focus
183 			ToolStripManager.AppClicked += new EventHandler (ToolStripMenuTracker_AppClicked);
184 			ToolStripManager.AppFocusChange += new EventHandler (ToolStripMenuTracker_AppFocusChange);
185 
186 			this.OnMenuActivate (EventArgs.Empty);
187 		}
188 
FireMenuDeactivate()189 		internal void FireMenuDeactivate ()
190 		{
191 			// Detach from the tracker
192 			ToolStripManager.AppClicked -= new EventHandler (ToolStripMenuTracker_AppClicked); ;
193 			ToolStripManager.AppFocusChange -= new EventHandler (ToolStripMenuTracker_AppFocusChange);
194 
195 			this.OnMenuDeactivate (EventArgs.Empty);
196 		}
197 
OnMenuKey()198 		internal override bool OnMenuKey ()
199 		{
200 			// Set ourselves active and select our first item
201 			ToolStripManager.SetActiveToolStrip (this, true);
202 			ToolStripItem tsi = this.SelectNextToolStripItem (null, true);
203 
204 			if (tsi == null)
205 				return false;
206 
207 			if (tsi is MdiControlStrip.SystemMenuItem)
208 				this.SelectNextToolStripItem (tsi, true);
209 
210 			return true;
211 		}
212 
ToolStripMenuTracker_AppFocusChange(object sender, EventArgs e)213 		private void ToolStripMenuTracker_AppFocusChange (object sender, EventArgs e)
214 		{
215 			this.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.AppFocusChange);
216 		}
217 
ToolStripMenuTracker_AppClicked(object sender, EventArgs e)218 		private void ToolStripMenuTracker_AppClicked (object sender, EventArgs e)
219 		{
220 			this.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.AppClicked);
221 		}
222 
RefreshMdiItems()223 		internal void RefreshMdiItems ()
224 		{
225 			if (this.mdi_window_list_item == null)
226 				return;
227 
228 			Form parent_form = this.FindForm ();
229 
230 			if (parent_form == null || parent_form.MainMenuStrip != this)
231 				return;
232 
233 			MdiClient mdi = parent_form.MdiContainer;
234 
235 			// If there isn't a MdiContainer, we don't need to worry about MdiItems  :)
236 			if (mdi == null)
237 				return;
238 
239 			// Make a copy so we can delete from the real one
240 			ToolStripItem[] loopitems = new ToolStripItem[this.mdi_window_list_item.DropDownItems.Count];
241 			this.mdi_window_list_item.DropDownItems.CopyTo (loopitems, 0);
242 
243 			// If the mdi child has been removed, remove our menu item
244 			foreach (ToolStripItem tsi in loopitems)
245 				if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry)
246 					if (!mdi.mdi_child_list.Contains ((tsi as ToolStripMenuItem).MdiClientForm) || !(tsi as ToolStripMenuItem).MdiClientForm.Visible)
247 						this.mdi_window_list_item.DropDownItems.Remove (tsi);
248 
249 			// Add the new forms and update state
250 			for (int i = 0; i < mdi.mdi_child_list.Count; i++) {
251 				Form mdichild = (Form)mdi.mdi_child_list[i];
252 				ToolStripMenuItem tsi;
253 
254 				if (!mdichild.Visible)
255 					continue;
256 
257 				if ((tsi = FindMdiMenuItemOfForm (mdichild)) == null) {
258 					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))
259 						this.mdi_window_list_item.DropDownItems.Add (new ToolStripSeparator ());
260 
261 					tsi = new ToolStripMenuItem ();
262 					tsi.MdiClientForm = mdichild;
263 					this.mdi_window_list_item.DropDownItems.Add (tsi);
264 				}
265 
266 				tsi.Text = string.Format ("&{0} {1}", i + 1, mdichild.Text);
267 				tsi.Checked = parent_form.ActiveMdiChild == mdichild;
268 			}
269 
270 			// Check that everything is in the correct order
271 			if (NeedToReorderMdi ())
272 				ReorderMdiMenu ();
273 		}
274 
FindMdiMenuItemOfForm(Form f)275 		private ToolStripMenuItem FindMdiMenuItemOfForm (Form f)
276 		{
277 			// Not terribly efficient, but Mdi window lists shouldn't get too big
278 			foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems)
279 				if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).MdiClientForm == f)
280 					return (ToolStripMenuItem)tsi;
281 
282 			return null;
283 		}
284 
CountMdiMenuItems()285 		private int CountMdiMenuItems ()
286 		{
287 			int count = 0;
288 
289 			foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems)
290 				if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry)
291 					count++;
292 
293 			return count;
294 		}
295 
NeedToReorderMdi()296 		private bool NeedToReorderMdi ()
297 		{
298 			// Mdi menus must be: User Items, Separator, Mdi Items
299 			bool seenMdi = false;
300 
301 			foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems) {
302 				if (tsi is ToolStripMenuItem) {
303 					if (!(tsi as ToolStripMenuItem).IsMdiWindowListEntry) {
304 						if (seenMdi)
305 							return true;
306 					} else
307 						seenMdi = true;
308 				}
309 			}
310 
311 			return false;
312 		}
313 
ReorderMdiMenu()314 		private void ReorderMdiMenu ()
315 		{
316 			ToolStripItem[] loopitems = new ToolStripItem[this.mdi_window_list_item.DropDownItems.Count];
317 			this.mdi_window_list_item.DropDownItems.CopyTo (loopitems, 0);
318 
319 			this.mdi_window_list_item.DropDownItems.Clear ();
320 
321 			foreach (ToolStripItem tsi in loopitems)
322 				if (tsi is ToolStripSeparator || !(tsi as ToolStripMenuItem).IsMdiWindowListEntry)
323 					this.mdi_window_list_item.DropDownItems.Add (tsi);
324 
325 			int count = this.mdi_window_list_item.DropDownItems.Count;
326 
327 			if (count > 0 && !(this.mdi_window_list_item.DropDownItems[count - 1] is ToolStripSeparator))
328 				this.mdi_window_list_item.DropDownItems.Add (new ToolStripSeparator ());
329 
330 			foreach (ToolStripItem tsi in loopitems)
331 				if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry)
332 					this.mdi_window_list_item.DropDownItems.Add (tsi);
333 		}
334 		#endregion
335 
336 		#region MenuStripAccessibleObject
337 		private class MenuStripAccessibleObject : AccessibleObject
338 		{
339 		}
340 		#endregion
341 
342 	}
343 
344 	#region MdiWindowListItemConverter
345 	internal class MdiWindowListItemConverter : TypeConverter
346 	{
347 	}
348 	#endregion
349 }
350