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
30 using System.Collections;
31 using System.ComponentModel;
32 using System.ComponentModel.Design;
33 using System.Reflection;
34 using System.Runtime.InteropServices;
35 using System.Collections.Generic;
37 namespace System.Windows.Forms
39 [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
41 public abstract class Menu : Component
43 internal MenuItemCollection menu_items;
44 internal IntPtr menu_handle = IntPtr.Zero;
45 internal Menu parent_menu = null;
46 System.Drawing.Rectangle rect;
47 // UIA Framework Note: Used to keep track of expanded menus
49 internal MenuTracker tracker;
50 private string control_name;
51 private object control_tag;
52 public const int FindHandle = 0;
53 public const int FindShortcut = 1;
55 protected Menu (MenuItem[] items)
57 menu_items = new MenuItemCollection (this);
60 menu_items.AddRange (items);
63 #region Public Properties
65 [BrowsableAttribute(false)]
66 [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
67 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
68 public IntPtr Handle {
69 get { return menu_handle; }
72 internal virtual void OnMenuChanged (EventArgs e)
74 EventHandler eh = (EventHandler)(Events [MenuChangedEvent]);
79 [BrowsableAttribute(false)]
80 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
81 public virtual bool IsParent {
83 if (menu_items != null && menu_items.Count > 0)
90 [BrowsableAttribute(false)]
91 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
92 public MenuItem MdiListItem {
94 throw new NotImplementedException ();
98 [BrowsableAttribute(false)]
99 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
100 [MergableProperty(false)]
101 public MenuItemCollection MenuItems {
102 get { return menu_items; }
105 [BrowsableAttribute(false)]
106 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
108 get { return control_name; }
109 set { control_name = value; }
114 [TypeConverter(typeof(StringConverter))]
116 [MWFCategory("Data")]
118 get { return control_tag; }
119 set { control_tag = value; }
122 #endregion Public Properties
124 #region Private Properties
126 // UIA Framework Note: Used to obtain menu bounds
127 internal System.Drawing.Rectangle Rect {
131 internal MenuItem SelectedItem {
133 foreach (MenuItem item in MenuItems)
141 internal int Height {
142 get { return rect.Height; }
143 set { rect.Height = value; }
147 get { return rect.Width; }
148 set { rect.Width = value; }
152 get { return rect.X; }
153 set { rect.X = value; }
157 get { return rect.Y; }
158 set { rect.Y = value; }
161 internal MenuTracker Tracker {
164 while (top.parent_menu != null)
165 top = top.parent_menu;
170 #endregion Private Properties
172 #region Public Methods
174 protected void CloneMenu (Menu menuSrc)
178 menu_items = new MenuItemCollection (this);
180 for (int i = 0; i < menuSrc.MenuItems.Count ; i++)
181 menu_items.Add (menuSrc.MenuItems [i].CloneMenu ());
184 protected virtual IntPtr CreateMenuHandle ()
189 protected override void Dispose (bool disposing)
192 if (menu_items != null) {
193 // MenuItem.Dispose removes the item from the list
194 while (menu_items.Count > 0) {
195 menu_items [0].Dispose ();
198 if (menu_handle != IntPtr.Zero) {
199 menu_handle = IntPtr.Zero;
204 // From Microsoft documentation is impossible to guess that
205 // this method is supossed to do
207 // update: according to MS documentation, first parameter is on of this
208 // constant values FindHandle or FindShortcut, value depends from what
209 // you what to search, by shortcut or handle. FindHandle and FindShortcut
210 // is a constant fields and was defined for this class.
211 public MenuItem FindMenuItem (int type, IntPtr value)
216 protected int FindMergePosition (int mergeOrder)
218 int cnt = MenuItems.Count, cur, pos;
220 for (pos = 0; pos < cnt; ) {
221 cur = (pos + cnt) /2;
222 if (MenuItems[cur].MergeOrder > mergeOrder) {
232 public ContextMenu GetContextMenu ()
234 for (Menu item = this; item != null; item = item.parent_menu) {
235 if (item is ContextMenu) {
236 return (ContextMenu) item;
243 public MainMenu GetMainMenu ()
245 for (Menu item = this; item != null; item = item.parent_menu) {
246 if (item is MainMenu) {
247 return (MainMenu) item;
254 internal virtual void InvalidateItem (MenuItem item)
257 Wnd.Invalidate (item.bounds);
260 public virtual void MergeMenu (Menu menuSrc)
263 throw new ArgumentException ("The menu cannot be merged with itself");
268 for (int i = 0; i < menuSrc.MenuItems.Count; i++) {
270 MenuItem sourceitem = menuSrc.MenuItems[i];
272 switch (sourceitem.MergeType) {
273 case MenuMerge.Remove: // Item not included
278 int pos = FindMergePosition (sourceitem.MergeOrder);
279 MenuItems.Add (pos, sourceitem.CloneMenu ());
283 case MenuMerge.Replace:
284 case MenuMerge.MergeItems:
286 for (int pos = FindMergePosition (sourceitem.MergeOrder-1); pos <= MenuItems.Count; pos++) {
288 if ((pos >= MenuItems.Count) || (MenuItems[pos].MergeOrder != sourceitem.MergeOrder)) {
289 MenuItems.Add (pos, sourceitem.CloneMenu ());
293 MenuItem mergeitem = MenuItems[pos];
295 if (mergeitem.MergeType != MenuMerge.Add) {
296 if ((sourceitem.MergeType == MenuMerge.MergeItems) && (mergeitem.MergeType == MenuMerge.MergeItems)) {
297 mergeitem.MergeMenu (sourceitem);
299 MenuItems.Remove (sourceitem);
300 MenuItems.Add (pos, sourceitem.CloneMenu ());
315 protected internal virtual bool ProcessCmdKey (ref Message msg, Keys keyData)
319 return tracker.ProcessKeys (ref msg, keyData);
322 public override string ToString ()
324 return base.ToString () + ", Items.Count: " + MenuItems.Count;
327 #endregion Public Methods
328 static object MenuChangedEvent = new object ();
330 // UIA Framework Note: Used to track changes in MenuItemCollection
331 internal event EventHandler MenuChanged {
332 add { Events.AddHandler (MenuChangedEvent, value); }
333 remove { Events.RemoveHandler (MenuChangedEvent, value); }
336 [ListBindable(false)]
337 public class MenuItemCollection : IList, ICollection, IEnumerable
340 private ArrayList items = new ArrayList ();
342 public MenuItemCollection (Menu owner)
347 #region Public Properties
350 get { return items.Count;}
353 public bool IsReadOnly {
354 get { return false; }
357 bool ICollection.IsSynchronized {
361 object ICollection.SyncRoot {
365 bool IList.IsFixedSize {
369 public virtual MenuItem this [int index] {
371 if (index < 0 || index >= Count)
372 throw new ArgumentOutOfRangeException ("Index of out range");
374 return (MenuItem) items[index];
378 public virtual MenuItem this [string key] {
380 if (string.IsNullOrEmpty (key))
383 foreach (MenuItem m in items)
384 if (string.Compare (m.Name, key, true) == 0)
391 object IList.this[int index] {
392 get { return items[index]; }
393 set { throw new NotSupportedException (); }
396 #endregion Public Properties
398 #region Public Methods
400 public virtual int Add (MenuItem item)
402 if (item.Parent != null)
403 item.Parent.MenuItems.Remove (item);
406 item.Index = items.Count - 1;
409 owner.OnMenuChanged (EventArgs.Empty);
410 if (owner.parent_menu != null)
411 owner.parent_menu.OnMenuChanged (EventArgs.Empty);
412 return items.Count - 1;
415 internal void AddNoEvents (MenuItem mi)
417 if (mi.Parent != null)
418 mi.Parent.MenuItems.Remove (mi);
421 mi.Index = items.Count - 1;
422 mi.parent_menu = owner;
425 public virtual MenuItem Add (string caption)
427 MenuItem item = new MenuItem (caption);
432 public virtual int Add (int index, MenuItem item)
434 if (index < 0 || index > Count)
435 throw new ArgumentOutOfRangeException ("Index of out range");
437 ArrayList new_items = new ArrayList (Count + 1);
439 for (int i = 0; i < index; i++)
440 new_items.Add (items[i]);
442 new_items.Add (item);
444 for (int i = index; i < Count; i++)
445 new_items.Add (items[i]);
448 UpdateItemsIndices ();
454 private void UpdateItem (MenuItem mi)
456 mi.parent_menu = owner;
457 owner.OnMenuChanged (EventArgs.Empty);
458 if (owner.parent_menu != null)
459 owner.parent_menu.OnMenuChanged (EventArgs.Empty);
460 if (owner.Tracker != null)
461 owner.Tracker.AddShortcuts (mi);
464 internal void Insert (int index, MenuItem mi)
466 if (index < 0 || index > Count)
467 throw new ArgumentOutOfRangeException ("Index of out range");
469 items.Insert (index, mi);
471 UpdateItemsIndices ();
475 public virtual MenuItem Add (string caption, EventHandler onClick)
477 MenuItem item = new MenuItem (caption, onClick);
483 public virtual MenuItem Add (string caption, MenuItem[] items)
485 MenuItem item = new MenuItem (caption, items);
491 public virtual void AddRange (MenuItem[] items)
494 throw new ArgumentNullException ("items");
496 foreach (MenuItem mi in items)
500 public virtual void Clear ()
502 MenuTracker tracker = owner.Tracker;
503 foreach (MenuItem item in items) {
505 tracker.RemoveShortcuts (item);
506 item.parent_menu = null;
509 owner.OnMenuChanged (EventArgs.Empty);
512 public bool Contains (MenuItem value)
514 return items.Contains (value);
517 public virtual bool ContainsKey (string key)
519 return !(this[key] == null);
522 public void CopyTo (Array dest, int index)
524 items.CopyTo (dest, index);
527 public MenuItem[] Find (string key, bool searchAllChildren)
529 if (string.IsNullOrEmpty (key))
530 throw new ArgumentNullException ("key");
532 List<MenuItem> list = new List<MenuItem> ();
534 foreach (MenuItem m in items)
535 if (string.Compare (m.Name, key, true) == 0)
538 if (searchAllChildren)
539 foreach (MenuItem m in items)
540 list.AddRange (m.MenuItems.Find (key, true));
542 return list.ToArray ();
545 public IEnumerator GetEnumerator ()
547 return items.GetEnumerator ();
550 int IList.Add (object value)
552 return Add ((MenuItem)value);
555 bool IList.Contains (object value)
557 return Contains ((MenuItem)value);
560 int IList.IndexOf (object value)
562 return IndexOf ((MenuItem)value);
565 void IList.Insert (int index, object value)
567 Insert (index, (MenuItem) value);
570 void IList.Remove (object value)
572 Remove ((MenuItem) value);
575 public int IndexOf (MenuItem value)
577 return items.IndexOf (value);
580 public virtual int IndexOfKey (string key)
582 if (string.IsNullOrEmpty (key))
585 return IndexOf (this[key]);
588 public virtual void Remove (MenuItem item)
590 RemoveAt (item.Index);
593 public virtual void RemoveAt (int index)
595 if (index < 0 || index >= Count)
596 throw new ArgumentOutOfRangeException ("Index of out range");
598 MenuItem item = (MenuItem) items [index];
599 MenuTracker tracker = owner.Tracker;
601 tracker.RemoveShortcuts (item);
602 item.parent_menu = null;
604 items.RemoveAt (index);
606 UpdateItemsIndices ();
607 owner.OnMenuChanged (EventArgs.Empty);
610 public virtual void RemoveByKey (string key)
615 #endregion Public Methods
617 #region Private Methods
619 private void UpdateItemsIndices ()
621 for (int i = 0; i < Count; i++) // Recalculate indeces
622 ((MenuItem)items[i]).Index = i;
625 #endregion Private Methods