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;
38 namespace System.Windows.Forms
40 [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
42 public abstract class Menu : Component
44 internal MenuItemCollection menu_items;
45 internal IntPtr menu_handle = IntPtr.Zero;
46 internal Menu parent_menu = null;
47 System.Drawing.Rectangle rect = new Rectangle ();
48 // UIA Framework Note: Used to keep track of expanded menus
50 internal MenuTracker tracker;
51 private string control_name;
52 private object control_tag;
53 public const int FindHandle = 0;
54 public const int FindShortcut = 1;
56 protected Menu (MenuItem[] items)
58 menu_items = new MenuItemCollection (this);
61 menu_items.AddRange (items);
64 #region Public Properties
66 [BrowsableAttribute(false)]
67 [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
68 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
69 public IntPtr Handle {
70 get { return menu_handle; }
73 internal virtual void OnMenuChanged (EventArgs e)
75 EventHandler eh = (EventHandler)(Events [MenuChangedEvent]);
80 [BrowsableAttribute(false)]
81 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
82 public virtual bool IsParent {
84 if (menu_items != null && menu_items.Count > 0)
91 [BrowsableAttribute(false)]
92 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
93 public MenuItem MdiListItem {
95 throw new NotImplementedException ();
99 [BrowsableAttribute(false)]
100 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
101 [MergableProperty(false)]
102 public MenuItemCollection MenuItems {
103 get { return menu_items; }
106 [BrowsableAttribute(false)]
107 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
109 get { return control_name; }
110 set { control_name = value; }
115 [TypeConverter(typeof(StringConverter))]
117 [MWFCategory("Data")]
119 get { return control_tag; }
120 set { control_tag = value; }
123 #endregion Public Properties
125 #region Private Properties
127 // UIA Framework Note: Used to obtain menu bounds
128 internal Rectangle Rect {
132 internal MenuItem SelectedItem {
134 foreach (MenuItem item in MenuItems)
142 internal int Height {
143 get { return rect.Height; }
144 set { rect.Height = value; }
148 get { return rect.Width; }
149 set { rect.Width = value; }
153 get { return rect.X; }
154 set { rect.X = value; }
158 get { return rect.Y; }
159 set { rect.Y = value; }
162 internal MenuTracker Tracker {
165 while (top.parent_menu != null)
166 top = top.parent_menu;
171 #endregion Private Properties
173 #region Public Methods
175 protected void CloneMenu (Menu menuSrc)
179 menu_items = new MenuItemCollection (this);
181 for (int i = 0; i < menuSrc.MenuItems.Count ; i++)
182 menu_items.Add (menuSrc.MenuItems [i].CloneMenu ());
185 protected virtual IntPtr CreateMenuHandle ()
190 protected override void Dispose (bool disposing)
193 if (menu_items != null) {
194 // MenuItem.Dispose removes the item from the list
195 while (menu_items.Count > 0) {
196 menu_items [0].Dispose ();
199 if (menu_handle != IntPtr.Zero) {
200 menu_handle = IntPtr.Zero;
205 // From Microsoft documentation is impossible to guess that
206 // this method is supossed to do
208 // update: according to MS documentation, first parameter is on of this
209 // constant values FindHandle or FindShortcut, value depends from what
210 // you what to search, by shortcut or handle. FindHandle and FindShortcut
211 // is a constant fields and was defined for this class.
212 public MenuItem FindMenuItem (int type, IntPtr value)
217 protected int FindMergePosition (int mergeOrder)
219 int cnt = MenuItems.Count, cur, pos;
221 for (pos = 0; pos < cnt; ) {
222 cur = (pos + cnt) /2;
223 if (MenuItems[cur].MergeOrder > mergeOrder) {
233 public ContextMenu GetContextMenu ()
235 for (Menu item = this; item != null; item = item.parent_menu) {
236 if (item is ContextMenu) {
237 return (ContextMenu) item;
244 public MainMenu GetMainMenu ()
246 for (Menu item = this; item != null; item = item.parent_menu) {
247 if (item is MainMenu) {
248 return (MainMenu) item;
255 internal virtual void InvalidateItem (MenuItem item)
258 Wnd.Invalidate (item.bounds);
261 public virtual void MergeMenu (Menu menuSrc)
264 throw new ArgumentException ("The menu cannot be merged with itself");
269 for (int i = 0; i < menuSrc.MenuItems.Count; i++) {
271 MenuItem sourceitem = menuSrc.MenuItems[i];
273 switch (sourceitem.MergeType) {
274 case MenuMerge.Remove: // Item not included
279 int pos = FindMergePosition (sourceitem.MergeOrder);
280 MenuItems.Add (pos, sourceitem.CloneMenu ());
284 case MenuMerge.Replace:
285 case MenuMerge.MergeItems:
287 for (int pos = FindMergePosition (sourceitem.MergeOrder-1); pos <= MenuItems.Count; pos++) {
289 if ((pos >= MenuItems.Count) || (MenuItems[pos].MergeOrder != sourceitem.MergeOrder)) {
290 MenuItems.Add (pos, sourceitem.CloneMenu ());
294 MenuItem mergeitem = MenuItems[pos];
296 if (mergeitem.MergeType != MenuMerge.Add) {
297 if ((sourceitem.MergeType == MenuMerge.MergeItems) && (mergeitem.MergeType == MenuMerge.MergeItems)) {
298 mergeitem.MergeMenu (sourceitem);
300 MenuItems.Remove (sourceitem);
301 MenuItems.Add (pos, sourceitem.CloneMenu ());
316 protected internal virtual bool ProcessCmdKey (ref Message msg, Keys keyData)
320 return tracker.ProcessKeys (ref msg, keyData);
323 public override string ToString ()
325 return base.ToString () + ", Items.Count: " + MenuItems.Count;
328 #endregion Public Methods
329 static object MenuChangedEvent = new object ();
331 // UIA Framework Note: Used to track changes in MenuItemCollection
332 internal event EventHandler MenuChanged {
333 add { Events.AddHandler (MenuChangedEvent, value); }
334 remove { Events.RemoveHandler (MenuChangedEvent, value); }
337 [ListBindable(false)]
338 public class MenuItemCollection : IList, ICollection, IEnumerable
341 private ArrayList items = new ArrayList ();
343 public MenuItemCollection (Menu owner)
348 #region Public Properties
351 get { return items.Count;}
354 public bool IsReadOnly {
355 get { return false; }
358 bool ICollection.IsSynchronized {
362 object ICollection.SyncRoot {
366 bool IList.IsFixedSize {
370 public virtual MenuItem this [int index] {
372 if (index < 0 || index >= Count)
373 throw new ArgumentOutOfRangeException ("Index of out range");
375 return (MenuItem) items[index];
379 public virtual MenuItem this [string key] {
381 if (string.IsNullOrEmpty (key))
384 foreach (MenuItem m in items)
385 if (string.Compare (m.Name, key, true) == 0)
392 object IList.this[int index] {
393 get { return items[index]; }
394 set { throw new NotSupportedException (); }
397 #endregion Public Properties
399 #region Public Methods
401 public virtual int Add (MenuItem item)
403 if (item.Parent != null)
404 item.Parent.MenuItems.Remove (item);
407 item.Index = items.Count - 1;
410 owner.OnMenuChanged (EventArgs.Empty);
411 if (owner.parent_menu != null)
412 owner.parent_menu.OnMenuChanged (EventArgs.Empty);
413 return items.Count - 1;
416 internal void AddNoEvents (MenuItem mi)
418 if (mi.Parent != null)
419 mi.Parent.MenuItems.Remove (mi);
422 mi.Index = items.Count - 1;
423 mi.parent_menu = owner;
426 public virtual MenuItem Add (string caption)
428 MenuItem item = new MenuItem (caption);
433 public virtual int Add (int index, MenuItem item)
435 if (index < 0 || index > Count)
436 throw new ArgumentOutOfRangeException ("Index of out range");
438 ArrayList new_items = new ArrayList (Count + 1);
440 for (int i = 0; i < index; i++)
441 new_items.Add (items[i]);
443 new_items.Add (item);
445 for (int i = index; i < Count; i++)
446 new_items.Add (items[i]);
449 UpdateItemsIndices ();
455 private void UpdateItem (MenuItem mi)
457 mi.parent_menu = owner;
458 owner.OnMenuChanged (EventArgs.Empty);
459 if (owner.parent_menu != null)
460 owner.parent_menu.OnMenuChanged (EventArgs.Empty);
461 if (owner.Tracker != null)
462 owner.Tracker.AddShortcuts (mi);
465 internal void Insert (int index, MenuItem mi)
467 if (index < 0 || index > Count)
468 throw new ArgumentOutOfRangeException ("Index of out range");
470 items.Insert (index, mi);
472 UpdateItemsIndices ();
476 public virtual MenuItem Add (string caption, EventHandler onClick)
478 MenuItem item = new MenuItem (caption, onClick);
484 public virtual MenuItem Add (string caption, MenuItem[] items)
486 MenuItem item = new MenuItem (caption, items);
492 public virtual void AddRange (MenuItem[] items)
495 throw new ArgumentNullException ("items");
497 foreach (MenuItem mi in items)
501 public virtual void Clear ()
503 MenuTracker tracker = owner.Tracker;
504 foreach (MenuItem item in items) {
506 tracker.RemoveShortcuts (item);
507 item.parent_menu = null;
510 owner.OnMenuChanged (EventArgs.Empty);
513 public bool Contains (MenuItem value)
515 return items.Contains (value);
518 public virtual bool ContainsKey (string key)
520 return !(this[key] == null);
523 public void CopyTo (Array dest, int index)
525 items.CopyTo (dest, index);
528 public MenuItem[] Find (string key, bool searchAllChildren)
530 if (string.IsNullOrEmpty (key))
531 throw new ArgumentNullException ("key");
533 List<MenuItem> list = new List<MenuItem> ();
535 foreach (MenuItem m in items)
536 if (string.Compare (m.Name, key, true) == 0)
539 if (searchAllChildren)
540 foreach (MenuItem m in items)
541 list.AddRange (m.MenuItems.Find (key, true));
543 return list.ToArray ();
546 public IEnumerator GetEnumerator ()
548 return items.GetEnumerator ();
551 int IList.Add (object value)
553 return Add ((MenuItem)value);
556 bool IList.Contains (object value)
558 return Contains ((MenuItem)value);
561 int IList.IndexOf (object value)
563 return IndexOf ((MenuItem)value);
566 void IList.Insert (int index, object value)
568 Insert (index, (MenuItem) value);
571 void IList.Remove (object value)
573 Remove ((MenuItem) value);
576 public int IndexOf (MenuItem value)
578 return items.IndexOf (value);
581 public virtual int IndexOfKey (string key)
583 if (string.IsNullOrEmpty (key))
586 return IndexOf (this[key]);
589 public virtual void Remove (MenuItem item)
591 RemoveAt (item.Index);
594 public virtual void RemoveAt (int index)
596 if (index < 0 || index >= Count)
597 throw new ArgumentOutOfRangeException ("Index of out range");
599 MenuItem item = (MenuItem) items [index];
600 MenuTracker tracker = owner.Tracker;
602 tracker.RemoveShortcuts (item);
603 item.parent_menu = null;
605 items.RemoveAt (index);
607 UpdateItemsIndices ();
608 owner.OnMenuChanged (EventArgs.Empty);
611 public virtual void RemoveByKey (string key)
616 #endregion Public Methods
618 #region Private Methods
620 private void UpdateItemsIndices ()
622 for (int i = 0; i < Count; i++) // Recalculate indeces
623 ((MenuItem)items[i]).Index = i;
626 #endregion Private Methods