Merge pull request #1275 from ranma42/fix-lib64
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Menu.cs
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:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
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.
19 //
20 // Copyright (c) 2004-2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jordi Mas i Hernandez, jordi@ximian.com
24 //
25 //      TODO:
26 //              - FindMenuItem
27 //              - MdiListItem
28 //
29
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;
36 using System.Drawing;
37
38 namespace System.Windows.Forms
39 {
40         [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
41         [ListBindable(false)]
42         public abstract class Menu : Component
43         {
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
49                 internal Control Wnd;
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;
55
56                 protected Menu (MenuItem[] items)
57                 {
58                         menu_items = new MenuItemCollection (this);
59
60                         if (items != null)
61                                 menu_items.AddRange (items);
62                 }
63
64                 #region Public Properties
65                 
66                 [BrowsableAttribute(false)]
67                 [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
68                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
69                 public IntPtr Handle {
70                         get { return menu_handle; }
71                 }
72
73                 internal virtual void OnMenuChanged (EventArgs e)
74                 {
75                         EventHandler eh = (EventHandler)(Events [MenuChangedEvent]);
76                         if (eh != null)
77                                 eh (this, e);
78                 }
79
80                 [BrowsableAttribute(false)]
81                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
82                 public virtual bool IsParent {
83                         get {
84                                 if (menu_items != null && menu_items.Count > 0)
85                                         return true;
86                                 else
87                                         return false;
88                         }
89                 }
90
91                 [BrowsableAttribute(false)]
92                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
93                 public MenuItem MdiListItem {
94                         get {
95                                 throw new NotImplementedException ();
96                         }
97                 }
98
99                 [BrowsableAttribute(false)]
100                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
101                 [MergableProperty(false)]
102                 public MenuItemCollection MenuItems {
103                         get { return menu_items; }
104                 }
105                 
106                 [BrowsableAttribute(false)]
107                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
108                 public string Name { 
109                         get { return control_name; } 
110                         set { control_name = value; }
111                 }
112
113                 [Localizable(false)]
114                 [Bindable(true)]
115                 [TypeConverter(typeof(StringConverter))]
116                 [DefaultValue(null)]
117                 [MWFCategory("Data")]
118                 public object Tag {
119                         get { return control_tag; }
120                         set { control_tag = value; }
121                 }
122
123                 #endregion Public Properties
124
125                 #region Private Properties
126
127                 // UIA Framework Note: Used to obtain menu bounds
128                 internal Rectangle Rect {
129                         get { return rect; }
130                 }
131
132                 internal MenuItem SelectedItem  {
133                         get {
134                                 foreach (MenuItem item in MenuItems)
135                                         if (item.Selected)
136                                                 return item;
137
138                                 return null;
139                         }
140                 }
141
142                 internal int Height {
143                         get { return rect.Height; }
144                         set { rect.Height = value; }
145                 }
146
147                 internal int Width {
148                         get { return rect.Width; }
149                         set { rect.Width = value; }
150                 }
151
152                 internal int X {
153                         get { return rect.X; }
154                         set { rect.X = value; }
155                 }
156
157                 internal int Y {
158                         get { return rect.Y; }
159                         set { rect.Y = value; }
160                 }
161
162                 internal MenuTracker Tracker {
163                         get {
164                                 Menu top = this;
165                                 while (top.parent_menu != null)
166                                         top = top.parent_menu;
167
168                                 return top.tracker;
169                         }
170                 }
171                 #endregion Private Properties
172
173                 #region Public Methods
174
175                 protected void CloneMenu (Menu menuSrc)
176                 {
177                         Dispose (true);
178
179                         menu_items = new MenuItemCollection (this);
180
181                         for (int i = 0; i < menuSrc.MenuItems.Count ; i++)
182                                 menu_items.Add (menuSrc.MenuItems [i].CloneMenu ());
183                 }
184
185                 protected virtual IntPtr CreateMenuHandle ()
186                 {
187                         return IntPtr.Zero;
188                 }
189
190                 protected override void Dispose (bool disposing)
191                 {
192                         if (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 ();
197                                         }
198                                 }
199                                 if (menu_handle != IntPtr.Zero) {
200                                         menu_handle = IntPtr.Zero;
201                                 }
202                         }
203                 }
204
205                 // From Microsoft documentation is impossible to guess that 
206                 // this method is supossed to do
207                 //
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)
213                 {
214                         return null;
215                 }
216
217                 protected int FindMergePosition (int mergeOrder)
218                 {
219                         int cnt = MenuItems.Count, cur, pos;
220                         
221                         for (pos = 0; pos < cnt; ) {
222                                 cur = (pos + cnt) /2;
223                                 if (MenuItems[cur].MergeOrder > mergeOrder) {
224                                         cnt = cur;
225                                 } else  {
226                                         pos = cur +1;
227                                 }
228                         }
229                         
230                         return pos;
231                 }
232
233                 public ContextMenu GetContextMenu ()
234                 {
235                         for (Menu item = this; item != null; item = item.parent_menu) {
236                                 if (item is ContextMenu) {
237                                         return (ContextMenu) item;
238                                 }
239                         }
240                         
241                         return null;
242                 }
243
244                 public MainMenu GetMainMenu ()
245                 {                               
246                         for (Menu item = this; item != null; item = item.parent_menu) {
247                                 if (item is MainMenu) {
248                                         return (MainMenu) item;
249                                 }                               
250                         }
251                         
252                         return null;
253                 }
254
255                 internal virtual void InvalidateItem (MenuItem item)
256                 {
257                         if (Wnd != null)
258                                 Wnd.Invalidate (item.bounds);
259                 }
260
261                 public virtual void MergeMenu (Menu menuSrc)
262                 {
263                         if (menuSrc == this)
264                                 throw new ArgumentException ("The menu cannot be merged with itself");
265                         
266                         if (menuSrc == null)
267                                 return;
268                                 
269                         for (int i = 0; i < menuSrc.MenuItems.Count; i++) {
270                                 
271                                 MenuItem sourceitem = menuSrc.MenuItems[i];
272                                 
273                                 switch (sourceitem.MergeType) {
274                                         case MenuMerge.Remove:  // Item not included
275                                                 break;
276                                                 
277                                         case MenuMerge.Add:
278                                         {
279                                                 int pos = FindMergePosition (sourceitem.MergeOrder);                                            
280                                                 MenuItems.Add (pos, sourceitem.CloneMenu ());
281                                                 break;                                  
282                                         }
283                                         
284                                         case MenuMerge.Replace:
285                                         case MenuMerge.MergeItems:
286                                         {
287                                                 for (int pos = FindMergePosition (sourceitem.MergeOrder-1); pos <= MenuItems.Count; pos++) {
288                                                         
289                                                         if  ((pos >= MenuItems.Count) || (MenuItems[pos].MergeOrder != sourceitem.MergeOrder)) {
290                                                                 MenuItems.Add (pos, sourceitem.CloneMenu ());
291                                                                 break;
292                                                         }
293                                                         
294                                                         MenuItem mergeitem = MenuItems[pos];
295                                                         
296                                                         if (mergeitem.MergeType != MenuMerge.Add) {
297                                                                 if ((sourceitem.MergeType == MenuMerge.MergeItems) && (mergeitem.MergeType == MenuMerge.MergeItems)) {
298                                                                         mergeitem.MergeMenu (sourceitem);
299                                                                 } else {
300                                                                         MenuItems.Remove (sourceitem);
301                                                                         MenuItems.Add (pos, sourceitem.CloneMenu ());
302                                                                 }
303                                                                 break;
304                                                         }
305                                                 }
306                                                 
307                                                 break;
308                                         }
309                                         
310                                         default:
311                                                 break;
312                                 }                       
313                         }               
314                 }
315
316                 protected internal virtual bool ProcessCmdKey (ref Message msg, Keys keyData)
317                 {
318                         if (tracker == null)
319                                 return false;
320                         return tracker.ProcessKeys (ref msg, keyData);
321                 }
322
323                 public override string ToString ()
324                 {
325                         return base.ToString () + ", Items.Count: " + MenuItems.Count;
326                 }
327
328                 #endregion Public Methods
329                 static object MenuChangedEvent = new object ();
330
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); }
335                 }
336
337                 [ListBindable(false)]
338                 public class MenuItemCollection : IList, ICollection, IEnumerable
339                 {
340                         private Menu owner;
341                         private ArrayList items = new ArrayList ();
342
343                         public MenuItemCollection (Menu owner)
344                         {
345                                 this.owner = owner;
346                         }
347
348                         #region Public Properties
349
350                         public int Count {
351                                 get { return items.Count;}
352                         }
353
354                         public bool IsReadOnly {
355                                 get { return false; }
356                         }
357
358                         bool ICollection.IsSynchronized {
359                                 get { return false;}
360                         }
361
362                         object ICollection.SyncRoot {
363                                 get { return this;}
364                         }
365
366                         bool IList.IsFixedSize {
367                                 get { return false;}
368                         }
369
370                         public virtual MenuItem this [int index] {
371                                 get {
372                                         if (index < 0 || index >= Count)
373                                                 throw new ArgumentOutOfRangeException ("Index of out range");
374
375                                         return (MenuItem) items[index];
376                                 }
377                         }
378
379                         public virtual MenuItem this [string key] {
380                                 get {
381                                         if (string.IsNullOrEmpty (key))
382                                                 return null;
383                                                 
384                                         foreach (MenuItem m in items)
385                                                 if (string.Compare (m.Name, key, true) == 0)
386                                                         return m;
387                                                         
388                                         return null;
389                                 }
390                         }
391
392                         object IList.this[int index] {
393                                 get { return items[index]; }
394                                 set { throw new NotSupportedException (); }
395                         }
396
397                         #endregion Public Properties
398
399                         #region Public Methods
400
401                         public virtual int Add (MenuItem item)
402                         {
403                                 if (item.Parent != null)
404                                         item.Parent.MenuItems.Remove (item);
405                                 
406                                 items.Add (item);
407                                 item.Index = items.Count - 1;
408                                 UpdateItem (item);
409                                 
410                                 owner.OnMenuChanged (EventArgs.Empty);
411                                 if (owner.parent_menu != null)
412                                         owner.parent_menu.OnMenuChanged (EventArgs.Empty);
413                                 return items.Count - 1;
414                         }
415
416                         internal void AddNoEvents (MenuItem mi)
417                         {
418                                 if (mi.Parent != null)
419                                         mi.Parent.MenuItems.Remove (mi);
420                                 
421                                 items.Add (mi);
422                                 mi.Index = items.Count - 1;
423                                 mi.parent_menu = owner;
424                         }
425
426                         public virtual MenuItem Add (string caption)
427                         {
428                                 MenuItem item = new MenuItem (caption);
429                                 Add (item);
430                                 return item;
431                         }
432
433                         public virtual int Add (int index, MenuItem item)
434                         {
435                                 if (index < 0 || index > Count)
436                                         throw new ArgumentOutOfRangeException ("Index of out range");
437
438                                 ArrayList new_items = new ArrayList (Count + 1);
439
440                                 for (int i = 0; i < index; i++)
441                                         new_items.Add (items[i]);
442
443                                 new_items.Add (item);
444
445                                 for (int i = index; i < Count; i++)
446                                         new_items.Add (items[i]);
447
448                                 items = new_items;
449                                 UpdateItemsIndices ();                          
450                                 UpdateItem (item);
451
452                                 return index;
453                         }
454
455                         private void UpdateItem (MenuItem mi)
456                         {
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);
463                         }
464
465                         internal void Insert (int index, MenuItem mi)
466                         {
467                                 if (index < 0 || index > Count)
468                                         throw new ArgumentOutOfRangeException ("Index of out range");
469                                 
470                                 items.Insert (index, mi);
471                                 
472                                 UpdateItemsIndices ();
473                                 UpdateItem (mi);
474                         }
475
476                         public virtual MenuItem Add (string caption, EventHandler onClick)
477                         {
478                                 MenuItem item = new MenuItem (caption, onClick);
479                                 Add (item);
480
481                                 return item;
482                         }
483
484                         public virtual MenuItem Add (string caption, MenuItem[] items)
485                         {
486                                 MenuItem item = new MenuItem (caption, items);
487                                 Add (item);
488
489                                 return item;
490                         }
491
492                         public virtual void AddRange (MenuItem[] items)
493                         {
494                                 if (items == null)
495                                         throw new ArgumentNullException ("items");
496
497                                 foreach (MenuItem mi in items)
498                                         Add (mi);
499                         }
500
501                         public virtual void Clear ()
502                         {
503                                 MenuTracker tracker = owner.Tracker;
504                                 foreach (MenuItem item in items) {
505                                         if (tracker != null)
506                                                 tracker.RemoveShortcuts (item);
507                                         item.parent_menu = null;
508                                 }
509                                 items.Clear ();
510                                 owner.OnMenuChanged (EventArgs.Empty);
511                         }
512
513                         public bool Contains (MenuItem value)
514                         {
515                                 return items.Contains (value);
516                         }
517
518                         public virtual bool ContainsKey (string key)
519                         {
520                                 return !(this[key] == null);
521                         }
522
523                         public void CopyTo (Array dest, int index)
524                         {
525                                 items.CopyTo (dest, index);
526                         }
527
528                         public MenuItem[] Find (string key, bool searchAllChildren)
529                         {
530                                 if (string.IsNullOrEmpty (key))
531                                         throw new ArgumentNullException ("key");
532                                         
533                                 List<MenuItem> list = new List<MenuItem> ();
534                                 
535                                 foreach (MenuItem m in items)
536                                         if (string.Compare (m.Name, key, true) == 0)
537                                                 list.Add (m);
538                                 
539                                 if (searchAllChildren)
540                                         foreach (MenuItem m in items)
541                                                 list.AddRange (m.MenuItems.Find (key, true));
542                                                 
543                                 return list.ToArray ();
544                         }
545
546                         public IEnumerator GetEnumerator ()
547                         {
548                                 return items.GetEnumerator ();
549                         }
550
551                         int IList.Add (object value)
552                         {
553                                 return Add ((MenuItem)value);
554                         }
555
556                         bool IList.Contains (object value)
557                         {
558                                 return Contains ((MenuItem)value);
559                         }
560
561                         int IList.IndexOf (object value)
562                         {
563                                 return IndexOf ((MenuItem)value);
564                         }
565
566                         void IList.Insert (int index, object value)
567                         {
568                                 Insert (index, (MenuItem) value);
569                         }
570
571                         void IList.Remove (object value)
572                         {
573                                 Remove ((MenuItem) value);
574                         }
575
576                         public int IndexOf (MenuItem value)
577                         {
578                                 return items.IndexOf (value);
579                         }
580
581                         public virtual int IndexOfKey (string key)
582                         {
583                                 if (string.IsNullOrEmpty (key))
584                                         return -1;
585                                         
586                                 return IndexOf (this[key]);
587                         }
588
589                         public virtual void Remove (MenuItem item)
590                         {
591                                 RemoveAt (item.Index);
592                         }
593
594                         public virtual void RemoveAt (int index)
595                         {
596                                 if (index < 0 || index >= Count)
597                                         throw new ArgumentOutOfRangeException ("Index of out range");
598
599                                 MenuItem item = (MenuItem) items [index];
600                                 MenuTracker tracker = owner.Tracker;
601                                 if (tracker != null)
602                                         tracker.RemoveShortcuts (item);
603                                 item.parent_menu = null;
604
605                                 items.RemoveAt (index);
606
607                                 UpdateItemsIndices ();
608                                 owner.OnMenuChanged (EventArgs.Empty);
609                         }
610
611                         public virtual void RemoveByKey (string key)
612                         {
613                                 Remove (this[key]);
614                         }
615
616                         #endregion Public Methods
617
618                         #region Private Methods
619
620                         private void UpdateItemsIndices ()
621                         {
622                                 for (int i = 0; i < Count; i++) // Recalculate indeces
623                                         ((MenuItem)items[i]).Index = i;
624                         }
625
626                         #endregion Private Methods
627                 }
628         }
629 }
630
631