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 internal Rectangle bounds;
70 public MenuItem (): base (null)
72 CommonConstructor (string.Empty);
73 shortcut = Shortcut.None;
76 public MenuItem (string text) : base (null)
78 CommonConstructor (text);
79 shortcut = Shortcut.None;
82 public MenuItem (string text, EventHandler onClick) : base (null)
84 CommonConstructor (text);
85 shortcut = Shortcut.None;
89 public MenuItem (string text, MenuItem[] items) : base (items)
91 CommonConstructor (text);
92 shortcut = Shortcut.None;
95 public MenuItem (string text, EventHandler onClick, Shortcut shortcut) : base (null)
97 CommonConstructor (text);
99 this.shortcut = shortcut;
102 public MenuItem (MenuMerge mergeType, int mergeOrder, Shortcut shortcut, string text,
103 EventHandler onClick, EventHandler onPopup, EventHandler onSelect, MenuItem[] items)
106 CommonConstructor (text);
107 this.shortcut = shortcut;
108 mergeorder = mergeOrder;
109 mergetype = mergeType;
116 private void CommonConstructor (string text)
135 mergetype = MenuMerge.Add;
136 Text = text; // Text can change separator status
140 static object ClickEvent = new object ();
141 static object DrawItemEvent = new object ();
142 static object MeasureItemEvent = new object ();
143 static object PopupEvent = new object ();
144 static object SelectEvent = new object ();
146 public event EventHandler Click {
147 add { Events.AddHandler (ClickEvent, value); }
148 remove { Events.RemoveHandler (ClickEvent, value); }
151 public event DrawItemEventHandler DrawItem {
152 add { Events.AddHandler (DrawItemEvent, value); }
153 remove { Events.RemoveHandler (DrawItemEvent, value); }
156 public event MeasureItemEventHandler MeasureItem {
157 add { Events.AddHandler (MeasureItemEvent, value); }
158 remove { Events.RemoveHandler (MeasureItemEvent, value); }
161 public event EventHandler Popup {
162 add { Events.AddHandler (PopupEvent, value); }
163 remove { Events.RemoveHandler (PopupEvent, value); }
166 public event EventHandler Select {
167 add { Events.AddHandler (SelectEvent, value); }
168 remove { Events.RemoveHandler (SelectEvent, value); }
172 #region Public Properties
175 [DefaultValue(false)]
176 public bool BarBreak {
177 get { return break_; }
178 set { break_ = value; }
182 [DefaultValue(false)]
184 get { return bar_break; }
185 set { bar_break = value; }
188 [DefaultValue(false)]
189 public bool Checked {
190 get { return checked_; }
191 set { checked_ = value; }
194 [DefaultValue(false)]
195 public bool DefaultItem {
196 get { return defaut_item; }
197 set { defaut_item = value; }
202 public bool Enabled {
203 get { return enabled; }
205 if (enabled == value)
215 get { return index; }
217 if (Parent != null && Parent.MenuItems != null && (value < 0 || value >= Parent.MenuItems.Count))
218 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'");
224 public override bool IsParent {
225 get { return IsPopup; }
228 [DefaultValue(false)]
229 public bool MdiList {
230 get { return mdilist; }
232 if (mdilist == value)
236 if (mdilist || mdilist_items == null)
239 foreach (MenuItem item in mdilist_items.Keys)
240 MenuItems.Remove (item);
241 mdilist_items.Clear ();
242 mdilist_items = null;
246 protected int MenuID {
247 get { return menuid; }
251 public int MergeOrder {
252 get { return mergeorder; }
253 set { mergeorder = value; }
256 [DefaultValue(MenuMerge.Add)]
257 public MenuMerge MergeType {
258 get { return mergetype; }
260 if (!Enum.IsDefined (typeof (MenuMerge), value))
261 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for MenuMerge", value));
268 public char Mnemonic {
269 get { return mnemonic; }
272 [DefaultValue(false)]
273 public bool OwnerDraw {
274 get { return ownerdraw; }
275 set { ownerdraw = value; }
280 get { return parent_menu;}
283 [DefaultValue(false)]
284 public bool RadioCheck {
285 get { return radiocheck; }
286 set { radiocheck = value; }
289 [DefaultValue(Shortcut.None)]
291 public Shortcut Shortcut {
292 get { return shortcut;}
294 if (!Enum.IsDefined (typeof (Shortcut), value))
295 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for Shortcut", value));
303 public bool ShowShortcut {
304 get { return showshortcut;}
305 set { showshortcut = value; }
326 public bool Visible {
327 get { return visible;}
329 if (value == visible)
334 if (menu_items != null) {
335 foreach (MenuItem mi in menu_items)
339 if (parent_menu != null)
340 parent_menu.OnMenuChanged (EventArgs.Empty);
344 #endregion Public Properties
346 #region Private Properties
348 internal new int Height {
349 get { return bounds.Height; }
350 set { bounds.Height = value; }
353 internal bool IsPopup {
355 if (menu_items.Count > 0)
362 internal bool MeasureEventDefined {
364 if (ownerdraw == true && Events [MeasureItemEvent] != null) {
372 internal bool MenuBar {
373 get { return menubar; }
374 set { menubar = value; }
377 internal int MenuHeight {
378 get { return menuheight; }
379 set { menuheight = value; }
383 internal bool Selected {
384 get { return selected; }
385 set { selected = value; }
388 internal bool Separator {
389 get { return separator; }
390 set { separator = value; }
393 internal DrawItemState Status {
395 DrawItemState status = DrawItemState.None;
396 MenuTracker tracker = Parent.Tracker;
398 status |= (tracker.active || tracker.Navigating ? DrawItemState.Selected : DrawItemState.HotLight);
400 status |= DrawItemState.Grayed | DrawItemState.Disabled;
402 status |= DrawItemState.Checked;
403 if (!tracker.Navigating)
404 status |= DrawItemState.NoAccelerator;
409 internal bool VisibleItems {
411 if (menu_items != null) {
412 foreach (MenuItem mi in menu_items)
420 internal new int Width {
421 get { return bounds.Width; }
422 set { bounds.Width = value; }
426 get { return bounds.X; }
427 set { bounds.X = value; }
432 set { xtab = value; }
436 get { return bounds.Y; }
437 set { bounds.Y = value; }
440 #endregion Private Properties
442 #region Public Methods
444 public virtual MenuItem CloneMenu ()
446 MenuItem item = new MenuItem ();
447 item.CloneMenu (this);
451 protected void CloneMenu (MenuItem menuitem)
453 base.CloneMenu (menuitem); // Copy subitems
456 MdiList = menuitem.MdiList;
457 is_window_menu_item = menuitem.is_window_menu_item;
458 // Remove items corresponding to window menu items, and add new items
459 // (Otherwise window menu items would show up twice, since the PopulateWindowMenu doesn't
461 bool populated = false;
462 for (int i = MenuItems.Count - 1; i >= 0; i--) {
463 if (MenuItems [i].is_window_menu_item) {
464 MenuItems.RemoveAt (i);
469 PopulateWindowMenu ();
472 BarBreak = menuitem.BarBreak;
473 Break = menuitem.Break;
474 Checked = menuitem.Checked;
475 DefaultItem = menuitem.DefaultItem;
476 Enabled = menuitem.Enabled;
477 MergeOrder = menuitem.MergeOrder;
478 MergeType = menuitem.MergeType;
479 OwnerDraw = menuitem.OwnerDraw;
480 //Parent = menuitem.Parent;
481 RadioCheck = menuitem.RadioCheck;
482 Shortcut = menuitem.Shortcut;
483 ShowShortcut = menuitem.ShowShortcut;
484 Text = menuitem.Text;
485 Visible = menuitem.Visible;
488 Events[ClickEvent] = menuitem.Events[ClickEvent];
489 Events[DrawItemEvent] = menuitem.Events[DrawItemEvent];
490 Events[MeasureItemEvent] = menuitem.Events[MeasureItemEvent];
491 Events[PopupEvent] = menuitem.Events[PopupEvent];
492 Events[SelectEvent] = menuitem.Events[SelectEvent];
495 protected override void Dispose (bool disposing)
497 base.Dispose (disposing);
500 // This really clones the item
501 public virtual MenuItem MergeMenu ()
503 MenuItem item = new MenuItem ();
504 item.CloneMenu (this);
508 public void MergeMenu (MenuItem menuitem)
510 base.MergeMenu (menuitem);
513 protected virtual void OnClick (EventArgs e)
515 EventHandler eh = (EventHandler)(Events [ClickEvent]);
520 protected virtual void OnDrawItem (DrawItemEventArgs e)
522 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
528 protected virtual void OnInitMenuPopup (EventArgs e)
533 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
538 MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
543 protected virtual void OnPopup (EventArgs e)
545 EventHandler eh = (EventHandler)(Events [PopupEvent]);
550 protected virtual void OnSelect (EventArgs e)
552 EventHandler eh = (EventHandler)(Events [SelectEvent]);
557 public void PerformClick ()
559 OnClick (EventArgs.Empty);
562 public virtual void PerformSelect ()
564 OnSelect (EventArgs.Empty);
567 public override string ToString ()
569 return base.ToString () + ", Items.Count: " + MenuItems.Count + ", Text: " + text;
572 #endregion Public Methods
574 #region Private Methods
576 internal virtual void Invalidate ()
578 if ((Parent == null) || !(Parent is MainMenu) || (Parent.Wnd == null))
581 Form form = Parent.Wnd.FindForm ();
582 if ((form == null) || (!form.IsHandleCreated))
585 XplatUI.RequestNCRecalc (form.Handle);
588 internal void PerformPopup ()
590 OnPopup (EventArgs.Empty);
593 internal void PerformDrawItem (DrawItemEventArgs e)
595 PopulateWindowMenu ();
599 ThemeEngine.Current.DrawMenuItem (this, e);
602 private void PopulateWindowMenu ()
605 if (mdilist_items == null) {
606 mdilist_items = new Hashtable ();
607 mdilist_forms = new Hashtable ();
611 MainMenu main = GetMainMenu ();
612 if (main == null || main.GetForm () == null)
615 Form form = main.GetForm ();
616 mdicontainer = form.MdiContainer;
617 if (mdicontainer == null)
621 // Remove closed forms
622 MenuItem[] items = new MenuItem[mdilist_items.Count];
623 mdilist_items.Keys.CopyTo (items, 0);
624 foreach (MenuItem item in items) {
625 Form mdichild = (Form) mdilist_items [item];
626 if (!mdicontainer.mdi_child_list.Contains(mdichild)) {
627 mdilist_items.Remove (item);
628 mdilist_forms.Remove (mdichild);
629 MenuItems.Remove (item);
633 // Add new forms and update state for existing forms.
634 for (int i = 0; i < mdicontainer.mdi_child_list.Count; i++) {
635 Form mdichild = (Form)mdicontainer.mdi_child_list[i];
637 if (mdilist_forms.Contains (mdichild)) {
638 item = (MenuItem) mdilist_forms [mdichild];
640 item = new MenuItem ();
641 item.is_window_menu_item = true;
642 item.Click += new EventHandler (MdiWindowClickHandler);
643 mdilist_items [item] = mdichild;
644 mdilist_forms [mdichild] = item;
645 MenuItems.AddNoEvents (item);
647 item.Visible = mdichild.Visible;
648 item.Text = "&" + (i + 1).ToString () + " " + mdichild.Text;
649 item.Checked = form.ActiveMdiChild == mdichild;
654 if (mdilist_items != null) {
655 foreach (MenuItem item in mdilist_items.Values) {
656 MenuItems.Remove (item);
659 mdilist_forms.Clear ();
660 mdilist_items.Clear ();
665 internal void PerformMeasureItem (MeasureItemEventArgs e)
670 private void ProcessMnemonic ()
672 if (text == null || text.Length < 2) {
677 bool bPrevAmp = false;
678 for (int i = 0; i < text.Length -1 ; i++) {
679 if (text[i] == '&') {
680 if (bPrevAmp == false && (text[i+1] != '&')) {
681 mnemonic = Char.ToUpper (text[i+1]);
694 private string GetShortCutTextCtrl () { return "Ctrl"; }
695 private string GetShortCutTextAlt () { return "Alt"; }
696 private string GetShortCutTextShift () { return "Shift"; }
698 internal string GetShortCutText ()
700 /* Ctrl+A - Ctrl+Z */
701 if (Shortcut >= Shortcut.CtrlA && Shortcut <= Shortcut.CtrlZ)
702 return GetShortCutTextCtrl () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlA));
705 if (Shortcut >= Shortcut.Alt0 && Shortcut <= Shortcut.Alt9)
706 return GetShortCutTextAlt () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Alt0));
708 /* Alt+F1 - Alt+F2 */
709 if (Shortcut >= Shortcut.AltF1 && Shortcut <= Shortcut.AltF9)
710 return GetShortCutTextAlt () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.AltF1));
712 /* Ctrl+0 - Ctrl+9 */
713 if (Shortcut >= Shortcut.Ctrl0 && Shortcut <= Shortcut.Ctrl9)
714 return GetShortCutTextCtrl () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Ctrl0));
716 /* Ctrl+F0 - Ctrl+F9 */
717 if (Shortcut >= Shortcut.CtrlF1 && Shortcut <= Shortcut.CtrlF9)
718 return GetShortCutTextCtrl () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlF1));
720 /* Ctrl+Shift+0 - Ctrl+Shift+9 */
721 if (Shortcut >= Shortcut.CtrlShift0 && Shortcut <= Shortcut.CtrlShift9)
722 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.CtrlShift0));
724 /* Ctrl+Shift+A - Ctrl+Shift+Z */
725 if (Shortcut >= Shortcut.CtrlShiftA && Shortcut <= Shortcut.CtrlShiftZ)
726 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlShiftA));
728 /* Ctrl+Shift+F1 - Ctrl+Shift+F9 */
729 if (Shortcut >= Shortcut.CtrlShiftF1 && Shortcut <= Shortcut.CtrlShiftF9)
730 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlShiftF1));
733 if (Shortcut >= Shortcut.F1 && Shortcut <= Shortcut.F9)
734 return "F" + (char)((int) '1' + (int)(Shortcut - Shortcut.F1));
736 /* Shift+F1 - Shift+F9 */
737 if (Shortcut >= Shortcut.ShiftF1 && Shortcut <= Shortcut.ShiftF9)
738 return GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.ShiftF1));
742 case Shortcut.AltBksp:
744 case Shortcut.AltF10:
745 return GetShortCutTextAlt () + "+F10";
746 case Shortcut.AltF11:
747 return GetShortCutTextAlt () + "+F11";
748 case Shortcut.AltF12:
749 return GetShortCutTextAlt () + "+F12";
750 case Shortcut.CtrlDel:
751 return GetShortCutTextCtrl () + "+Del";
752 case Shortcut.CtrlF10:
753 return GetShortCutTextCtrl () + "+F10";
754 case Shortcut.CtrlF11:
755 return GetShortCutTextCtrl () + "+F11";
756 case Shortcut.CtrlF12:
757 return GetShortCutTextCtrl () + "+F12";
758 case Shortcut.CtrlIns:
759 return GetShortCutTextCtrl () + "+Ins";
760 case Shortcut.CtrlShiftF10:
761 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F10";
762 case Shortcut.CtrlShiftF11:
763 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F11";
764 case Shortcut.CtrlShiftF12:
765 return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F12";
778 case Shortcut.ShiftDel:
779 return GetShortCutTextShift () + "+Del";
780 case Shortcut.ShiftF10:
781 return GetShortCutTextShift () + "+F10";
782 case Shortcut.ShiftF11:
783 return GetShortCutTextShift () + "+F11";
784 case Shortcut.ShiftF12:
785 return GetShortCutTextShift () + "+F12";
786 case Shortcut.ShiftIns:
787 return GetShortCutTextShift () + "+Ins";
795 private void MdiWindowClickHandler (object sender, EventArgs e)
797 Form mdichild = (Form) mdilist_items [sender];
799 // people could add weird items to the Window menu
800 // so we can't assume its just us
801 if (mdichild == null)
804 mdicontainer.ActivateChild (mdichild);
807 #endregion Private Methods