2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / MenuAPI.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 Novell, Inc.
21 //
22 // Authors:
23 //      Jordi Mas i Hernandez, jordi@ximian.com
24 //
25
26 // NOT COMPLETE
27
28 using System.Drawing;
29 using System.Drawing.Text;
30 using System.Collections;
31
32 namespace System.Windows.Forms
33 {
34
35         /*
36                 This class mimics the Win32 API Menu functionality
37
38                 When writing this code the Wine project was of great help to
39                 understand the logic behind some Win32 issues. Thanks to them. Jordi,
40         */
41         internal class MenuAPI
42         {
43                 static StringFormat string_format_text = new StringFormat ();
44                 static StringFormat string_format_shortcut = new StringFormat ();
45                 static StringFormat string_format_menubar_text = new StringFormat ();
46                 static ArrayList menu_list = new ArrayList ();          
47                 static Font MENU_FONT = new Font (FontFamily.GenericSansSerif, 8.25f);
48                 static int POPUP_ARROW_WITDH;
49                 static int POPUP_ARROW_HEIGHT;
50                 const int SEPARATOR_HEIGHT = 5;
51                 const int SM_CXBORDER = 1;
52                 const int SM_CYBORDER = 1;
53                 const int SM_CXMENUCHECK = 14;          // Width of the menu check
54                 const int SM_CYMENUCHECK = 14;          // Height of the menu check
55                 const int SM_CXARROWCHECK = 16;         // Width of the arrow
56                 const int SM_CYARROWCHECK = 16;         // Height of the arrow
57                 const int SM_CYMENU = 18;               // Minimum height of a menu
58                 const int MENU_TAB_SPACE = 8;           // Pixels added to the width of an item because of a tab
59                 const int MENU_BAR_ITEMS_SPACE = 8;     // Space between menu bar items
60
61                 public class MENU
62                 {
63                         public MF               Flags;          // Menu flags (MF_POPUP, MF_SYSMENU)
64                         public int              Width;          // Width of the whole menu
65                         public int              Height;         // Height of the whole menu
66                         public Control          Wnd;            // In a Popup menu is the PopupWindow and in a MenuBar the Form
67                         public ArrayList        items;          // Array of menu items
68                         public int              FocusedItem;    // Currently focused item
69                         public IntPtr           hParent;                        
70                         public MENUITEM         SelectedItem;   // Currently selected item
71                         public bool             bMenubar;
72                         public bool             bTracking;
73                         public Menu             menu;           // SWF.Menu 
74
75                         public MENU (Menu menu_obj)
76                         {
77                                 Wnd = null;
78                                 hParent = IntPtr.Zero;
79                                 items = new ArrayList ();
80                                 Flags = MF.MF_INSERT;
81                                 Width = Height = FocusedItem = 0;                               
82                                 bMenubar = false;
83                                 bTracking = false;
84                                 menu = menu_obj;
85                         }
86
87                 }
88
89                 public class MENUITEM
90                 {
91                         public  MenuItem        item;
92                         public  Rectangle       rect;
93                         public  MF              fState;
94                         public  int             wID;
95                         public  IntPtr          hSubMenu;
96                         public  int             xTab;
97                         public  int             pos;    /* Position in the menuitems array*/
98
99                         public MENUITEM ()
100                         {
101                                 xTab = 0;
102                                 wID = 0;
103                                 pos = 0;
104                                 rect = new Rectangle ();
105                         }
106
107                 };
108
109                 public class TRACKER
110                 {
111                         public  IntPtr  hCurrentMenu;
112                         public  IntPtr  hTopMenu;
113
114                         public TRACKER ()
115                         {
116                                 hCurrentMenu = hTopMenu = IntPtr.Zero;
117                         }
118                 };
119
120                 public enum MenuMouseEvent
121                 {
122                         Down,
123                         Move,
124                 }
125
126                 internal enum ItemNavigation
127                 {
128                         First,
129                         Last,
130                         Next,
131                         Previous,
132                 }
133
134                 internal enum MF
135                 {
136                         MF_INSERT           = 0x0,
137                         MF_APPEND           = 0x100,
138                         MF_DELETE           = 0x200,
139                         MF_REMOVE           = 0x1000,
140                         MF_BYCOMMAND        = 0,
141                         MF_BYPOSITION       = 0x400,
142                         MF_SEPARATOR        = 0x800,
143                         MF_ENABLED          = 0,
144                         MF_GRAYED           = 1,
145                         MF_DISABLED         = 2,
146                         MF_UNCHECKED        = 0,
147                         MF_CHECKED          = 8,
148                         MF_USECHECKBITMAPS  = 0x200,
149                         MF_STRING           = 0,
150                         MF_BITMAP           = 4,
151                         MF_OWNERDRAW        = 0x100,
152                         MF_POPUP            = 0x10,
153                         MF_MENUBARBREAK     = 0x20,
154                         MF_MENUBREAK        = 0x40,
155                         MF_UNHILITE         = 0,
156                         MF_HILITE           = 0x80,
157                         MF_DEFAULT          = 0x1000,
158                         MF_SYSMENU          = 0x2000,
159                         MF_HELP             = 0x4000,
160                         MF_RIGHTJUSTIFY     = 0x4000,
161                         MF_MENUBAR          = 0x8000    // Internal
162                 }
163
164                 static MenuAPI ()
165                 {
166                         string_format_text.LineAlignment = StringAlignment.Center;
167                         string_format_text.Alignment = StringAlignment.Near;
168                         string_format_text.HotkeyPrefix = HotkeyPrefix.Show;
169
170                         string_format_shortcut.LineAlignment = StringAlignment.Center;
171                         string_format_shortcut.Alignment = StringAlignment.Far;
172
173                         string_format_menubar_text.LineAlignment = StringAlignment.Center;
174                         string_format_menubar_text.Alignment = StringAlignment.Center;
175                         string_format_menubar_text.HotkeyPrefix = HotkeyPrefix.Show;                    
176                 }               
177                 
178                 static public IntPtr StoreMenuID (MENU menu)
179                 {
180                         int id = menu_list.Add (menu);
181                         return (IntPtr)(id + 1);
182                 }
183
184                 static public MENU GetMenuFromID (IntPtr ptr)
185                 {
186                         int id = (int)ptr;
187                         id = id - 1;
188
189                         if (menu_list[id] == null)      // It has been delete it
190                                 return null;
191
192                         return (MENU) menu_list[id];
193                 }
194
195                 static public IntPtr CreateMenu (Menu menu_obj)
196                 {
197                         MENU menu = new MENU (menu_obj);                        
198                         return StoreMenuID (menu);
199                 }
200
201                 static public IntPtr CreatePopupMenu (Menu menu_obj)
202                 {
203                         MENU popMenu = new MENU (menu_obj);
204                         popMenu.Flags |= MF.MF_POPUP;                   
205                         return StoreMenuID (popMenu);
206                 }
207
208                 static public int InsertMenuItem (IntPtr hMenu, int uItem, bool fByPosition, MenuItem item, ref IntPtr hSubMenu)
209                 {
210                         int id;
211                         
212                         if (fByPosition == false)
213                                 throw new NotImplementedException ();
214
215                         MENU menu = GetMenuFromID (hMenu);
216                         if ((uint)uItem > menu.items.Count)
217                                 uItem =  menu.items.Count;
218
219                         MENUITEM menu_item = new MENUITEM ();
220                         menu_item.item = item;
221
222                         if (item.IsPopup) {
223                                 menu_item.hSubMenu = CreatePopupMenu (menu_item.item);
224                                 MENU submenu = GetMenuFromID (menu_item.hSubMenu);
225                                 submenu.hParent = hMenu;
226                         }
227                         else
228                                 menu_item.hSubMenu = IntPtr.Zero;
229
230                         hSubMenu = menu_item.hSubMenu;
231                         id = menu.items.Count;
232                         menu_item.pos = menu.items.Count;
233                         menu.items.Insert (uItem, menu_item);
234
235                         return id;
236                 }
237
238                 // The Point object contains screen coordinates
239                 static public bool TrackPopupMenu (IntPtr hTopMenu, IntPtr hMenu, Point pnt, bool bMenubar, Control Wnd)
240                 {
241                         TRACKER tracker = new TRACKER ();                       
242                         MENU top_menu = GetMenuFromID (hTopMenu);
243                         MENU menu = null;
244
245                         if (hMenu == IntPtr.Zero)       // No submenus to track
246                                 return true;                            
247
248                         menu = GetMenuFromID (hMenu);
249                         
250                         Console.WriteLine ("TrackPopupMenu hTopMenu: {0} hMenu:{1} bMenubar:{2} top_menu.bMenubar: {3} menu.bMenubar: {4}",hTopMenu,
251                                 hMenu, bMenubar, top_menu.bMenubar, menu.bMenubar);
252                         
253                         
254                         menu.Wnd = new PopUpWindow (hMenu, tracker);
255                         tracker.hCurrentMenu = hMenu;
256                         tracker.hTopMenu = hTopMenu;
257
258                         MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
259
260                         if (select_item != null) {
261                                 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
262                         }
263
264                         // Make sure the menu is always visible and does not 'leave' the screen
265                         // What is menu.Width/Height? It seemed to be 0/0
266                         if ((pnt.X + menu.Wnd.Width) > SystemInformation.WorkingArea.Width) {
267                                 pnt.X -= menu.Wnd.Width;
268                         }
269
270                         if ((pnt.X + menu.Wnd.Height) > SystemInformation.WorkingArea.Height) {
271                                 pnt.Y -= menu.Wnd.Height;
272                         }
273
274                         menu.Wnd.Location =  menu.Wnd.PointToClient (pnt);                      
275                         
276                         if (menu.menu.IsDirty) {                                
277                                 menu.items.Clear ();
278                                 menu.menu.CreateItems ();
279                                 ((PopUpWindow)menu.Wnd).RefreshItems ();
280                                 menu.menu.IsDirty = false;
281                         }
282                         
283                         ((PopUpWindow)menu.Wnd).ShowWindow ();
284
285                         Application.Run ();
286
287                         if (menu.Wnd == null) {
288                                 menu.Wnd.Dispose ();
289                                 menu.Wnd = null;
290                         }
291
292                         return true;
293                 }
294
295                 /*
296                         Menu drawing API
297                 */
298
299                 static public void CalcItemSize (Graphics dc, MENUITEM item, int y, int x, bool menuBar)
300                 {
301                         item.rect.Y = y;
302                         item.rect.X = x;
303
304                         if (item.item.Visible == false)
305                                 return;
306
307                         if (item.item.Separator == true) {
308                                 item.rect.Height = SEPARATOR_HEIGHT / 2;
309                                 item.rect.Width = -1;
310                                 return;
311                         }
312
313                         SizeF size;
314                         size =  dc.MeasureString (item.item.Text, MENU_FONT);
315                         item.rect.Width = (int) size.Width;
316                         item.rect.Height = (int) size.Height;
317
318                         if (!menuBar) {
319
320                                 if (item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
321                                         item.xTab = SM_CXMENUCHECK + MENU_TAB_SPACE + (int) size.Width;
322                                         size =  dc.MeasureString (" " + item.item.GetShortCutText (), MENU_FONT);
323                                         item.rect.Width += MENU_TAB_SPACE + (int) size.Width;
324                                 }
325
326                                 item.rect.Width += 4 + (SM_CXMENUCHECK * 2);
327                         }
328                         else {
329                                 item.rect.Width += MENU_BAR_ITEMS_SPACE;
330                                 x += item.rect.Width;
331                         }
332
333                         if (item.rect.Height < SM_CYMENU - 1)
334                                 item.rect.Height = SM_CYMENU - 1;
335                 }
336
337                 static public void CalcPopupMenuSize (Graphics dc, IntPtr hMenu)
338                 {
339                         int x = 3;
340                         int start = 0;
341                         int i, n, y, max;
342
343                         MENU menu = GetMenuFromID (hMenu);
344                         menu.Height = 0;
345
346                         while (start < menu.items.Count) {
347                                 y = 2;
348                                 max = 0;
349                                 for (i = start; i < menu.items.Count; i++) {
350                                         MENUITEM item = (MENUITEM) menu.items[i];
351
352                                         if ((i != start) && (item.item.Break || item.item.BarBreak))
353                                                 break;
354
355                                         CalcItemSize (dc, item, y, x, false);
356                                         y += item.rect.Height;
357
358                                         if (item.rect.Width > max)
359                                                 max = item.rect.Width;
360                                 }
361
362                                 // Reemplace the -1 by the menu width (separators)
363                                 for (n = start; n < i; n++, start++) {
364                                         MENUITEM item = (MENUITEM) menu.items[n];
365                                         item.rect.Width = max;
366                                 }
367
368                                 if (y > menu.Height)
369                                         menu.Height = y;
370
371                                 x+= max;
372                         }
373
374                         menu.Width = x;
375
376                         //space for border
377                         menu.Width += 2;
378                         menu.Height += 2;
379
380                         menu.Width += SM_CXBORDER;
381                         menu.Height += SM_CYBORDER;
382                 }
383
384                 static public void DrawMenuItem (Graphics dc, MENUITEM item, int menu_height, bool menuBar)
385                 {
386                         StringFormat string_format;
387
388                         if (item.item.Visible == false)
389                                 return;
390
391                         if (menuBar)
392                                 string_format = string_format_menubar_text;
393                         else
394                                 string_format = string_format_text;
395
396                         if (item.item.Separator == true) {
397
398                                 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
399                                         item.rect.X, item.rect.Y, item.rect.X + item.rect.Width, item.rect.Y);
400
401                                 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
402                                         item.rect.X, item.rect.Y + 1, item.rect.X + item.rect.Width, item.rect.Y + 1);
403
404                                 return;
405                         }
406
407                         Rectangle rect_text = item.rect;
408
409                         if (!menuBar)
410                                 rect_text.X += SM_CXMENUCHECK;
411
412                         if (item.item.BarBreak) { /* Draw vertical break bar*/
413
414                                 Rectangle rect = item.rect;
415                                 rect.Y++;
416                                 rect.Width = 3;
417                                 rect.Height = menu_height - 6;
418
419                                 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
420                                         rect.X, rect.Y , rect.X, rect.Y + rect.Height);
421
422                                 dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
423                                         rect.X + 1, rect.Y , rect.X +1, rect.Y + rect.Height);
424
425                         }
426
427                         if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE) {
428                                 Rectangle rect = item.rect;
429                                 rect.X++;
430                                 rect.Width -=2;
431
432                                 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
433                                         (ThemeEngine.Current.ColorHilight), rect);
434                         }
435
436                         if (item.item.Enabled) {
437
438                                 Color color_text;
439
440                                 if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE)
441                                         color_text = ThemeEngine.Current.ColorHilightText;
442                                 else
443                                         color_text = ThemeEngine.Current.ColorMenuText;
444
445
446                                 dc.DrawString (item.item.Text, MENU_FONT,
447                                         ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
448                                         rect_text, string_format);
449
450                                 if (!menuBar && item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
451
452                                         string str = item.item.GetShortCutText ();
453                                         Rectangle rect = rect_text;
454                                         rect.X = item.xTab;
455                                         rect.Width -= item.xTab;
456
457                                         dc.DrawString (str, MENU_FONT, ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
458                                                 rect, string_format_shortcut);
459
460                                 }
461                         }
462                         else {
463                                 ControlPaint.DrawStringDisabled (dc,
464                                         item.item.Text, MENU_FONT, Color.Black, rect_text,
465                                         string_format);
466                         }
467
468                         /* Draw arrow */
469                         if (menuBar == false && item.item.IsPopup) {
470
471                                 Bitmap  bmp = new Bitmap (SM_CXARROWCHECK, SM_CYARROWCHECK);
472                                 Graphics gr = Graphics.FromImage (bmp);
473                                 Rectangle rect_arrow = new Rectangle (0, 0, SM_CXARROWCHECK, SM_CYARROWCHECK);
474                                 ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Arrow);
475                                 bmp.MakeTransparent ();
476                                 dc.DrawImage (bmp, item.rect.X + item.rect.Width - SM_CXARROWCHECK,
477                                         item.rect.Y + ((item.rect.Height - SM_CYARROWCHECK) /2));
478
479                                 gr.Dispose ();
480                                 bmp.Dispose ();
481                         }
482
483                         /* Draw checked or radio */
484                         if (menuBar == false && item.item.Checked) {
485
486                                 Rectangle area = item.rect;
487                                 Bitmap  bmp = new Bitmap (SM_CXMENUCHECK, SM_CYMENUCHECK);
488                                 Graphics gr = Graphics.FromImage (bmp);
489                                 Rectangle rect_arrow = new Rectangle (0, 0, SM_CXMENUCHECK, SM_CYMENUCHECK);
490
491                                 if (item.item.RadioCheck)
492                                         ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Bullet);
493                                 else
494                                         ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Checkmark);
495
496                                 bmp.MakeTransparent ();
497                                 dc.DrawImage (bmp, area.X, item.rect.Y + ((item.rect.Height - SM_CYMENUCHECK) / 2));
498
499                                 gr.Dispose ();
500                                 bmp.Dispose ();
501                         }
502
503                 }
504
505                 static public void DrawPopupMenu (Graphics dc, IntPtr hMenu, Rectangle cliparea, Rectangle rect)
506                 {
507                         MENU menu = GetMenuFromID (hMenu);
508
509                         dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
510                                 (ThemeEngine.Current.ColorMenu), cliparea);
511
512                         /* Draw menu borders */
513                         dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
514                                 rect.X, rect.Y, rect.X + rect.Width, rect.Y);
515
516                         dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
517                                 rect.X, rect.Y, rect.X, rect.Y + rect.Height);
518
519                         dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
520                                 rect.X + rect.Width - 1 , rect.Y , rect.X + rect.Width - 1, rect.Y + rect.Height);
521
522                         dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
523                                 rect.X + rect.Width, rect.Y , rect.X + rect.Width, rect.Y + rect.Height);
524
525                         dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
526                                 rect.X , rect.Y + rect.Height - 1 , rect.X + rect.Width - 1, rect.Y + rect.Height -1);
527
528                         dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
529                                 rect.X , rect.Y + rect.Height, rect.X + rect.Width - 1, rect.Y + rect.Height);
530
531                         for (int i = 0; i < menu.items.Count; i++)
532                                 if (cliparea.IntersectsWith (((MENUITEM) menu.items[i]).rect)) {
533                                         DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, menu.bMenubar);
534
535                         }
536                 }
537
538                 // Updates the menu rect and returns the height
539                 static public int MenuBarCalcSize (Graphics dc, IntPtr hMenu, int width)
540                 {
541                         int x = 0;
542                         int i = 0;
543                         int y = 0;
544                         MENU menu = GetMenuFromID (hMenu);
545                         menu.Height = 0;
546                         MENUITEM item;
547
548                         while (i < menu.items.Count) {
549
550                                 item = (MENUITEM) menu.items[i];
551                                 CalcItemSize (dc, item, y, x, true);
552                                 i = i + 1;
553
554                                 if (x + item.rect.Width > width) {
555                                         item.rect.X = 0;
556                                         y += item.rect.Height;
557                                         item.rect.Y = y;
558                                         x = 0;
559                                 }
560
561                                 x += item.rect.Width;
562                                 item.fState |= MF.MF_MENUBAR;
563
564                                 if (y + item.rect.Height > menu.Height)
565                                         menu.Height = item.rect.Height + y;
566                         }
567
568                         menu.Width = width;
569                         return menu.Height;
570                 }
571
572                 // Draws a menu bar in a Window
573                 static public void DrawMenuBar (Graphics dc, IntPtr hMenu, Rectangle rect)
574                 {
575                         MENU menu = GetMenuFromID (hMenu);
576                         Rectangle rect_menu = new Rectangle ();
577
578                         if (menu.Height == 0)
579                                 MenuBarCalcSize (dc, hMenu, rect_menu.Width);
580
581                         rect.Height = menu.Height;
582                         rect.Width = menu.Width;
583
584                         for (int i = 0; i < menu.items.Count; i++)
585                                 DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, true);
586                 }
587
588                 /*
589                         Menu handeling API
590                 */
591
592                 static public MENUITEM FindItemByCoords (IntPtr hMenu, Point pt)
593                 {
594                         MENU menu = GetMenuFromID (hMenu);
595
596                         for (int i = 0; i < menu.items.Count; i++) {
597                                 MENUITEM item = (MENUITEM) menu.items[i];
598                                 if (item.rect.Contains (pt)) {
599                                         return item;
600                                 }
601                         }
602
603                         return null;
604                 }
605                 
606                 // Get the current selected item
607                 static public MENUITEM GetSelected (IntPtr hMenu)
608                 {
609                         MENU menu = GetMenuFromID (hMenu);                      
610                         MENUITEM it;
611                         
612                         /* Loop all items */
613                         for (int i = 0; i < menu.items.Count; i++) {
614                                 it = (MENUITEM) menu.items[i];
615                                 if ((it.fState & MF.MF_HILITE) == MF.MF_HILITE) {
616                                         return it;
617                                 }                               
618                         }
619                         
620                         return null;
621                 }
622                 
623                 static public void UnSelectItem (IntPtr hMenu, MENUITEM item)
624                 {                       
625                         MENU menu = GetMenuFromID (hMenu);
626                         
627                         if (item == null)
628                                 return;                         
629                         
630                         item.fState = item.fState & ~MF.MF_HILITE;
631                         menu.Wnd.Invalidate (item.rect);
632                 }
633
634                 // Select the item and unselect the previous selecte item
635                 static public void SelectItem (IntPtr hMenu, MENUITEM item, bool execute, TRACKER tracker)
636                 {
637                         MENU menu = GetMenuFromID (hMenu);
638                         MENUITEM previous_selitem = GetSelected (hMenu);
639                         
640                         /* Already selected */
641                         if (previous_selitem != null && item.rect == previous_selitem.rect) {
642                                 return;
643                         }
644
645                         UnSelectItem (hMenu, previous_selitem);
646                         
647                         // If the previous item had subitems, hide them
648                         if (previous_selitem != null && previous_selitem.item.IsPopup)
649                                 HideSubPopups (hMenu);
650
651                         if (tracker.hCurrentMenu != hMenu) {
652                                 menu.Wnd.Capture = true;
653                                 tracker.hCurrentMenu = hMenu;
654                         }
655
656                         menu.SelectedItem = item;
657                         item.fState |= MF.MF_HILITE;
658                         menu.Wnd.Invalidate (item.rect);
659
660                         item.item.PerformSelect ();                                     
661                         
662                         if (execute)
663                                 ExecFocusedItem (hMenu, item, tracker);
664                 }
665
666
667                 //      Used when the user executes the action of an item (press enter, shortcut)
668                 //      or a sub-popup menu has to be shown
669                 static public void ExecFocusedItem (IntPtr hMenu, MENUITEM item, TRACKER tracker)
670                 {
671                         if (item.item.Enabled == false)
672                                 return;                         
673                         
674                         if (item.item.IsPopup) {                                
675                                 ShowSubPopup (hMenu, item.hSubMenu, item, tracker);
676                         }
677                         else {
678                                 // Execute function
679                         }
680                 }
681
682                 // Create a popup window and show it or only show it if it is already created
683                 static public void ShowSubPopup (IntPtr hParent, IntPtr hMenu, MENUITEM item, TRACKER tracker)
684                 {
685                         MENU menu = GetMenuFromID (hMenu);
686                         Point pnt = new Point ();
687
688                         if (item.item.Enabled == false)
689                                 return;
690
691                         MENU menu_parent = GetMenuFromID (hParent);
692                         ((PopUpWindow)menu_parent.Wnd).LostFocus ();
693                         tracker.hCurrentMenu = hMenu;
694
695                         if (menu.Wnd == null)
696                                 menu.Wnd = new PopUpWindow (hMenu, tracker);
697                         
698                         pnt.X = item.rect.X + item.rect.Width;
699                         pnt.Y = item.rect.Y + 1;
700                         pnt = menu_parent.Wnd.PointToScreen (pnt);
701                         menu.Wnd.Location = pnt;
702
703                         MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
704
705                         if (select_item != null)
706                                 MenuAPI.SelectItem (hMenu, select_item, false, tracker);
707
708                         ((PopUpWindow)menu.Wnd).ShowWindow ();          
709                         Console.WriteLine ("ShowSubPopup end {0} {1}", ((PopUpWindow)menu.Wnd).Location,
710                                 ((PopUpWindow)menu.Wnd).Size);  
711                 }
712
713                 /* Hides all the submenus open in a menu */
714                 static public void HideSubPopups (IntPtr hMenu)
715                 {
716                         MENU menu = GetMenuFromID (hMenu);
717                         MENUITEM item;                                          
718                         
719                         for (int i = 0; i < menu.items.Count; i++) {
720                                 item = (MENUITEM) menu.items[i];
721                                 if (!item.item.IsPopup)
722                                         continue;
723
724                                 MENU sub_menu = GetMenuFromID (item.hSubMenu);
725
726                                 if (sub_menu.Wnd != null) {
727                                         HideSubPopups (item.hSubMenu);                                  
728                                         ((PopUpWindow)sub_menu.Wnd).Hide ();
729                                 }
730                         }
731                 }
732
733                 static public void DestroyMenu (IntPtr hMenu)
734                 {
735                         if (hMenu == IntPtr.Zero)
736                                 return;                         
737
738                         MENU menu = GetMenuFromID (hMenu);
739                         MENUITEM item;
740
741                         for (int i = 0; i < menu.items.Count; i++) {
742                                 item = (MENUITEM) menu.items[i];
743                                 if (item.item.IsPopup) {
744                                         MENU sub_menu = GetMenuFromID (item.hSubMenu);
745                                         if (sub_menu != null && sub_menu.Wnd != null) 
746                                                 HideSubPopups (item.hSubMenu);
747                                                 
748                                         DestroyMenu (item.hSubMenu);                                    
749                                 }
750                         }
751
752                         // Do not destroy the window of a Menubar
753                         if (menu.Wnd != null && menu.bMenubar == false) {
754                                 ((PopUpWindow)menu.Wnd).Dispose ();
755                                 menu.Wnd = null;                        
756                         }
757                         
758                         /* Unreference from the array list */
759                         menu_list[((int)hMenu)-1] = null;
760                 }
761                 
762                 // Find item by screen coordinates
763                 static public bool FindSubItemByCoord (IntPtr hMenu, Point pnt, ref IntPtr hMenuItem, ref MENUITEM itemfound)
764                 {               
765                         Point pnt_client;       
766                         Rectangle rect;
767                         MENU menu = GetMenuFromID (hMenu);
768                         MENUITEM item;
769                         
770                         for (int i = 0; i < menu.items.Count; i++) {
771                                 item = (MENUITEM) menu.items[i];
772                                 
773                                 if (item.item.IsPopup)
774                                         if (FindSubItemByCoord (item.hSubMenu, pnt, ref hMenuItem, ref itemfound) == true)
775                                                 return true;
776                                         
777                                 if (menu.Wnd == null) // Menu has not been created yet
778                                         continue;                                       
779                                                                 
780                                 rect = item.rect;
781                                 pnt_client = menu.Wnd.PointToScreen (new Point (item.rect.X, item.rect.Y));
782                                 rect.X = pnt_client.X;
783                                 rect.Y = pnt_client.Y;
784                                 
785                                 if (rect.Contains (pnt) == true) {
786                                         itemfound = item;
787                                         hMenuItem = hMenu;
788                                         return true;
789                                 }
790                         }                       
791                         
792                         return false;
793                 }
794
795                 static public void SetMenuBarWindow (IntPtr hMenu, Control wnd)
796                 {
797                         MENU menu = GetMenuFromID (hMenu);
798                         menu.Wnd = wnd;
799                         menu.bMenubar = true;
800                 }
801
802                 static private void MenuBarMove (IntPtr hMenu, MENUITEM item, TRACKER tracker)
803                 {
804                         MENU menu = GetMenuFromID (hMenu);
805                         Point pnt = new Point (item.rect.X, item.rect.Y + item.rect.Height);
806                         pnt = menu.Wnd.PointToScreen (pnt);
807
808                         MenuAPI.SelectItem (hMenu, item, false, tracker);
809                         HideSubPopups (tracker.hCurrentMenu);
810                         tracker.hCurrentMenu = hMenu;
811                         MenuAPI.TrackPopupMenu (hMenu, item.hSubMenu, pnt, false, null);
812                 }
813
814                 // Function that process all menubar mouse events
815                 static public void TrackBarMouseEvent (IntPtr hMenu, Control wnd, MouseEventArgs e, MenuMouseEvent eventype, TRACKER tracker)
816                 {
817                         MENU menu = GetMenuFromID (hMenu);
818
819                         switch (eventype) {
820                                 case MenuMouseEvent.Down: {
821
822                                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
823
824                                         if (item != null) {
825                                                 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
826                                                 
827                                                 top_menu.bTracking = true;
828                                                 MenuBarMove (hMenu, item, tracker);
829                 
830                                                 if (item != null) {
831                                                         item.item.PerformClick ();                      
832                                                 }
833                                         }
834
835                                         break;
836                                 }
837
838                                 case MenuMouseEvent.Move: { /* Coordinates in screen position*/
839
840                                         if (tracker.hTopMenu != IntPtr.Zero && tracker.hCurrentMenu != IntPtr.Zero) {
841                                                 
842                                                 MENU top_menu = GetMenuFromID (tracker.hTopMenu);
843                                                 
844                                                 if (top_menu.bTracking == false)
845                                                         break;
846
847                                                 Point pnt = new Point (e.X, e.Y);
848                                                 pnt = menu.Wnd.PointToClient (pnt);
849
850                                                 MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
851
852                                                 if (item != null && menu.SelectedItem != item)
853                                                         MenuBarMove (hMenu, item, tracker);
854                                         }
855                                         break;
856                                 }
857
858                                 default:
859                                         break;
860                         }
861                 }
862
863                 static public MENUITEM FindItemByKey (IntPtr hMenu, IntPtr key)
864                 {
865                         MENU menu = GetMenuFromID (hMenu);
866                         MENUITEM item;
867
868                         char key_char = (char ) (key.ToInt32() & 0xff);
869                         key_char = Char.ToUpper (key_char);
870
871                         for (int i = 0; i < menu.items.Count; i++) {
872                                 item = (MENUITEM) menu.items[i];
873
874                                 if (item.item.Mnemonic == '\0')
875                                         continue;
876
877                                 if (item.item.Mnemonic == key_char)
878                                         return item;
879                         }
880
881                         return null;
882                 }
883
884                 // Get the next or previous selectable item on a menu
885                 static public MENUITEM GetNextItem (IntPtr hMenu, ItemNavigation navigation)
886                 {
887                         MENU menu = GetMenuFromID (hMenu);
888                         int pos = 0;
889                         bool selectable_items = false;
890                         MENUITEM item;
891
892                         // Check if there is at least a selectable item
893                         for (int i = 0; i < menu.items.Count; i++) {
894                                 item = (MENUITEM)menu.items[i];
895                                 if (item.item.Separator == false && item.item.Visible == true) {
896                                         selectable_items = true;
897                                         break;
898                                 }
899                         }
900
901                         if (selectable_items == false)
902                                 return null;
903
904                         switch (navigation) {
905                         case ItemNavigation.First: {
906                                 pos = 0;
907
908                                 /* Next item that is not separator and it is visible*/
909                                 for (; pos < menu.items.Count; pos++) {
910                                         item = (MENUITEM)menu.items[pos];
911                                         if (item.item.Separator == false && item.item.Visible == true)
912                                                 break;
913                                 }
914
915                                 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
916                                         pos = 0;
917                                         /* Next item that is not separator and it is visible*/
918                                         for (; pos < menu.items.Count; pos++) {
919                                                 item = (MENUITEM)menu.items[pos];
920                                                 if (item.item.Separator == false && item.item.Visible == true)
921                                                         break;
922                                         }
923                                 }
924
925                                 break;
926                         }
927
928                         case ItemNavigation.Last: { // Not used
929                                 break;
930                         }
931
932                         case ItemNavigation.Next: {
933
934                                 if (menu.SelectedItem != null)
935                                         pos = menu.SelectedItem.pos;
936
937                                 /* Next item that is not separator and it is visible*/
938                                 for (pos++; pos < menu.items.Count; pos++) {
939                                         item = (MENUITEM)menu.items[pos];
940                                         if (item.item.Separator == false && item.item.Visible == true)
941                                                 break;
942                                 }
943
944                                 if (pos >= menu.items.Count) { /* Jump at the start of the menu */
945                                         pos = 0;
946                                         /* Next item that is not separator and it is visible*/
947                                         for (; pos < menu.items.Count; pos++) {
948                                                 item = (MENUITEM)menu.items[pos];
949                                                 if (item.item.Separator == false && item.item.Visible == true)
950                                                         break;
951                                         }
952                                 }
953                                 break;
954                         }
955
956                         case ItemNavigation.Previous: {
957
958                                 if (menu.SelectedItem != null)
959                                         pos = menu.SelectedItem.pos;
960
961                                 /* Previous item that is not separator and it is visible*/
962                                 for (pos--; pos >= 0; pos--) {
963                                         item = (MENUITEM)menu.items[pos];
964                                         if (item.item.Separator == false && item.item.Visible == true)
965                                                 break;
966                                 }
967
968                                 if (pos < 0 ) { /* Jump at the end of the menu*/
969                                         pos = menu.items.Count - 1;
970                                         /* Previous item that is not separator and it is visible*/
971                                         for (; pos >= 0; pos--) {
972                                                 item = (MENUITEM)menu.items[pos];
973                                                 if (item.item.Separator == false && item.item.Visible == true)
974                                                         break;
975                                         }
976                                 }
977
978                                 break;
979                         }
980
981                         default:
982                                 break;
983                         }
984
985                         return (MENUITEM)menu.items[pos];
986                 }
987
988                 static public bool ProcessKeys (IntPtr hMenu, ref Message msg, Keys keyData, TRACKER tracker)
989                 {
990                         MENU menu = GetMenuFromID (hMenu);
991                         MENUITEM item;
992
993                         switch (keyData) {
994                                 case Keys.Up: {
995                                         item = GetNextItem (hMenu, ItemNavigation.Previous);
996                                         if (item != null)
997                                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
998
999                                         break;
1000                                 }
1001
1002                                 case Keys.Down: {
1003                                         item = GetNextItem (hMenu, ItemNavigation.Next);
1004
1005                                         if (item != null)
1006                                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
1007                                         break;
1008                                 }
1009
1010                                 /* Menubar selects and opens next. Popups next or open*/
1011                                 case Keys.Right: {
1012
1013                                         // Try to Expand popup first
1014                                         if (menu.SelectedItem.item.IsPopup) {
1015                                                 ShowSubPopup (hMenu, menu.SelectedItem.hSubMenu, menu.SelectedItem, tracker);
1016                                         } else {
1017
1018                                                 MENU parent = null;
1019                                                 if (menu.hParent != IntPtr.Zero)
1020                                                         parent = GetMenuFromID (menu.hParent);
1021
1022                                                 if (parent != null && parent.bMenubar == true) {
1023                                                         MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Next);
1024                                                         MenuBarMove (menu.hParent, select_item, tracker);
1025                                                 }
1026                                         }
1027
1028                                         break;
1029                                 }
1030
1031                                 case Keys.Left: {
1032
1033                                         // Try to Collapse popup first
1034                                         if (menu.SelectedItem.item.IsPopup) {
1035
1036                                         } else {
1037
1038                                                 MENU parent = null;
1039                                                 if (menu.hParent != IntPtr.Zero)
1040                                                         parent = GetMenuFromID (menu.hParent);
1041
1042                                                 if (parent != null && parent.bMenubar == true) {
1043                                                         MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Previous);
1044                                                         MenuBarMove (menu.hParent, select_item, tracker);
1045                                                 }
1046                                         }
1047
1048                                         break;
1049                                 }
1050                                 
1051                                 case Keys.Return: {
1052                                         Console.WriteLine ("Return key: "+ menu.SelectedItem.item.Text);
1053                                         MenuAPI.ExecFocusedItem (hMenu, menu.SelectedItem, tracker);
1054                                         break;
1055                                 }
1056
1057                                 default:
1058                                         break;
1059                         }
1060
1061                         /* Try if it is a menu hot key */
1062                         item = MenuAPI.FindItemByKey (hMenu, msg.WParam);
1063
1064                         if (item != null) {
1065                                 MenuAPI.SelectItem (hMenu, item, false, tracker);
1066                                 return true;
1067                         }
1068
1069                         return false;
1070                 }
1071         }
1072
1073         /*
1074
1075                 class PopUpWindow
1076
1077         */
1078         internal class PopUpWindow : Control
1079         {
1080                 private IntPtr hMenu;
1081                 private MenuAPI.TRACKER tracker;
1082
1083                 public PopUpWindow (IntPtr hMenu, MenuAPI.TRACKER tracker): base ()
1084                 {
1085                         this.hMenu = hMenu;
1086                         this.tracker = tracker;
1087                         MouseDown += new MouseEventHandler (OnMouseDownPUW);
1088                         MouseMove += new MouseEventHandler (OnMouseMovePUW);
1089                         MouseUp += new MouseEventHandler (OnMouseUpPUW);
1090                         Paint += new PaintEventHandler (OnPaintPUW);
1091                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
1092                         SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
1093                         is_visible = false;
1094                 }
1095
1096                 protected override CreateParams CreateParams
1097                 {
1098                         get {
1099                                 CreateParams cp = base.CreateParams;
1100                                 cp.Caption = "Menu PopUp";
1101                                 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP));
1102                                 cp.ExStyle |= (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
1103                                 return cp;
1104                         }
1105                 }
1106
1107                 public void ShowWindow ()
1108                 {
1109                         Show ();
1110                         Capture = true;
1111                         Refresh ();
1112                 }
1113                 
1114                 public new void LostFocus ()
1115                 {                       
1116                         Capture = false;
1117                 }
1118
1119                 protected override void OnResize (EventArgs e)
1120                 {
1121                         base.OnResize (e);
1122                 }
1123
1124                 private void OnPaintPUW (Object o, PaintEventArgs pevent)
1125                 {
1126                         if (Width <= 0 || Height <=  0 || Visible == false)
1127                                 return;
1128
1129                         Draw (pevent.ClipRectangle);
1130                         pevent.Graphics.DrawImage (ImageBuffer, pevent.ClipRectangle, pevent.ClipRectangle, GraphicsUnit.Pixel);
1131                 }
1132                 
1133                 public void HideWindow ()
1134                 {
1135                         Capture = false;
1136                         Hide ();
1137                         MenuAPI.MENU top_menu = MenuAPI.GetMenuFromID (tracker.hTopMenu);
1138                         top_menu.bTracking = false;
1139                         
1140                         MenuAPI.HideSubPopups (tracker.hTopMenu);
1141                         
1142                         if (top_menu.bMenubar) {
1143                                 MenuAPI.MENUITEM item = MenuAPI.GetSelected (tracker.hTopMenu);
1144                         
1145                                 if (item != null) {
1146                                         MenuAPI.UnSelectItem (tracker.hTopMenu, item);
1147                                 }
1148                         } else { // Context Menu
1149                                 Console.WriteLine ("HideWindow context menu {0} ", top_menu.hParent);
1150                                 ((PopUpWindow)top_menu.Wnd).Hide ();
1151                         }
1152                 }
1153
1154                 private void OnMouseDownPUW (object sender, MouseEventArgs e)
1155                 {                       
1156                         /* Click outside the client area*/
1157                         if (ClientRectangle.Contains (e.X, e.Y) == false) {
1158                                 HideWindow ();
1159                         }
1160                 }
1161
1162                 private void OnMouseUpPUW (object sender, MouseEventArgs e)
1163                 {
1164                         /* Click in an item area*/
1165                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
1166
1167                         if (item != null) {
1168                                 item.item.PerformClick ();
1169                                 HideWindow ();                          
1170                         }
1171                 }
1172
1173                 private void OnMouseMovePUW (object sender, MouseEventArgs e)
1174                 {       
1175                         MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
1176                         
1177                         if (item != null) {                             
1178                                 MenuAPI.SelectItem (hMenu, item, true, tracker);
1179                         } else {                                        
1180
1181                                 MenuAPI.MENU menu_parent = null;
1182
1183                                 if (tracker.hTopMenu != IntPtr.Zero)
1184                                         menu_parent = MenuAPI.GetMenuFromID (tracker.hTopMenu);
1185                                         
1186                                 if (menu_parent == null) 
1187                                         return;
1188                                 
1189                                 if (menu_parent.bMenubar) {
1190                                         MenuAPI.TrackBarMouseEvent (tracker.hTopMenu,
1191                                                 this, new MouseEventArgs(e.Button, e.Clicks, MousePosition.X, MousePosition.Y, e.Delta),
1192                                                 MenuAPI.MenuMouseEvent.Move, tracker);
1193                                 }                               
1194                                         
1195                                 IntPtr hMenuItem = IntPtr.Zero;
1196                                 MenuAPI.MENUITEM item_found = null;
1197                                 
1198                                 if (MenuAPI.FindSubItemByCoord (tracker.hTopMenu, MousePosition, ref hMenuItem, ref item_found) == false)
1199                                         return;
1200                                 
1201                                 MenuAPI.SelectItem (hMenuItem, item_found, false, tracker);
1202                         }
1203                 }
1204
1205                 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
1206                 {       
1207                         return MenuAPI.ProcessKeys (hMenu, ref msg, keyData, tracker);
1208                 }
1209
1210                 protected override void CreateHandle ()
1211                 {
1212                         base.CreateHandle ();
1213                         RefreshItems ();                        
1214                 }               
1215                 
1216                 // Called when the number of items has changed
1217                 internal void RefreshItems ()
1218                 {
1219                         MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
1220                         MenuAPI.CalcPopupMenuSize (DeviceContext, hMenu);
1221
1222                         Width = menu.Width;
1223                         Height = menu.Height;                   
1224                 }
1225
1226                 private void Draw (Rectangle clip)
1227                 {
1228                         MenuAPI.DrawPopupMenu  (DeviceContext, hMenu, clip, ClientRectangle);
1229                 }
1230         }
1231
1232 }
1233