1 //
2 // ToolStripMenuItem.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.Windows.Forms.Design;
33 using System.ComponentModel.Design.Serialization;
34 
35 namespace System.Windows.Forms
36 {
37 	[ToolStripItemDesignerAvailability (ToolStripItemDesignerAvailability.MenuStrip | ToolStripItemDesignerAvailability.ContextMenuStrip)]
38 	[DesignerSerializer ("System.Windows.Forms.Design.ToolStripMenuItemCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
39 	public class ToolStripMenuItem : ToolStripDropDownItem
40 	{
41 		private CheckState checked_state;
42 		private bool check_on_click;
43 		private bool close_on_mouse_release;
44 		private string shortcut_display_string;
45 		private Keys shortcut_keys = Keys.None;
46 		private bool show_shortcut_keys = true;
47 		private Form mdi_client_form;
48 
49 		#region Public Constructors
ToolStripMenuItem()50 		public ToolStripMenuItem ()
51 			: this (null, null, null, string.Empty)
52 		{
53 		}
54 
ToolStripMenuItem(Image image)55 		public ToolStripMenuItem (Image image)
56 			: this (null, image, null, string.Empty)
57 		{
58 		}
59 
ToolStripMenuItem(string text)60 		public ToolStripMenuItem (string text)
61 			: this (text, null, null, string.Empty)
62 		{
63 		}
64 
ToolStripMenuItem(string text, Image image)65 		public ToolStripMenuItem (string text, Image image)
66 			: this (text, image, null, string.Empty)
67 		{
68 		}
69 
ToolStripMenuItem(string text, Image image, EventHandler onClick)70 		public ToolStripMenuItem (string text, Image image, EventHandler onClick)
71 			: this (text, image, onClick, string.Empty)
72 		{
73 		}
74 
ToolStripMenuItem(string text, Image image, params ToolStripItem[] dropDownItems)75 		public ToolStripMenuItem (string text, Image image, params ToolStripItem[] dropDownItems)
76 			: this (text, image, null, string.Empty)
77 		{
78 			if (dropDownItems != null)
79 				foreach (ToolStripItem tsi in dropDownItems)
80 					this.DropDownItems.Add (tsi);
81 		}
82 
ToolStripMenuItem(string text, Image image, EventHandler onClick, Keys shortcutKeys)83 		public ToolStripMenuItem (string text, Image image, EventHandler onClick, Keys shortcutKeys)
84 			: this (text, image, onClick, string.Empty)
85 		{
86 		}
87 
ToolStripMenuItem(string text, Image image, EventHandler onClick, string name)88 		public ToolStripMenuItem (string text, Image image, EventHandler onClick, string name)
89 			: base (text, image, onClick, name)
90 		{
91 			base.Overflow = ToolStripItemOverflow.Never;
92 		}
93 		#endregion
94 
95 		#region Public Properties
96 		[Bindable (true)]
97 		[DefaultValue (false)]
98 		[RefreshProperties (RefreshProperties.All)]
99 		public bool Checked {
100 			get {
101 				switch (this.checked_state) {
102 					case CheckState.Unchecked:
103 					default:
104 						return false;
105 					case CheckState.Checked:
106 					case CheckState.Indeterminate:
107 						return true;
108 				}
109 			}
110 			set {
111 				CheckState = value ? CheckState.Checked : CheckState.Unchecked;
112 			}
113 		}
114 
115 		[DefaultValue (false)]
116 		public bool CheckOnClick {
117 			get { return this.check_on_click; }
118 			set {
119 				if (this.check_on_click != value) {
120 					this.check_on_click = value;
121 					OnUIACheckOnClickChangedEvent (EventArgs.Empty);
122 				}
123 			}
124 		}
125 
126 		[Bindable (true)]
127 		[DefaultValue (CheckState.Unchecked)]
128 		[RefreshProperties (RefreshProperties.All)]
129 		public CheckState CheckState {
130 			get { return this.checked_state; }
131 			set
132 			{
133 				if (!Enum.IsDefined (typeof (CheckState), value))
134 					throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for CheckState", value));
135 
136 				if (value == checked_state)
137 					return;
138 
139 				this.checked_state = value;
140 				this.Invalidate ();
141 				this.OnCheckedChanged (EventArgs.Empty);
142 				this.OnCheckStateChanged (EventArgs.Empty);
143 			}
144 		}
145 
146 		public override bool Enabled {
147 			get { return base.Enabled; }
148 			set { base.Enabled = value; }
149 		}
150 
151 		[Browsable (false)]
152 		public bool IsMdiWindowListEntry {
153 			get { return this.mdi_client_form != null; }
154 		}
155 
156 		[DefaultValue (ToolStripItemOverflow.Never)]
157 		public new ToolStripItemOverflow Overflow {
158 			get { return base.Overflow; }
159 			set { base.Overflow = value; }
160 		}
161 
162 		[Localizable (true)]
163 		[DefaultValue (true)]
164 		public bool ShowShortcutKeys {
165 			get { return this.show_shortcut_keys; }
166 			set { this.show_shortcut_keys = value; }
167 		}
168 
169 		[Localizable (true)]
170 		[DefaultValue (null)]
171 		public string ShortcutKeyDisplayString {
172 			get { return this.shortcut_display_string; }
173 			set { this.shortcut_display_string = value; }
174 		}
175 
176 		[Localizable (true)]
177 		[DefaultValue (Keys.None)]
178 		public Keys ShortcutKeys {
179 			get { return this.shortcut_keys; }
180 			set {
181 				if (this.shortcut_keys != value) {
182 					this.shortcut_keys = value;
183 
184 					if (this.Parent != null)
185 						ToolStripManager.AddToolStripMenuItem (this);
186 				}
187 			 }
188 		}
189 		#endregion
190 
191 		#region Protected Properties
192 		protected internal override Padding DefaultMargin {
193 			get { return new Padding (0); }
194 		}
195 
196 		protected override Padding DefaultPadding {
197 			get { return new Padding (4, 0, 4, 0); }
198 		}
199 
200 		protected override Size DefaultSize {
201 			get { return new Size (32, 19); }
202 		}
203 		#endregion
204 
205 		#region Protected Methods
206 		[EditorBrowsable (EditorBrowsableState.Advanced)]
CreateAccessibilityInstance()207 		protected override AccessibleObject CreateAccessibilityInstance ()
208 		{
209 			return new ToolStripMenuItemAccessibleObject ();
210 		}
211 
CreateDefaultDropDown()212 		protected override ToolStripDropDown CreateDefaultDropDown ()
213 		{
214 			ToolStripDropDownMenu tsddm = new ToolStripDropDownMenu ();
215 			tsddm.OwnerItem = this;
216 			return tsddm;
217 		}
218 
Dispose(bool disposing)219 		protected override void Dispose (bool disposing)
220 		{
221 			base.Dispose (disposing);
222 		}
223 
OnCheckedChanged(EventArgs e)224 		protected virtual void OnCheckedChanged (EventArgs e)
225 		{
226 			EventHandler eh = (EventHandler)Events [CheckedChangedEvent];
227 			if (eh != null)
228 				eh (this, e);
229 		}
230 
OnCheckStateChanged(EventArgs e)231 		protected virtual void OnCheckStateChanged (EventArgs e)
232 		{
233 			EventHandler eh = (EventHandler)Events [CheckStateChangedEvent];
234 			if (eh != null)
235 				eh (this, e);
236 		}
237 
OnClick(EventArgs e)238 		protected override void OnClick (EventArgs e)
239 		{
240 			if (!this.Enabled)
241 				return;
242 
243 			if (this.HasDropDownItems) {
244 				base.OnClick (e);
245 				return;
246 			}
247 
248 			if (this.OwnerItem is ToolStripDropDownItem)
249 				(this.OwnerItem as ToolStripDropDownItem).OnDropDownItemClicked (new ToolStripItemClickedEventArgs (this));
250 
251 			if (this.IsOnDropDown) {
252 				ToolStrip ts = this.GetTopLevelToolStrip ();
253 
254 				if (ts != null)
255 					ts.Dismiss (ToolStripDropDownCloseReason.ItemClicked);
256 			}
257 
258 			if (this.IsMdiWindowListEntry) {
259 				this.mdi_client_form.MdiParent.MdiContainer.ActivateChild (this.mdi_client_form);
260 				return;
261 			}
262 
263 			if (this.check_on_click)
264 				this.Checked = !this.Checked;
265 
266 			base.OnClick (e);
267 
268 			if (!this.IsOnDropDown && !this.HasDropDownItems) {
269 				ToolStrip ts = this.GetTopLevelToolStrip ();
270 
271 				if (ts != null)
272 					ts.Dismiss (ToolStripDropDownCloseReason.ItemClicked);
273 			}
274 		}
275 
OnDropDownHide(EventArgs e)276 		protected override void OnDropDownHide (EventArgs e)
277 		{
278 			base.OnDropDownHide (e);
279 		}
280 
OnDropDownShow(EventArgs e)281 		protected override void OnDropDownShow (EventArgs e)
282 		{
283 			base.OnDropDownShow (e);
284 		}
285 
OnFontChanged(EventArgs e)286 		protected override void OnFontChanged (EventArgs e)
287 		{
288 			base.OnFontChanged (e);
289 		}
290 
OnMouseDown(MouseEventArgs e)291 		protected override void OnMouseDown (MouseEventArgs e)
292 		{
293 			if (!this.IsOnDropDown && this.HasDropDownItems && this.DropDown.Visible)
294 				this.close_on_mouse_release = true;
295 
296 			if (Enabled && !this.DropDown.Visible)
297 				this.ShowDropDown ();
298 
299 			base.OnMouseDown (e);
300 		}
301 
OnMouseEnter(EventArgs e)302 		protected override void OnMouseEnter (EventArgs e)
303 		{
304 			if (this.IsOnDropDown && this.HasDropDownItems && Enabled)
305 				this.ShowDropDown ();
306 
307 			base.OnMouseEnter (e);
308 		}
309 
OnMouseLeave(EventArgs e)310 		protected override void OnMouseLeave (EventArgs e)
311 		{
312 			base.OnMouseLeave (e);
313 		}
314 
OnMouseUp(MouseEventArgs e)315 		protected override void OnMouseUp (MouseEventArgs e)
316 		{
317 			if (this.close_on_mouse_release) {
318 				this.Parent.Dismiss (ToolStripDropDownCloseReason.ItemClicked);
319 				this.Invalidate ();
320 				this.close_on_mouse_release = false;
321 			}
322 
323 			if (!this.HasDropDownItems && Enabled)
324 				base.OnMouseUp (e);
325 		}
326 
OnOwnerChanged(EventArgs e)327 		protected override void OnOwnerChanged (EventArgs e)
328 		{
329 			base.OnOwnerChanged (e);
330 		}
331 
OnPaint(System.Windows.Forms.PaintEventArgs e)332 		protected override void OnPaint (System.Windows.Forms.PaintEventArgs e)
333 		{
334 			base.OnPaint (e);
335 
336 			// Can't render without an owner
337 			if (this.Owner == null)
338 				return;
339 
340 			// If DropDown.ShowImageMargin is false, we don't display the image
341 			Image draw_image = this.UseImageMargin ? this.Image : null;
342 
343 			// Disable this color detection until we do the color detection for ToolStrip *completely*
344 			// Color font_color = this.ForeColor == SystemColors.ControlText ? SystemColors.MenuText : this.ForeColor;
345 			Color font_color = ForeColor;
346 
347 			if ((this.Selected || this.Pressed) && this.IsOnDropDown && font_color == SystemColors.MenuText)
348 				font_color = SystemColors.HighlightText;
349 
350 			if (!this.Enabled && this.ForeColor == SystemColors.ControlText)
351 				font_color = SystemColors.GrayText;
352 
353 			// Gray stuff out if we're disabled
354 			draw_image = this.Enabled ? draw_image : ToolStripRenderer.CreateDisabledImage (draw_image);
355 
356 			// Draw our background
357 			this.Owner.Renderer.DrawMenuItemBackground (new ToolStripItemRenderEventArgs (e.Graphics, this));
358 
359 			// Figure out where our text and image go
360 			Rectangle text_layout_rect;
361 			Rectangle image_layout_rect;
362 
363 			this.CalculateTextAndImageRectangles (out text_layout_rect, out image_layout_rect);
364 
365 			if (this.IsOnDropDown) {
366 				if (!this.UseImageMargin) {
367 					image_layout_rect = Rectangle.Empty;
368 					text_layout_rect = new Rectangle (8, text_layout_rect.Top, text_layout_rect.Width, text_layout_rect.Height);
369 				} else {
370 					text_layout_rect = new Rectangle (35, text_layout_rect.Top, text_layout_rect.Width, text_layout_rect.Height);
371 
372 					if (image_layout_rect != Rectangle.Empty)
373 						image_layout_rect = new Rectangle (new Point (4, 3), base.GetImageSize ());
374 				}
375 
376 				if (this.Checked && this.ShowMargin)
377 					this.Owner.Renderer.DrawItemCheck (new ToolStripItemImageRenderEventArgs (e.Graphics, this, new Rectangle (2, 1, 19, 19)));
378 			}
379 			if (text_layout_rect != Rectangle.Empty)
380 				this.Owner.Renderer.DrawItemText (new ToolStripItemTextRenderEventArgs (e.Graphics, this, this.Text, text_layout_rect, font_color, this.Font, this.TextAlign));
381 
382 			string key_string = GetShortcutDisplayString ();
383 
384 			if (!string.IsNullOrEmpty (key_string) && !this.HasDropDownItems) {
385 				int offset = 15;
386 				Size key_string_size = TextRenderer.MeasureText (key_string, this.Font);
387 				Rectangle key_string_rect = new Rectangle (this.ContentRectangle.Right - key_string_size.Width - offset, text_layout_rect.Top, key_string_size.Width, text_layout_rect.Height);
388 				this.Owner.Renderer.DrawItemText (new ToolStripItemTextRenderEventArgs (e.Graphics, this, key_string, key_string_rect, font_color, this.Font, this.TextAlign));
389 			}
390 
391 			if (image_layout_rect != Rectangle.Empty)
392 				this.Owner.Renderer.DrawItemImage (new ToolStripItemImageRenderEventArgs (e.Graphics, this, draw_image, image_layout_rect));
393 
394 			if (this.IsOnDropDown && this.HasDropDownItems && this.Parent is ToolStripDropDownMenu)
395 				this.Owner.Renderer.DrawArrow (new ToolStripArrowRenderEventArgs (e.Graphics, this, new Rectangle (this.Bounds.Width - 17, 2, 10, 20), Color.Black, ArrowDirection.Right));
396 
397 			return;
398 		}
399 
ProcessCmdKey(ref Message m, Keys keyData)400 		protected internal override bool ProcessCmdKey (ref Message m, Keys keyData)
401 		{
402 			Control source = Control.FromHandle (m.HWnd);
403 			Form f = source == null ? null : (Form)source.TopLevelControl;
404 
405 			if (this.Enabled && keyData == this.shortcut_keys && GetTopLevelControl () == f) {
406 				this.FireEvent (EventArgs.Empty, ToolStripItemEventType.Click);
407 				return true;
408 			}
409 
410 			return base.ProcessCmdKey (ref m, keyData);
411 		}
412 
GetTopLevelControl()413 		Control GetTopLevelControl ()
414 		{
415 			ToolStripItem item = this;
416 			while (item.OwnerItem != null)
417 				item = item.OwnerItem;
418 
419 			if (item.Owner == null)
420 				return null;
421 
422 			if (item.Owner is ContextMenuStrip) {
423 				Control container = ((ContextMenuStrip)item.Owner).container;
424 				return container == null ? null : container.TopLevelControl;
425 			}
426 
427 			// MainMenuStrip
428 			return item.Owner.TopLevelControl;
429 		}
430 
ProcessMnemonic(char charCode)431 		protected internal override bool ProcessMnemonic (char charCode)
432 		{
433 			if (!this.Selected)
434 				this.Parent.ChangeSelection (this);
435 
436 			if (this.HasDropDownItems) {
437 				ToolStripManager.SetActiveToolStrip (this.Parent, true);
438 				this.ShowDropDown ();
439 				this.DropDown.SelectNextToolStripItem (null, true);
440 			} else
441 				this.PerformClick ();
442 
443 			return true;
444 		}
445 
SetBounds(Rectangle rect)446 		protected internal override void SetBounds (Rectangle rect)
447 		{
448 			base.SetBounds (rect);
449 		}
450 		#endregion
451 
452 		#region Public Events
453 		static object CheckedChangedEvent = new object ();
454 		static object CheckStateChangedEvent = new object ();
455 
456 		public event EventHandler CheckedChanged {
457 			add { Events.AddHandler (CheckedChangedEvent, value); }
458 			remove {Events.RemoveHandler (CheckedChangedEvent, value); }
459 		}
460 
461 		public event EventHandler CheckStateChanged {
462 			add { Events.AddHandler (CheckStateChangedEvent, value); }
463 			remove {Events.RemoveHandler (CheckStateChangedEvent, value); }
464 		}
465 		#endregion
466 
467 		#region UIA Framework Events
468 		static object UIACheckOnClickChangedEvent = new object ();
469 
470 		internal event EventHandler UIACheckOnClickChanged {
471 			add { Events.AddHandler (UIACheckOnClickChangedEvent, value); }
472 			remove { Events.RemoveHandler (UIACheckOnClickChangedEvent, value); }
473 		}
474 
OnUIACheckOnClickChangedEvent(EventArgs args)475 		internal void OnUIACheckOnClickChangedEvent (EventArgs args)
476 		{
477 			EventHandler eh
478 				= (EventHandler) Events [UIACheckOnClickChangedEvent];
479 			if (eh != null)
480 				eh (this, args);
481 		}
482 		#endregion
483 
484 		#region Internal Properties
485 		internal Form MdiClientForm {
486 			get { return this.mdi_client_form; }
487 			set { this.mdi_client_form = value; }
488 		}
489 		#endregion
490 
491 		#region Internal Methods
CalculatePreferredSize(Size constrainingSize)492 		internal override Size CalculatePreferredSize (Size constrainingSize)
493 		{
494 			Size base_size = base.CalculatePreferredSize (constrainingSize);
495 
496 			string key_string = GetShortcutDisplayString ();
497 
498 			if (string.IsNullOrEmpty (key_string))
499 				return base_size;
500 
501 			Size text_size = TextRenderer.MeasureText (key_string, this.Font);
502 
503 			return new Size (base_size.Width + text_size.Width - 25, base_size.Height);
504 		}
505 
GetShortcutDisplayString()506 		internal string GetShortcutDisplayString ()
507 		{
508 			if (this.show_shortcut_keys == false)
509 				return string.Empty;
510 			if (this.Parent == null || !(this.Parent is ToolStripDropDownMenu))
511 				return string.Empty;
512 
513 			string key_string = string.Empty;
514 
515 			if (!string.IsNullOrEmpty (this.shortcut_display_string))
516 				key_string = this.shortcut_display_string;
517 			else if (this.shortcut_keys != Keys.None) {
518 				KeysConverter kc = new KeysConverter ();
519 				key_string = kc.ConvertToString (this.shortcut_keys);
520 			}
521 
522 			return key_string;
523 		}
524 
HandleAutoExpansion()525 		internal void HandleAutoExpansion ()
526 		{
527 			if (this.HasDropDownItems) {
528 				this.ShowDropDown ();
529 				this.DropDown.SelectNextToolStripItem (null, true);
530 			}
531 		}
532 
HandleClick(int mouse_clicks, EventArgs e)533 		internal override void HandleClick (int mouse_clicks, EventArgs e)
534 		{
535 			this.OnClick (e);
536 
537 			if (Parent != null)
538 				Parent.Invalidate ();
539 		}
540 		#endregion
541 
542 		#region ToolStripMenuItemAccessibleObject
543 		private class ToolStripMenuItemAccessibleObject : AccessibleObject
544 		{
545 		}
546 		#endregion
547 	}
548 }
549