Merge pull request #3802 from lambdageek/dev-reference-attr-take3
[mono.git] / mcs / class / System.Windows.Forms / System.Windows.Forms / ToolStripMenuItem.cs
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
50                 public ToolStripMenuItem ()
51                         : this (null, null, null, string.Empty)
52                 {
53                 }
54
55                 public ToolStripMenuItem (Image image)
56                         : this (null, image, null, string.Empty)
57                 {
58                 }
59
60                 public ToolStripMenuItem (string text)
61                         : this (text, null, null, string.Empty)
62                 {
63                 }
64
65                 public ToolStripMenuItem (string text, Image image)
66                         : this (text, image, null, string.Empty)
67                 {
68                 }
69
70                 public ToolStripMenuItem (string text, Image image, EventHandler onClick)
71                         : this (text, image, onClick, string.Empty)
72                 {
73                 }
74
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
83                 public ToolStripMenuItem (string text, Image image, EventHandler onClick, Keys shortcutKeys)
84                         : this (text, image, onClick, string.Empty)
85                 {
86                 }
87
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)]
207                 protected override AccessibleObject CreateAccessibilityInstance ()
208                 {
209                         return new ToolStripMenuItemAccessibleObject ();
210                 }
211                 
212                 protected override ToolStripDropDown CreateDefaultDropDown ()
213                 {
214                         ToolStripDropDownMenu tsddm = new ToolStripDropDownMenu ();
215                         tsddm.OwnerItem = this;
216                         return tsddm;
217                 }
218
219                 protected override void Dispose (bool disposing)
220                 {
221                         base.Dispose (disposing);
222                 }
223
224                 protected virtual void OnCheckedChanged (EventArgs e)
225                 {
226                         EventHandler eh = (EventHandler)Events [CheckedChangedEvent];
227                         if (eh != null)
228                                 eh (this, e);
229                 }
230
231                 protected virtual void OnCheckStateChanged (EventArgs e)
232                 {
233                         EventHandler eh = (EventHandler)Events [CheckStateChangedEvent];
234                         if (eh != null)
235                                 eh (this, e);
236                 }
237
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
276                 protected override void OnDropDownHide (EventArgs e)
277                 {
278                         base.OnDropDownHide (e);
279                 }
280
281                 protected override void OnDropDownShow (EventArgs e)
282                 {
283                         base.OnDropDownShow (e);
284                 }
285
286                 protected override void OnFontChanged (EventArgs e)
287                 {
288                         base.OnFontChanged (e);
289                 }
290
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
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
310                 protected override void OnMouseLeave (EventArgs e)
311                 {
312                         base.OnMouseLeave (e);
313                 }
314
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
327                 protected override void OnOwnerChanged (EventArgs e)
328                 {
329                         base.OnOwnerChanged (e);
330                 }
331
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
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
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
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                 
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
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
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                 
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                 
525                 internal void HandleAutoExpansion ()
526                 {
527                         if (this.HasDropDownItems) {
528                                 this.ShowDropDown ();
529                                 this.DropDown.SelectNextToolStripItem (null, true);
530                         }
531                 }
532
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 }