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