1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc.
23 // Jordi Mas i Hernandez, jordi@ximian.com
29 using System.Collections;
30 using System.ComponentModel;
31 using System.ComponentModel.Design;
33 using System.Drawing.Text;
35 namespace System.Windows.Forms
37 [DefaultProperty("Text")]
38 [DefaultEvent("Click")]
39 [DesignTimeVisible(false)]
41 public class MenuItem : Menu
43 internal bool separator;
45 internal bool bar_break;
46 private Shortcut shortcut;
48 private bool checked_;
49 private bool radiocheck;
51 private char mnemonic;
52 private bool showshortcut;
55 private Hashtable mdilist_items;
56 private Hashtable mdilist_forms;
57 private MdiClient mdicontainer;
58 private bool is_window_menu_item;
59 private bool defaut_item;
61 private bool ownerdraw;
63 private int mergeorder;
65 private int menuheight;
67 private MenuMerge mergetype;
68 // UIA Framework Note: Used to obtain item bounds
69 internal Rectangle bounds;
71 public MenuItem (): base (null)
73 CommonConstructor (string.Empty);
74 shortcut = Shortcut.None;
77 public MenuItem (string text) : base (null)
79 CommonConstructor (text);
80 shortcut = Shortcut.None;
83 public MenuItem (string text, EventHandler onClick) : base (null)
85 CommonConstructor (text);
86 shortcut = Shortcut.None;
90 public MenuItem (string text, MenuItem[] items) : base (items)
92 CommonConstructor (text);
93 shortcut = Shortcut.None;
96 public MenuItem (string text, EventHandler onClick, Shortcut shortcut) : base (null)
98 CommonConstructor (text);
100 this.shortcut = shortcut;
103 public MenuItem (MenuMerge mergeType, int mergeOrder, Shortcut shortcut, string text,
104 EventHandler onClick, EventHandler onPopup, EventHandler onSelect, MenuItem[] items)
107 CommonConstructor (text);
108 this.shortcut = shortcut;
109 mergeorder = mergeOrder;
110 mergetype = mergeType;
117 private void CommonConstructor (string text)
136 mergetype = MenuMerge.Add;
137 Text = text; // Text can change separator status
141 static object ClickEvent = new object ();
142 static object DrawItemEvent = new object ();
143 static object MeasureItemEvent = new object ();
144 static object PopupEvent = new object ();
145 static object SelectEvent = new object ();
147 public event EventHandler Click {
148 add { Events.AddHandler (ClickEvent, value); }
149 remove { Events.RemoveHandler (ClickEvent, value); }
152 public event DrawItemEventHandler DrawItem {
153 add { Events.AddHandler (DrawItemEvent, value); }
154 remove { Events.RemoveHandler (DrawItemEvent, value); }
157 public event MeasureItemEventHandler MeasureItem {
158 add { Events.AddHandler (MeasureItemEvent, value); }
159 remove { Events.RemoveHandler (MeasureItemEvent, value); }
162 public event EventHandler Popup {
163 add { Events.AddHandler (PopupEvent, value); }
164 remove { Events.RemoveHandler (PopupEvent, value); }
167 public event EventHandler Select {
168 add { Events.AddHandler (SelectEvent, value); }
169 remove { Events.RemoveHandler (SelectEvent, value); }
172 #region UIA Framework Events
174 static object UIACheckedChangedEvent = new object ();
176 internal event EventHandler UIACheckedChanged {
177 add { Events.AddHandler (UIACheckedChangedEvent, value); }
178 remove { Events.RemoveHandler (UIACheckedChangedEvent, value); }
181 internal void OnUIACheckedChanged (EventArgs e)
183 EventHandler eh = (EventHandler) Events [UIACheckedChangedEvent];
188 static object UIARadioCheckChangedEvent = new object ();
190 internal event EventHandler UIARadioCheckChanged {
191 add { Events.AddHandler (UIARadioCheckChangedEvent, value); }
192 remove { Events.RemoveHandler (UIARadioCheckChangedEvent, value); }
195 internal void OnUIARadioCheckChanged (EventArgs e)
197 EventHandler eh = (EventHandler) Events [UIARadioCheckChangedEvent];
202 static object UIAEnabledChangedEvent = new object ();
204 internal event EventHandler UIAEnabledChanged {
205 add { Events.AddHandler (UIAEnabledChangedEvent, value); }
206 remove { Events.RemoveHandler (UIAEnabledChangedEvent, value); }
209 internal void OnUIAEnabledChanged (EventArgs e)
211 EventHandler eh = (EventHandler) Events [UIAEnabledChangedEvent];
216 static object UIATextChangedEvent = new object ();
218 internal event EventHandler UIATextChanged {
219 add { Events.AddHandler (UIATextChangedEvent, value); }
220 remove { Events.RemoveHandler (UIATextChangedEvent, value); }
223 internal void OnUIATextChanged (EventArgs e)
225 EventHandler eh = (EventHandler) Events [UIATextChangedEvent];
233 #region Public Properties
236 [DefaultValue(false)]
237 public bool BarBreak {
238 get { return break_; }
239 set { break_ = value; }
243 [DefaultValue(false)]
245 get { return bar_break; }
246 set { bar_break = value; }
249 [DefaultValue(false)]
250 public bool Checked {
251 get { return checked_; }
253 if (checked_ == value)
258 // UIA Framework Event: Checked Changed
259 OnUIACheckedChanged (EventArgs.Empty);
264 [DefaultValue(false)]
265 public bool DefaultItem {
266 get { return defaut_item; }
267 set { defaut_item = value; }
272 public bool Enabled {
273 get { return enabled; }
275 if (enabled == value)
280 // UIA Framework Event: Enabled Changed
281 OnUIAEnabledChanged (EventArgs.Empty);
289 get { return index; }
291 if (Parent != null && Parent.MenuItems != null && (value < 0 || value >= Parent.MenuItems.Count))
292 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'");
298 public override bool IsParent {
299 get { return IsPopup; }
302 [DefaultValue(false)]
303 public bool MdiList {
304 get { return mdilist; }
306 if (mdilist == value)
310 if (mdilist || mdilist_items == null)
313 foreach (MenuItem item in mdilist_items.Keys)
314 MenuItems.Remove (item);
315 mdilist_items.Clear ();
316 mdilist_items = null;
320 protected int MenuID {
321 get { return menuid; }
325 public int MergeOrder {
326 get { return mergeorder; }
327 set { mergeorder = value; }
330 [DefaultValue(MenuMerge.Add)]
331 public MenuMerge MergeType {
332 get { return mergetype; }
334 if (!Enum.IsDefined (typeof (MenuMerge), value))
335 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for MenuMerge", value));
342 public char Mnemonic {
343 get { return mnemonic; }
346 [DefaultValue(false)]
347 public bool OwnerDraw {
348 get { return ownerdraw; }
349 set { ownerdraw = value; }
354 get { return parent_menu;}
357 [DefaultValue(false)]
358 public bool RadioCheck {
359 get { return radiocheck; }
361 if (radiocheck == value)
366 // UIA Framework Event: Checked Changed
367 OnUIARadioCheckChanged (EventArgs.Empty);
372 [DefaultValue(Shortcut.None)]
374 public Shortcut Shortcut {
375 get { return shortcut;}
377 if (!Enum.IsDefined (typeof (Shortcut), value))
378 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for Shortcut", value));
387 public bool ShowShortcut {
388 get { return showshortcut;}
389 set { showshortcut = value; }
403 // UIA Framework Event: Text Changed
404 OnUIATextChanged (EventArgs.Empty);
414 public bool Visible {
415 get { return visible;}
417 if (value == visible)
422 if (menu_items != null) {
423 foreach (MenuItem mi in menu_items)
427 if (parent_menu != null)
428 parent_menu.OnMenuChanged (EventArgs.Empty);
432 #endregion Public Properties
434 #region Private Properties
436 internal new int Height {
437 get { return bounds.Height; }
438 set { bounds.Height = value; }
441 internal bool IsPopup {
443 if (menu_items.Count > 0)
450 internal bool MeasureEventDefined {
452 if (ownerdraw == true && Events [MeasureItemEvent] != null) {
460 internal bool MenuBar {
461 get { return menubar; }
462 set { menubar = value; }
465 internal int MenuHeight {
466 get { return menuheight; }
467 set { menuheight = value; }
471 internal bool Selected {
472 get { return selected; }
473 set { selected = value; }
476 internal bool Separator {
477 get { return separator; }
478 set { separator = value; }
481 internal DrawItemState Status {
483 DrawItemState status = DrawItemState.None;
484 MenuTracker tracker = Parent.Tracker;
486 status |= (tracker.active || tracker.Navigating ? DrawItemState.Selected : DrawItemState.HotLight);
488 status |= DrawItemState.Grayed | DrawItemState.Disabled;
490 status |= DrawItemState.Checked;
491 if (!tracker.Navigating)
492 status |= DrawItemState.NoAccelerator;
497 internal bool VisibleItems {
499 if (menu_items != null) {
500 foreach (MenuItem mi in menu_items)
508 internal new int Width {
509 get { return bounds.Width; }
510 set { bounds.Width = value; }
514 get { return bounds.X; }
515 set { bounds.X = value; }
520 set { xtab = value; }
524 get { return bounds.Y; }
525 set { bounds.Y = value; }
528 #endregion Private Properties
530 #region Public Methods
532 public virtual MenuItem CloneMenu ()
534 MenuItem item = new MenuItem ();
535 item.CloneMenu (this);
539 protected void CloneMenu (MenuItem itemSrc)
541 base.CloneMenu (itemSrc); // Copy subitems
544 MdiList = itemSrc.MdiList;
545 is_window_menu_item = itemSrc.is_window_menu_item;
546 // Remove items corresponding to window menu items, and add new items
547 // (Otherwise window menu items would show up twice, since the PopulateWindowMenu doesn't
549 bool populated = false;
550 for (int i = MenuItems.Count - 1; i >= 0; i--) {
551 if (MenuItems [i].is_window_menu_item) {
552 MenuItems.RemoveAt (i);
557 PopulateWindowMenu ();
560 BarBreak = itemSrc.BarBreak;
561 Break = itemSrc.Break;
562 Checked = itemSrc.Checked;
563 DefaultItem = itemSrc.DefaultItem;
564 Enabled = itemSrc.Enabled;
565 MergeOrder = itemSrc.MergeOrder;
566 MergeType = itemSrc.MergeType;
567 OwnerDraw = itemSrc.OwnerDraw;
568 //Parent = menuitem.Parent;
569 RadioCheck = itemSrc.RadioCheck;
570 Shortcut = itemSrc.Shortcut;
571 ShowShortcut = itemSrc.ShowShortcut;
573 Visible = itemSrc.Visible;
580 Events[ClickEvent] = itemSrc.Events[ClickEvent];
581 Events[DrawItemEvent] = itemSrc.Events[DrawItemEvent];
582 Events[MeasureItemEvent] = itemSrc.Events[MeasureItemEvent];
583 Events[PopupEvent] = itemSrc.Events[PopupEvent];
584 Events[SelectEvent] = itemSrc.Events[SelectEvent];
587 protected override void Dispose (bool disposing)
589 if (disposing && parent_menu != null)
590 parent_menu.MenuItems.Remove (this);
592 base.Dispose (disposing);
595 // This really clones the item
596 public virtual MenuItem MergeMenu ()
598 MenuItem item = new MenuItem ();
599 item.CloneMenu (this);
603 public void MergeMenu (MenuItem itemSrc)
605 base.MergeMenu (itemSrc);
608 protected virtual void OnClick (EventArgs e)
610 EventHandler eh = (EventHandler)(Events [ClickEvent]);
615 protected virtual void OnDrawItem (DrawItemEventArgs e)
617 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
623 protected virtual void OnInitMenuPopup (EventArgs e)
628 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
633 MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
638 protected virtual void OnPopup (EventArgs e)
640 EventHandler eh = (EventHandler)(Events [PopupEvent]);
645 protected virtual void OnSelect (EventArgs e)
647 EventHandler eh = (EventHandler)(Events [SelectEvent]);
652 public void PerformClick ()
654 OnClick (EventArgs.Empty);
657 public virtual void PerformSelect ()
659 OnSelect (EventArgs.Empty);
662 public override string ToString ()
664 return base.ToString () + ", Items.Count: " + MenuItems.Count + ", Text: " + text;
667 #endregion Public Methods
669 #region Private Methods
671 internal virtual void Invalidate ()
673 if ((Parent == null) || !(Parent is MainMenu) || (Parent.Wnd == null))
676 Form form = Parent.Wnd.FindForm ();
677 if ((form == null) || (!form.IsHandleCreated))
680 XplatUI.RequestNCRecalc (form.Handle);
683 internal void PerformPopup ()
685 OnPopup (EventArgs.Empty);
688 internal void PerformDrawItem (DrawItemEventArgs e)
690 PopulateWindowMenu ();
694 ThemeEngine.Current.DrawMenuItem (this, e);
697 private void PopulateWindowMenu ()
700 if (mdilist_items == null) {
701 mdilist_items = new Hashtable ();
702 mdilist_forms = new Hashtable ();
706 MainMenu main = GetMainMenu ();
707 if (main == null || main.GetForm () == null)
710 Form form = main.GetForm ();
711 mdicontainer = form.MdiContainer;
712 if (mdicontainer == null)
716 // Remove closed forms
717 MenuItem[] items = new MenuItem[mdilist_items.Count];
718 mdilist_items.Keys.CopyTo (items, 0);
719 foreach (MenuItem item in items) {
720 Form mdichild = (Form) mdilist_items [item];
721 if (!mdicontainer.mdi_child_list.Contains(mdichild)) {
722 mdilist_items.Remove (item);
723 mdilist_forms.Remove (mdichild);
724 MenuItems.Remove (item);
728 // Add new forms and update state for existing forms.
729 for (int i = 0; i < mdicontainer.mdi_child_list.Count; i++) {
730 Form mdichild = (Form)mdicontainer.mdi_child_list[i];
732 if (mdilist_forms.Contains (mdichild)) {
733 item = (MenuItem) mdilist_forms [mdichild];
735 item = new MenuItem ();
736 item.is_window_menu_item = true;
737 item.Click += new EventHandler (MdiWindowClickHandler);
738 mdilist_items [item] = mdichild;
739 mdilist_forms [mdichild] = item;
740 MenuItems.AddNoEvents (item);
742 item.Visible = mdichild.Visible;
743 item.Text = "&" + (i + 1).ToString () + " " + mdichild.Text;
744 item.Checked = form.ActiveMdiChild == mdichild;
749 if (mdilist_items != null) {
750 foreach (MenuItem item in mdilist_items.Values) {
751 MenuItems.Remove (item);
754 mdilist_forms.Clear ();
755 mdilist_items.Clear ();
760 internal void PerformMeasureItem (MeasureItemEventArgs e)
765 private void ProcessMnemonic ()
767 if (text == null || text.Length < 2) {
772 bool bPrevAmp = false;
773 for (int i = 0; i < text.Length -1 ; i++) {
774 if (text[i] == '&') {
775 if (bPrevAmp == false && (text[i+1] != '&')) {
776 mnemonic = Char.ToUpper (text[i+1]);
789 private string GetShortCutTextCtrl () { return "Ctrl"; }
790 private string GetShortCutTextAlt () { return "Alt"; }
791 private string GetShortCutTextShift () { return "Shift"; }
793 internal string GetShortCutText ()
795 /* Ctrl+A - Ctrl+Z */
796 if (Shortcut >= Shortcut.CtrlA && Shortcut <= Shortcut.CtrlZ)
797 return GetShortCutTextCtrl () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlA));
800 if (Shortcut >= Shortcut.Alt0 && Shortcut <= Shortcut.Alt9)
801 return GetShortCutTextAlt () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Alt0));
803 /* Alt+F1 - Alt+F2 */
804 if (Shortcut >= Shortcut.AltF1 && Shortcut <= Shortcut.AltF9)
805 return GetShortCutTextAlt () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.AltF1));
807 /* Ctrl+0 - Ctrl+9 */
808 if (Shortcut >= Shortcut.Ctrl0 && Shortcut <= Shortcut.Ctrl9)
809 return GetShortCutTextCtrl () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Ctrl0));
811 /* Ctrl+F0 - Ctrl+F9 */
812 if (Shortcut >= Shortcut.CtrlF1 && Shortcut <= Shortcut.CtrlF9)
813 return GetShortCutTextCtrl () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlF1));
815 /* Ctrl+Shift+0 - Ctrl+Shift+9 */
816 if (Shortcut >= Shortcut.CtrlShift0 && Shortcut <= Shortcut.CtrlShift9)
817 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.CtrlShift0));
819 /* Ctrl+Shift+A - Ctrl+Shift+Z */
820 if (Shortcut >= Shortcut.CtrlShiftA && Shortcut <= Shortcut.CtrlShiftZ)
821 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlShiftA));
823 /* Ctrl+Shift+F1 - Ctrl+Shift+F9 */
824 if (Shortcut >= Shortcut.CtrlShiftF1 && Shortcut <= Shortcut.CtrlShiftF9)
825 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlShiftF1));
828 if (Shortcut >= Shortcut.F1 && Shortcut <= Shortcut.F9)
829 return "F" + (char)((int) '1' + (int)(Shortcut - Shortcut.F1));
831 /* Shift+F1 - Shift+F9 */
832 if (Shortcut >= Shortcut.ShiftF1 && Shortcut <= Shortcut.ShiftF9)
833 return GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.ShiftF1));
837 case Shortcut.AltBksp:
839 case Shortcut.AltF10:
840 return GetShortCutTextAlt () + "+F10";
841 case Shortcut.AltF11:
842 return GetShortCutTextAlt () + "+F11";
843 case Shortcut.AltF12:
844 return GetShortCutTextAlt () + "+F12";
845 case Shortcut.CtrlDel:
846 return GetShortCutTextCtrl () + "+Del";
847 case Shortcut.CtrlF10:
848 return GetShortCutTextCtrl () + "+F10";
849 case Shortcut.CtrlF11:
850 return GetShortCutTextCtrl () + "+F11";
851 case Shortcut.CtrlF12:
852 return GetShortCutTextCtrl () + "+F12";
853 case Shortcut.CtrlIns:
854 return GetShortCutTextCtrl () + "+Ins";
855 case Shortcut.CtrlShiftF10:
856 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F10";
857 case Shortcut.CtrlShiftF11:
858 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F11";
859 case Shortcut.CtrlShiftF12:
860 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F12";
873 case Shortcut.ShiftDel:
874 return GetShortCutTextShift () + "+Del";
875 case Shortcut.ShiftF10:
876 return GetShortCutTextShift () + "+F10";
877 case Shortcut.ShiftF11:
878 return GetShortCutTextShift () + "+F11";
879 case Shortcut.ShiftF12:
880 return GetShortCutTextShift () + "+F12";
881 case Shortcut.ShiftIns:
882 return GetShortCutTextShift () + "+Ins";
890 private void MdiWindowClickHandler (object sender, EventArgs e)
892 Form mdichild = (Form) mdilist_items [sender];
894 // people could add weird items to the Window menu
895 // so we can't assume its just us
896 if (mdichild == null)
899 mdicontainer.ActivateChild (mdichild);
902 private void UpdateMenuItem ()
904 if ((parent_menu == null) || (parent_menu.Tracker == null))
907 parent_menu.Tracker.RemoveShortcuts (this);
908 parent_menu.Tracker.AddShortcuts (this);
911 #endregion Private Methods