2007-03-29 Jonathan Pobst <monkey@jpobst.com>
[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                         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                 internal event EventHandler MenuChanged {
332                         add { Events.AddHandler (MenuChangedEvent, value); }
333                         remove { Events.RemoveHandler (MenuChangedEvent, value); }
334                 }
335
336                 [ListBindable(false)]
337                 public class MenuItemCollection : IList, ICollection, IEnumerable
338                 {
339                         private Menu owner;
340                         private ArrayList items = new ArrayList ();
341
342                         public MenuItemCollection (Menu owner)
343                         {
344                                 this.owner = owner;
345                         }
346
347                         #region Public Properties
348
349                         public int Count {
350                                 get { return items.Count;}
351                         }
352
353                         public bool IsReadOnly {
354                                 get { return false; }
355                         }
356
357                         bool ICollection.IsSynchronized {
358                                 get { return false;}
359                         }
360
361                         object ICollection.SyncRoot {
362                                 get { return this;}
363                         }
364
365                         bool IList.IsFixedSize {
366                                 get { return false;}
367                         }
368
369                         public virtual MenuItem this [int index] {
370                                 get {
371                                         if (index < 0 || index >= Count)
372                                                 throw new ArgumentOutOfRangeException ("Index of out range");
373
374                                         return (MenuItem) items[index];
375                                 }
376                         }
377
378                         object IList.this[int index] {
379                                 get { return items[index]; }
380                                 set { throw new NotSupportedException (); }
381                         }
382
383                         #endregion Public Properties
384
385                         #region Public Methods
386
387                         public virtual int Add (MenuItem mi)
388                         {
389                                 if (mi.Parent != null)
390                                         mi.Parent.MenuItems.Remove (mi);
391                                 
392                                 items.Add (mi);
393                                 mi.Index = items.Count - 1;
394                                 UpdateItem (mi);
395                                 
396                                 owner.OnMenuChanged (EventArgs.Empty);
397                                 if (owner.parent_menu != null)
398                                         owner.parent_menu.OnMenuChanged (EventArgs.Empty);
399                                 return items.Count - 1;
400                         }
401
402                         internal void AddNoEvents (MenuItem mi)
403                         {
404                                 if (mi.Parent != null)
405                                         mi.Parent.MenuItems.Remove (mi);
406                                 
407                                 items.Add (mi);
408                                 mi.Index = items.Count - 1;
409                                 mi.parent_menu = owner;
410                         }
411
412                         public virtual MenuItem Add (string s)
413                         {
414                                 MenuItem item = new MenuItem (s);
415                                 Add (item);
416                                 return item;
417                         }
418
419                         public virtual int Add (int index, MenuItem mi)
420                         {
421                                 if (index < 0 || index > Count)
422                                         throw new ArgumentOutOfRangeException ("Index of out range");
423
424                                 ArrayList new_items = new ArrayList (Count + 1);
425
426                                 for (int i = 0; i < index; i++)
427                                         new_items.Add (items[i]);
428
429                                 new_items.Add (mi);
430
431                                 for (int i = index; i < Count; i++)
432                                         new_items.Add (items[i]);
433
434                                 items = new_items;
435                                 UpdateItemsIndices ();                          
436                                 UpdateItem (mi);
437
438                                 return index;
439                         }
440
441                         private void UpdateItem (MenuItem mi)
442                         {
443                                 mi.parent_menu = owner;
444                                 owner.OnMenuChanged (EventArgs.Empty);
445                                 if (owner.parent_menu != null)
446                                         owner.parent_menu.OnMenuChanged (EventArgs.Empty);
447                                 if (owner.Tracker != null)
448                                         owner.Tracker.AddShortcuts (mi);
449                         }
450
451                         internal void Insert (int index, MenuItem mi)
452                         {
453                                 if (index < 0 || index > Count)
454                                         throw new ArgumentOutOfRangeException ("Index of out range");
455                                 
456                                 items.Insert (index, mi);
457                                 
458                                 UpdateItemsIndices ();
459                                 UpdateItem (mi);
460                         }
461
462                         public virtual MenuItem Add (string s, EventHandler e)
463                         {
464                                 MenuItem item = new MenuItem (s, e);
465                                 Add (item);
466
467                                 return item;
468                         }
469
470                         public virtual MenuItem Add (string s, MenuItem[] items)
471                         {
472                                 MenuItem item = new MenuItem (s, items);
473                                 Add (item);
474
475                                 return item;
476                         }
477
478                         public virtual void AddRange (MenuItem[] items)
479                         {
480                                 if (items == null)
481                                         throw new ArgumentNullException ("items");
482
483                                 foreach (MenuItem mi in items)
484                                         Add (mi);
485                         }
486
487                         public virtual void Clear ()
488                         {
489                                 MenuTracker tracker = owner.Tracker;
490                                 foreach (MenuItem item in items) {
491                                         if (tracker != null)
492                                                 tracker.RemoveShortcuts (item);
493                                         item.parent_menu = null;
494                                 }
495                                 items.Clear ();
496                                 owner.OnMenuChanged (EventArgs.Empty);
497                         }
498
499                         public bool Contains (MenuItem value)
500                         {
501                                 return items.Contains (value);
502                         }
503
504                         public void CopyTo (Array dest, int index)
505                         {
506                                 items.CopyTo (dest, index);
507                         }
508
509                         public IEnumerator GetEnumerator ()
510                         {
511                                 return items.GetEnumerator ();
512                         }
513
514                         int IList.Add (object value)
515                         {
516                                 return Add ((MenuItem)value);
517                         }
518
519                         bool IList.Contains (object value)
520                         {
521                                 return Contains ((MenuItem)value);
522                         }
523
524                         int IList.IndexOf (object value)
525                         {
526                                 return IndexOf ((MenuItem)value);
527                         }
528
529                         void IList.Insert (int index, object value)
530                         {
531                                 Insert (index, (MenuItem) value);
532                         }
533
534                         void IList.Remove (object value)
535                         {
536                                 Remove ((MenuItem) value);
537                         }
538
539                         public int IndexOf (MenuItem value)
540                         {
541                                 return items.IndexOf (value);
542                         }
543
544                         public virtual void Remove (MenuItem item)
545                         {
546                                 RemoveAt (item.Index);
547                         }
548
549                         public virtual void RemoveAt (int index)
550                         {
551                                 if (index < 0 || index >= Count)
552                                         throw new ArgumentOutOfRangeException ("Index of out range");
553
554                                 MenuItem item = (MenuItem) items [index];
555                                 MenuTracker tracker = owner.Tracker;
556                                 if (tracker != null)
557                                         tracker.RemoveShortcuts (item);
558                                 item.parent_menu = null;
559
560                                 items.RemoveAt (index);
561
562                                 UpdateItemsIndices ();
563                                 owner.OnMenuChanged (EventArgs.Empty);
564                         }
565
566                         #endregion Public Methods
567
568                         #region Private Methods
569
570                         private void UpdateItemsIndices ()
571                         {
572                                 for (int i = 0; i < Count; i++) // Recalculate indeces
573                                         ((MenuItem)items[i]).Index = i;
574                         }
575
576                         #endregion Private Methods
577                 }
578         }
579 }
580
581