- Implement 2.0 image key feature.
[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
36 namespace System.Windows.Forms
37 {
38 #if !NET_2_0
39         [Designer ("Microsoft.VisualStudio.Windows.Forms.MenuDesigner, " + Consts.AssemblyMicrosoft_VisualStudio, "System.ComponentModel.Design.IDesigner")]
40 #endif
41         [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
42         [ListBindable(false)]
43         public abstract class Menu : Component
44         {
45                 internal MenuItemCollection menu_items;
46                 internal IntPtr menu_handle = IntPtr.Zero;
47                 internal Menu parent_menu = null;
48                 System.Drawing.Rectangle rect;
49                 internal Control Wnd;
50                 internal MenuTracker tracker;
51
52 #if NET_2_0
53                 private string control_name;
54                 private object control_tag;
55 #endif
56
57                 public const int FindHandle = 0;
58                 public const int FindShortcut = 1;
59
60                 protected Menu (MenuItem[] items)
61                 {
62                         menu_items = new MenuItemCollection (this);
63
64                         if (items != null)
65                                 menu_items.AddRange (items);
66                 }
67
68                 #region Public Properties
69                 
70                 [BrowsableAttribute(false)]
71                 [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
72                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
73                 public IntPtr Handle {
74                         get { return menu_handle; }
75                 }
76
77                 internal virtual void OnMenuChanged (EventArgs e)
78                 {
79                         EventHandler eh = (EventHandler)(Events [MenuChangedEvent]);
80                         if (eh != null)
81                                 eh (this, e);
82                 }
83
84                 [BrowsableAttribute(false)]
85                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
86                 public virtual bool IsParent {
87                         get {
88                                 if (menu_items != null && menu_items.Count > 0)
89                                         return true;
90                                 else
91                                         return false;
92                         }
93                 }
94
95                 [BrowsableAttribute(false)]
96                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
97                 public MenuItem MdiListItem {
98                         get {
99                                 throw new NotImplementedException ();
100                         }
101                 }
102
103                 [BrowsableAttribute(false)]
104                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
105                 [MergableProperty(false)]
106                 public MenuItemCollection MenuItems {
107                         get { return menu_items; }
108                 }
109                 
110 #if NET_2_0
111
112                 [BrowsableAttribute(false)]
113                 [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
114                 public string Name { 
115                         get { return control_name; } 
116                         set { control_name = value; }
117                 }
118
119                 [Localizable(false)]
120                 [Bindable(true)]
121                 [TypeConverter(typeof(StringConverter))]
122                 [DefaultValue(null)]
123                 [MWFCategory("Data")]
124                 public object Tag {
125                         get { return control_tag; }
126                         set { control_tag = value; }
127                 }
128 #endif
129
130                 #endregion Public Properties
131
132                 #region Private Properties
133
134                 internal System.Drawing.Rectangle Rect {
135                         get { return rect; }
136                 }
137
138                 internal MenuItem SelectedItem  {
139                         get {
140                                 foreach (MenuItem item in MenuItems)
141                                         if (item.Selected)
142                                                 return item;
143
144                                 return null;
145                         }
146                 }
147
148                 internal int Height {
149                         get { return rect.Height; }
150                         set { rect.Height = value; }
151                 }
152
153                 internal int Width {
154                         get { return rect.Width; }
155                         set { rect.Width = value; }
156                 }
157
158                 internal int X {
159                         get { return rect.X; }
160                         set { rect.X = value; }
161                 }
162
163                 internal int Y {
164                         get { return rect.Y; }
165                         set { rect.Y = value; }
166                 }
167
168                 internal MenuTracker Tracker {
169                         get {
170                                 Menu top = this;
171                                 while (top.parent_menu != null)
172                                         top = top.parent_menu;
173
174                                 return top.tracker;
175                         }
176                 }
177                 #endregion Private Properties
178
179                 #region Public Methods
180
181                 protected void CloneMenu (Menu menuSrc)
182                 {
183                         Dispose (true);
184
185                         menu_items = new MenuItemCollection (this);
186
187                         for (int i = 0; i < menuSrc.MenuItems.Count ; i++)
188                                 menu_items.Add (menuSrc.MenuItems [i].CloneMenu ());
189                 }
190
191                 protected virtual IntPtr CreateMenuHandle ()
192                 {
193                         return IntPtr.Zero;
194                 }
195
196                 protected override void Dispose (bool disposing)
197                 {               
198                         if (disposing) {
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                         for (int i = 0; i < menuSrc.MenuItems.Count; i++) {
267                                 
268                                 MenuItem sourceitem = menuSrc.MenuItems[i];
269                                 
270                                 switch (sourceitem.MergeType) {
271                                         case MenuMerge.Remove:  // Item not included
272                                                 break;
273                                                 
274                                         case MenuMerge.Add:
275                                         {
276                                                 int pos = FindMergePosition (sourceitem.MergeOrder);                                            
277                                                 MenuItems.Add (pos, sourceitem.CloneMenu ());
278                                                 break;                                  
279                                         }
280                                         
281                                         case MenuMerge.Replace:
282                                         case MenuMerge.MergeItems:
283                                         {
284                                                 for (int pos = FindMergePosition (sourceitem.MergeOrder-1); pos <= MenuItems.Count; pos++) {
285                                                         
286                                                         if  ((pos >= MenuItems.Count) || (MenuItems[pos].MergeOrder != sourceitem.MergeOrder)) {
287                                                                 MenuItems.Add (pos, sourceitem.CloneMenu ());
288                                                                 break;
289                                                         }
290                                                         
291                                                         MenuItem mergeitem = MenuItems[pos];
292                                                         
293                                                         if (mergeitem.MergeType != MenuMerge.Add) {
294                                                                 if ((sourceitem.MergeType == MenuMerge.MergeItems) && (mergeitem.MergeType == MenuMerge.MergeItems)) {
295                                                                         mergeitem.MergeMenu (sourceitem);
296                                                                 } else {
297                                                                         MenuItems.Remove (sourceitem);
298                                                                         MenuItems.Add (pos, sourceitem.CloneMenu ());
299                                                                 }
300                                                                 break;
301                                                         }
302                                                 }
303                                                 
304                                                 break;
305                                         }
306                                         
307                                         default:
308                                                 break;
309                                 }                       
310                         }               
311                 }
312
313                 protected internal virtual bool ProcessCmdKey (ref Message msg, Keys keyData)
314                 {
315                         if (tracker == null)
316                                 return false;
317                         return tracker.ProcessKeys (ref msg, keyData);
318                 }
319
320                 public override string ToString ()
321                 {
322                         return base.ToString () + ", Items.Count: " + MenuItems.Count;
323                 }
324
325                 #endregion Public Methods
326                 static object MenuChangedEvent = new object ();
327
328                 internal event EventHandler MenuChanged {
329                         add { Events.AddHandler (MenuChangedEvent, value); }
330                         remove { Events.RemoveHandler (MenuChangedEvent, value); }
331                 }
332
333                 [ListBindable(false)]
334                 public class MenuItemCollection : IList, ICollection, IEnumerable
335                 {
336                         private Menu owner;
337                         private ArrayList items = new ArrayList ();
338
339                         public MenuItemCollection (Menu owner)
340                         {
341                                 this.owner = owner;
342                         }
343
344                         #region Public Properties
345
346                         public int Count {
347                                 get { return items.Count;}
348                         }
349
350                         public bool IsReadOnly {
351                                 get { return false; }
352                         }
353
354                         bool ICollection.IsSynchronized {
355                                 get { return false;}
356                         }
357
358                         object ICollection.SyncRoot {
359                                 get { return this;}
360                         }
361
362                         bool IList.IsFixedSize {
363                                 get { return false;}
364                         }
365
366                         public virtual MenuItem this [int index] {
367                                 get {
368                                         if (index < 0 || index >= Count)
369                                                 throw new ArgumentOutOfRangeException ("Index of out range");
370
371                                         return (MenuItem) items[index];
372                                 }
373                         }
374
375                         object IList.this[int index] {
376                                 get { return items[index]; }
377                                 set { throw new NotSupportedException (); }
378                         }
379
380                         #endregion Public Properties
381
382                         #region Public Methods
383
384                         public virtual int Add (MenuItem mi)
385                         {
386                                 if (mi.Parent != null)
387                                         mi.Parent.MenuItems.Remove (mi);
388                                 
389                                 items.Add (mi);
390                                 mi.Index = items.Count - 1;
391                                 UpdateItem (mi);
392                                 
393                                 owner.OnMenuChanged (EventArgs.Empty);
394                                 if (owner.parent_menu != null)
395                                         owner.parent_menu.OnMenuChanged (EventArgs.Empty);
396                                 return items.Count - 1;
397                         }
398
399                         internal void AddNoEvents (MenuItem mi)
400                         {
401                                 if (mi.Parent != null)
402                                         mi.Parent.MenuItems.Remove (mi);
403                                 
404                                 items.Add (mi);
405                                 mi.Index = items.Count - 1;
406                                 mi.parent_menu = owner;
407                         }
408
409                         public virtual MenuItem Add (string s)
410                         {
411                                 MenuItem item = new MenuItem (s);
412                                 Add (item);
413                                 return item;
414                         }
415
416                         public virtual int Add (int index, MenuItem mi)
417                         {
418                                 if (index < 0 || index > Count)
419                                         throw new ArgumentOutOfRangeException ("Index of out range");
420
421                                 ArrayList new_items = new ArrayList (Count + 1);
422
423                                 for (int i = 0; i < index; i++)
424                                         new_items.Add (items[i]);
425
426                                 new_items.Add (mi);
427
428                                 for (int i = index; i < Count; i++)
429                                         new_items.Add (items[i]);
430
431                                 items = new_items;
432                                 UpdateItemsIndices ();                          
433                                 UpdateItem (mi);
434
435                                 return index;
436                         }
437
438                         private void UpdateItem (MenuItem mi)
439                         {
440                                 mi.parent_menu = owner;
441                                 owner.OnMenuChanged (EventArgs.Empty);
442                                 if (owner.parent_menu != null)
443                                         owner.parent_menu.OnMenuChanged (EventArgs.Empty);
444                                 if (owner.Tracker != null)
445                                         owner.Tracker.AddShortcuts (mi);
446                         }
447
448                         internal void Insert (int index, MenuItem mi)
449                         {
450                                 if (index < 0 || index > Count)
451                                         throw new ArgumentOutOfRangeException ("Index of out range");
452                                 
453                                 items.Insert (index, mi);
454                                 
455                                 UpdateItemsIndices ();
456                                 UpdateItem (mi);
457                         }
458
459                         public virtual MenuItem Add (string s, EventHandler e)
460                         {
461                                 MenuItem item = new MenuItem (s, e);
462                                 Add (item);
463
464                                 return item;
465                         }
466
467                         public virtual MenuItem Add (string s, MenuItem[] items)
468                         {
469                                 MenuItem item = new MenuItem (s, items);
470                                 Add (item);
471
472                                 return item;
473                         }
474
475                         public virtual void AddRange (MenuItem[] items)
476                         {
477                                 if (items == null)
478                                         throw new ArgumentNullException ("items");
479
480                                 foreach (MenuItem mi in items)
481                                         Add (mi);
482                         }
483
484                         public virtual void Clear ()
485                         {
486                                 MenuTracker tracker = owner.Tracker;
487                                 foreach (MenuItem item in items) {
488                                         if (tracker != null)
489                                                 tracker.RemoveShortcuts (item);
490                                         item.parent_menu = null;
491                                 }
492                                 items.Clear ();
493                                 owner.OnMenuChanged (EventArgs.Empty);
494                         }
495
496                         public bool Contains (MenuItem value)
497                         {
498                                 return items.Contains (value);
499                         }
500
501                         public void CopyTo (Array dest, int index)
502                         {
503                                 items.CopyTo (dest, index);
504                         }
505
506                         public IEnumerator GetEnumerator ()
507                         {
508                                 return items.GetEnumerator ();
509                         }
510
511                         int IList.Add (object value)
512                         {
513                                 return Add ((MenuItem)value);
514                         }
515
516                         bool IList.Contains (object value)
517                         {
518                                 return Contains ((MenuItem)value);
519                         }
520
521                         int IList.IndexOf (object value)
522                         {
523                                 return IndexOf ((MenuItem)value);
524                         }
525
526                         void IList.Insert (int index, object value)
527                         {
528                                 Insert (index, (MenuItem) value);
529                         }
530
531                         void IList.Remove (object value)
532                         {
533                                 Remove ((MenuItem) value);
534                         }
535
536                         public int IndexOf (MenuItem value)
537                         {
538                                 return items.IndexOf (value);
539                         }
540
541                         public virtual void Remove (MenuItem item)
542                         {
543                                 RemoveAt (item.Index);
544                         }
545
546                         public virtual void RemoveAt (int index)
547                         {
548                                 if (index < 0 || index >= Count)
549                                         throw new ArgumentOutOfRangeException ("Index of out range");
550
551                                 MenuItem item = (MenuItem) items [index];
552                                 MenuTracker tracker = owner.Tracker;
553                                 if (tracker != null)
554                                         tracker.RemoveShortcuts (item);
555                                 item.parent_menu = null;
556
557                                 items.RemoveAt (index);
558
559                                 UpdateItemsIndices ();
560                                 owner.OnMenuChanged (EventArgs.Empty);
561                         }
562
563                         #endregion Public Methods
564
565                         #region Private Methods
566
567                         private void UpdateItemsIndices ()
568                         {
569                                 for (int i = 0; i < Count; i++) // Recalculate indeces
570                                         ((MenuItem)items[i]).Index = i;
571                         }
572
573                         #endregion Private Methods
574                 }
575         }
576 }
577
578