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);
263 [DefaultValue(false)]
264 public bool DefaultItem {
265 get { return defaut_item; }
266 set { defaut_item = value; }
271 public bool Enabled {
272 get { return enabled; }
274 if (enabled == value)
279 // UIA Framework Event: Enabled Changed
280 OnUIAEnabledChanged (EventArgs.Empty);
288 get { return index; }
290 if (Parent != null && Parent.MenuItems != null && (value < 0 || value >= Parent.MenuItems.Count))
291 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'");
297 public override bool IsParent {
298 get { return IsPopup; }
301 [DefaultValue(false)]
302 public bool MdiList {
303 get { return mdilist; }
305 if (mdilist == value)
309 if (mdilist || mdilist_items == null)
312 foreach (MenuItem item in mdilist_items.Keys)
313 MenuItems.Remove (item);
314 mdilist_items.Clear ();
315 mdilist_items = null;
319 protected int MenuID {
320 get { return menuid; }
324 public int MergeOrder {
325 get { return mergeorder; }
326 set { mergeorder = value; }
329 [DefaultValue(MenuMerge.Add)]
330 public MenuMerge MergeType {
331 get { return mergetype; }
333 if (!Enum.IsDefined (typeof (MenuMerge), value))
334 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for MenuMerge", value));
341 public char Mnemonic {
342 get { return mnemonic; }
345 [DefaultValue(false)]
346 public bool OwnerDraw {
347 get { return ownerdraw; }
348 set { ownerdraw = value; }
353 get { return parent_menu;}
356 [DefaultValue(false)]
357 public bool RadioCheck {
358 get { return radiocheck; }
360 if (radiocheck == value)
365 // UIA Framework Event: Checked Changed
366 OnUIARadioCheckChanged (EventArgs.Empty);
370 [DefaultValue(Shortcut.None)]
372 public Shortcut Shortcut {
373 get { return shortcut;}
375 if (!Enum.IsDefined (typeof (Shortcut), value))
376 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for Shortcut", value));
385 public bool ShowShortcut {
386 get { return showshortcut;}
387 set { showshortcut = value; }
401 // UIA Framework Event: Text Changed
402 OnUIATextChanged (EventArgs.Empty);
411 public bool Visible {
412 get { return visible;}
414 if (value == visible)
419 if (menu_items != null) {
420 foreach (MenuItem mi in menu_items)
424 if (parent_menu != null)
425 parent_menu.OnMenuChanged (EventArgs.Empty);
429 #endregion Public Properties
431 #region Private Properties
433 internal new int Height {
434 get { return bounds.Height; }
435 set { bounds.Height = value; }
438 internal bool IsPopup {
440 if (menu_items.Count > 0)
447 internal bool MeasureEventDefined {
449 if (ownerdraw == true && Events [MeasureItemEvent] != null) {
457 internal bool MenuBar {
458 get { return menubar; }
459 set { menubar = value; }
462 internal int MenuHeight {
463 get { return menuheight; }
464 set { menuheight = value; }
468 internal bool Selected {
469 get { return selected; }
470 set { selected = value; }
473 internal bool Separator {
474 get { return separator; }
475 set { separator = value; }
478 internal DrawItemState Status {
480 DrawItemState status = DrawItemState.None;
481 MenuTracker tracker = Parent.Tracker;
483 status |= (tracker.active || tracker.Navigating ? DrawItemState.Selected : DrawItemState.HotLight);
485 status |= DrawItemState.Grayed | DrawItemState.Disabled;
487 status |= DrawItemState.Checked;
488 if (!tracker.Navigating)
489 status |= DrawItemState.NoAccelerator;
494 internal bool VisibleItems {
496 if (menu_items != null) {
497 foreach (MenuItem mi in menu_items)
505 internal new int Width {
506 get { return bounds.Width; }
507 set { bounds.Width = value; }
511 get { return bounds.X; }
512 set { bounds.X = value; }
517 set { xtab = value; }
521 get { return bounds.Y; }
522 set { bounds.Y = value; }
525 #endregion Private Properties
527 #region Public Methods
529 public virtual MenuItem CloneMenu ()
531 MenuItem item = new MenuItem ();
532 item.CloneMenu (this);
536 protected void CloneMenu (MenuItem itemSrc)
538 base.CloneMenu (itemSrc); // Copy subitems
541 MdiList = itemSrc.MdiList;
542 is_window_menu_item = itemSrc.is_window_menu_item;
543 // Remove items corresponding to window menu items, and add new items
544 // (Otherwise window menu items would show up twice, since the PopulateWindowMenu doesn't
546 bool populated = false;
547 for (int i = MenuItems.Count - 1; i >= 0; i--) {
548 if (MenuItems [i].is_window_menu_item) {
549 MenuItems.RemoveAt (i);
554 PopulateWindowMenu ();
557 BarBreak = itemSrc.BarBreak;
558 Break = itemSrc.Break;
559 Checked = itemSrc.Checked;
560 DefaultItem = itemSrc.DefaultItem;
561 Enabled = itemSrc.Enabled;
562 MergeOrder = itemSrc.MergeOrder;
563 MergeType = itemSrc.MergeType;
564 OwnerDraw = itemSrc.OwnerDraw;
565 //Parent = menuitem.Parent;
566 RadioCheck = itemSrc.RadioCheck;
567 Shortcut = itemSrc.Shortcut;
568 ShowShortcut = itemSrc.ShowShortcut;
570 Visible = itemSrc.Visible;
575 Events[ClickEvent] = itemSrc.Events[ClickEvent];
576 Events[DrawItemEvent] = itemSrc.Events[DrawItemEvent];
577 Events[MeasureItemEvent] = itemSrc.Events[MeasureItemEvent];
578 Events[PopupEvent] = itemSrc.Events[PopupEvent];
579 Events[SelectEvent] = itemSrc.Events[SelectEvent];
582 protected override void Dispose (bool disposing)
584 if (disposing && parent_menu != null)
585 parent_menu.MenuItems.Remove (this);
587 base.Dispose (disposing);
590 // This really clones the item
591 public virtual MenuItem MergeMenu ()
593 MenuItem item = new MenuItem ();
594 item.CloneMenu (this);
598 public void MergeMenu (MenuItem itemSrc)
600 base.MergeMenu (itemSrc);
603 protected virtual void OnClick (EventArgs e)
605 EventHandler eh = (EventHandler)(Events [ClickEvent]);
610 protected virtual void OnDrawItem (DrawItemEventArgs e)
612 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
618 protected virtual void OnInitMenuPopup (EventArgs e)
623 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
628 MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
633 protected virtual void OnPopup (EventArgs e)
635 EventHandler eh = (EventHandler)(Events [PopupEvent]);
640 protected virtual void OnSelect (EventArgs e)
642 EventHandler eh = (EventHandler)(Events [SelectEvent]);
647 public void PerformClick ()
649 OnClick (EventArgs.Empty);
652 public virtual void PerformSelect ()
654 OnSelect (EventArgs.Empty);
657 public override string ToString ()
659 return base.ToString () + ", Items.Count: " + MenuItems.Count + ", Text: " + text;
662 #endregion Public Methods
664 #region Private Methods
666 internal virtual void Invalidate ()
668 if ((Parent == null) || !(Parent is MainMenu) || (Parent.Wnd == null))
671 Form form = Parent.Wnd.FindForm ();
672 if ((form == null) || (!form.IsHandleCreated))
675 XplatUI.RequestNCRecalc (form.Handle);
678 internal void PerformPopup ()
680 OnPopup (EventArgs.Empty);
683 internal void PerformDrawItem (DrawItemEventArgs e)
685 PopulateWindowMenu ();
689 ThemeEngine.Current.DrawMenuItem (this, e);
692 private void PopulateWindowMenu ()
695 if (mdilist_items == null) {
696 mdilist_items = new Hashtable ();
697 mdilist_forms = new Hashtable ();
701 MainMenu main = GetMainMenu ();
702 if (main == null || main.GetForm () == null)
705 Form form = main.GetForm ();
706 mdicontainer = form.MdiContainer;
707 if (mdicontainer == null)
711 // Remove closed forms
712 MenuItem[] items = new MenuItem[mdilist_items.Count];
713 mdilist_items.Keys.CopyTo (items, 0);
714 foreach (MenuItem item in items) {
715 Form mdichild = (Form) mdilist_items [item];
716 if (!mdicontainer.mdi_child_list.Contains(mdichild)) {
717 mdilist_items.Remove (item);
718 mdilist_forms.Remove (mdichild);
719 MenuItems.Remove (item);
723 // Add new forms and update state for existing forms.
724 for (int i = 0; i < mdicontainer.mdi_child_list.Count; i++) {
725 Form mdichild = (Form)mdicontainer.mdi_child_list[i];
727 if (mdilist_forms.Contains (mdichild)) {
728 item = (MenuItem) mdilist_forms [mdichild];
730 item = new MenuItem ();
731 item.is_window_menu_item = true;
732 item.Click += new EventHandler (MdiWindowClickHandler);
733 mdilist_items [item] = mdichild;
734 mdilist_forms [mdichild] = item;
735 MenuItems.AddNoEvents (item);
737 item.Visible = mdichild.Visible;
738 item.Text = "&" + (i + 1).ToString () + " " + mdichild.Text;
739 item.Checked = form.ActiveMdiChild == mdichild;
744 if (mdilist_items != null) {
745 foreach (MenuItem item in mdilist_items.Values) {
746 MenuItems.Remove (item);
749 mdilist_forms.Clear ();
750 mdilist_items.Clear ();
755 internal void PerformMeasureItem (MeasureItemEventArgs e)
760 private void ProcessMnemonic ()
762 if (text == null || text.Length < 2) {
767 bool bPrevAmp = false;
768 for (int i = 0; i < text.Length -1 ; i++) {
769 if (text[i] == '&') {
770 if (bPrevAmp == false && (text[i+1] != '&')) {
771 mnemonic = Char.ToUpper (text[i+1]);
784 private string GetShortCutTextCtrl () { return "Ctrl"; }
785 private string GetShortCutTextAlt () { return "Alt"; }
786 private string GetShortCutTextShift () { return "Shift"; }
788 internal string GetShortCutText ()
790 /* Ctrl+A - Ctrl+Z */
791 if (Shortcut >= Shortcut.CtrlA && Shortcut <= Shortcut.CtrlZ)
792 return GetShortCutTextCtrl () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlA));
795 if (Shortcut >= Shortcut.Alt0 && Shortcut <= Shortcut.Alt9)
796 return GetShortCutTextAlt () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Alt0));
798 /* Alt+F1 - Alt+F2 */
799 if (Shortcut >= Shortcut.AltF1 && Shortcut <= Shortcut.AltF9)
800 return GetShortCutTextAlt () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.AltF1));
802 /* Ctrl+0 - Ctrl+9 */
803 if (Shortcut >= Shortcut.Ctrl0 && Shortcut <= Shortcut.Ctrl9)
804 return GetShortCutTextCtrl () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Ctrl0));
806 /* Ctrl+F0 - Ctrl+F9 */
807 if (Shortcut >= Shortcut.CtrlF1 && Shortcut <= Shortcut.CtrlF9)
808 return GetShortCutTextCtrl () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlF1));
810 /* Ctrl+Shift+0 - Ctrl+Shift+9 */
811 if (Shortcut >= Shortcut.CtrlShift0 && Shortcut <= Shortcut.CtrlShift9)
812 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.CtrlShift0));
814 /* Ctrl+Shift+A - Ctrl+Shift+Z */
815 if (Shortcut >= Shortcut.CtrlShiftA && Shortcut <= Shortcut.CtrlShiftZ)
816 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlShiftA));
818 /* Ctrl+Shift+F1 - Ctrl+Shift+F9 */
819 if (Shortcut >= Shortcut.CtrlShiftF1 && Shortcut <= Shortcut.CtrlShiftF9)
820 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlShiftF1));
823 if (Shortcut >= Shortcut.F1 && Shortcut <= Shortcut.F9)
824 return "F" + (char)((int) '1' + (int)(Shortcut - Shortcut.F1));
826 /* Shift+F1 - Shift+F9 */
827 if (Shortcut >= Shortcut.ShiftF1 && Shortcut <= Shortcut.ShiftF9)
828 return GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.ShiftF1));
832 case Shortcut.AltBksp:
834 case Shortcut.AltF10:
835 return GetShortCutTextAlt () + "+F10";
836 case Shortcut.AltF11:
837 return GetShortCutTextAlt () + "+F11";
838 case Shortcut.AltF12:
839 return GetShortCutTextAlt () + "+F12";
840 case Shortcut.CtrlDel:
841 return GetShortCutTextCtrl () + "+Del";
842 case Shortcut.CtrlF10:
843 return GetShortCutTextCtrl () + "+F10";
844 case Shortcut.CtrlF11:
845 return GetShortCutTextCtrl () + "+F11";
846 case Shortcut.CtrlF12:
847 return GetShortCutTextCtrl () + "+F12";
848 case Shortcut.CtrlIns:
849 return GetShortCutTextCtrl () + "+Ins";
850 case Shortcut.CtrlShiftF10:
851 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F10";
852 case Shortcut.CtrlShiftF11:
853 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F11";
854 case Shortcut.CtrlShiftF12:
855 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F12";
868 case Shortcut.ShiftDel:
869 return GetShortCutTextShift () + "+Del";
870 case Shortcut.ShiftF10:
871 return GetShortCutTextShift () + "+F10";
872 case Shortcut.ShiftF11:
873 return GetShortCutTextShift () + "+F11";
874 case Shortcut.ShiftF12:
875 return GetShortCutTextShift () + "+F12";
876 case Shortcut.ShiftIns:
877 return GetShortCutTextShift () + "+Ins";
885 private void MdiWindowClickHandler (object sender, EventArgs e)
887 Form mdichild = (Form) mdilist_items [sender];
889 // people could add weird items to the Window menu
890 // so we can't assume its just us
891 if (mdichild == null)
894 mdicontainer.ActivateChild (mdichild);
897 private void UpdateMenuItem ()
899 if ((parent_menu == null) || (parent_menu.Tracker == null))
902 parent_menu.Tracker.RemoveShortcuts (this);
903 parent_menu.Tracker.AddShortcuts (this);
906 #endregion Private Methods